Non-onload-blocking Async JS with require.js

Posted on June 28 2012 in

Stoyan Stefanov at phpied.com explained how to do non-onload-blocking async JS. TL;DR: window.onload can now fire before your async script loads and we don't trigger the browser's loading indicators (except in Opera).

I loved the method; but thought we could take it a bit further. I also wanted to use it with require.js.

And here is what I came up with:

// non-onload blocking async script adder that won't show the loading spinner.
(function(d,s){
	// Places an iframe in the document's head.
	// IE7 doesn't have document.head, and document.body.previousSibling might not be the head in modern browsers
	// so we get it the old fashioned way
	with(d.getElementsByTagName("head")[0].appendChild(d.createElement('iframe')).contentWindow.document){
		open().c=function(){
			// `this` here is the iframe's document (kinda cool!)
			s=this.createElement('script');
			s.setAttribute('data-main','/app/main');
			s.src='/app/require.js';
			this.body.appendChild(s);
		};
		write('<body onload=document.c();>');
		close();
	}
}(document));

Yeah, yeah, I use with, I guess I was feeling a little evil. If you aren't as daring as I (heh), swap with out for a variable instead (it'll probably be about 0.01ms faster too!).

So, what's changed?


It's smaller.

This code isn't totally "standards compliant" - we're putting an iframe in the head. Doing this allows us to skip the part where we need to hide the iframe by setting its width, height, and border properties. #win

We don't have an ugly multi-line string and we don't have to worry about double-escaping any quotes in that string either. Stoyan's code had its JavaScript embedded in an html attribute; that could get confusing if you want to get fancy.
Instead, we add the function as a property of the iframe's document. Opera requires that we add property after the call to open(), which, lucky for us, returns the iframe's document object so we can make use of chaining.

<body onload> takes a millisecond or two longer to fire than a regular script tag. We fixed that. Note that we still need the body tag because of good-ol IE < 8 (if you don't need support for IE < 9 you can just use document.head everywhere). Ihárosi Wiktor pointed out that using just a script tag, as I had originally used, does block onload of the parent element. So we are back to using body onload.

Getting require.js to work.


Unfortunately, you can't just tell require.js what document to use without editing the file directly. The good news is that this is not that hard:

(function(document, navigator){

    /* RequireJS 2.0.2 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
     Available via the MIT or new BSD license.
     see: http://github.com/jrburke/requirejs for details
    */
    var requirejs,require,define;
    /* require.js goes here. Omitted for brevity. */

        function scripts() {
            return self.document.getElementsByTagName('script');
        }

    /* the rest require.js goes here. */

    this.requirejs = this.require = require;
    this.define = define;

}.call( parent.window, parent.document, parent.navigator ));

What we do here is just like Stoyan tells us to do, plus some.

First we wrap require.js in a modified IIFE - changing the way it's invoked so the context (this) of the function is the parent's window object. We then pass some of our parent window's global document and navigator objects as arguments so that require.js uses those instead of the iframe's.

Then we change one function within require.js in order for it to reference scripts within the iframe document, not our parents:

document.getElementsByTagName('script');
self.document.getElementsByTagName('script');

Now when we load require.js it should load all additional scripts within the parent document. If you are optimizing and concatenating with r.js you will need to do some additional work on the generated file. I'll leave that to you.

Tags: ,

How to move your Windows 7's User directory to your data drive

Posted on September 15 2011 in Windows

Use this at your own risk. If you try this and you break your system, I am not responsible. :-)

I recently built a new computer and opted for an 120GB SSD for Windows 7 Pro and programs with a separate 1TB HDD for data.

After installing the OS, all my programs, and setting up my machine to my liking, I realized that a huge chunk of data wasn't on my "data" drive. We all should know that SSD's do not have a great reputation when it comes to reliability. So, my C:/Users folder had to be moved.

I Googled a bit, found a couple articles on doing just that, followed the directions perfectly, then spent the next 3 hours trying to boot back into Windows. Fun.

I managed to get System Restore to work (it wasn't as easy as it should have been) and I was now back where I started (~3 hours later), but still not ready to give up. I decided to give it one more try and this is what worked:

READ THIS ALL THE WAY THROUGH BEFORE DOING ANYTHING!!! (I recommend reading through the comments as well, especially this one)

  1. Create a System Resort point (don't skip this step):
    1. Click the Start button, right click on Computer, click Properties.
    2. Select the System Protection tab, click Create.
    3. Type a description and click Create.
  2. Name your drives something obvious:
    1. Click the Start button, right click on Computer, click Manage.
    2. In the left panel, expand Storage, select Disk Management.
    3. (optional) If you want to rename the drive for your optical drive (CD, DVD, BD) you'll want to do that now:
      1. Place a disk in the drive now.
      2. Right click its entry in the right-side pane.
      3. Select Change Drive Letter and Paths.
      4. Click Change and select the new drive letter (I chose "O", for "Optical").
      5. Click Yes In the "Some programs that rely on drive letters might not run correctly" dialog.
    4. Right click on your SSD (probably C:), click Properties, and type in a description, e.g., "OS and Programs" and click OK.
    5. Right click on your HDD, click Properties, and type in a description, e.g., "Data", and click OK. If you want to change the drive letter do so now (I chose "D", for "Data").
  3. Boot into Windows 7's System Recovery Command Prompt:
    1. Insert your Windows 7 installation disk into your optical drive and restart your computer.
    2. When/if prompted press a key to boot from the disk (you may need a PS/2 keyboard for this).
    3. At the "Install Now" screen choose "Repair your computer". You might be asked if you want to "Repair and Restart", select No.
    4. Make sure Windows 7 is listed as the installed OS and that it is selected then select Next.
    5. In the list of recovery tools choose "Command Prompt".
  4. You should now be located at the virtual Windows drive (probably X:) in the command prompt.
    • NOTE: My actual Windows drives are: C = SSD (OS and Programs) and D = HDD (Data)
    • NOTE: My virtual drives in System Restore are: X = Virtual Windows, D = SSD, and E = HDD
  5. Your drive letters may differ from mine, adjust these instructions accordingly. To check what's what:
    1. type:  wmic logicaldisk get name press Enter
    2. for each letter do the following:
    3. type:   D: press Enter (D is any one of the drive letters from this section's first step.)
    4. type:   vol press Enter
    5. The first line will tell you what the drive is, e.g., "Volume in drive D is OS and Programs"
    6. Repeat steps 3-5 for each letter and take note of what drive is what
  6. Backup and copy your Window's C:\Users directory to your D:\ HDD:
    1. type: robocopy /copyall /mir /xj D:\Users E:\Users (D is my virtual SSD and E is my virtual HDD), press Enter
    2. Make sure no files failed to copy (FAILED and SKIPPED columns should = 0). If you have any failed or skipped files you should probably stop now. :-/
    3. type: D: press Enter (D is my virtual SSD)
    4. type: cd / press Enter
    5. type: rename Users UsersOld press Enter
  7. Create a "Super Shortcut" (aka Junction/symlink) for your actual C:/Users to point to D:/Users
    1. The is the confusing part. We'll be creating a link on our virtual SSD to point to our actual HDD. Because my actual HDD's drive letter is the same as my virtual SSD's drive letter the following command looks wrong, but it's not.
    2. type: mklink /J D:\Users D:\Users
      • IMPORTANT!!! 
      • The first D:\Users is referring to my virtual SSD's drive letter.
      • IMPORTANT!!! The second D:\Users is referring to my actual HDD's drive letter.
    3. Press Enter
    4. Verify the shortcut was created by typing: dir C:. You should find Users [D:\Users] in the list.
    1. type: exit press Enter
  8. Restart (you'll probably want to remove the Windows 7 installation DVD during POST)
  9. Keep the C:/UsersOld directory around for a while just in case. :-)

 

This is what worked for me and so far I haven't seen any issues. Let me know if you have any trouble.

The above is a culmination of the following similar articles (and their comments), all of which were flawed on their own:

 

If the above does break your system you may not be able to use System Restore directly. The following steps from this article may help you recover:

  1. *DON'T* use system restore yet, it doesn't know what you've done with your user profiles and therefore can't repair the permissions on them.
  2. Isolate the copy of your user profiles from the NTFS junction: move D:\Users D:\Users2
  3. Delete the NTFS junction: rmdir D:\Users
  4. Copy your user profiles back over to your boot drive: robocopy /mir /xj D:\Users2 E:\Users
  5. Now use system restore [via Install DVD or Safe Mode], either to the point you created in the instructions or an earlier point. This will repair the permissions on your user profiles.
  6. Reboot and you should be able to login…

Tags:

iPhone-Style Checkbox Using Only CSS (No Images!)

Posted on August 10 2011 in CSS

This is just a proof-of-concept and quite is fragile and hacky. For this reason, I won't be explaining any of the techniques or even providing the code snippets for you. You can always fork the fiddle to play with it.

Some of the awful things I've done:

  • I'm using psuedo elements (:before, :after) on the containing div to hide the inner-element border-radius overflow...this is bad. Feel free to fix it. :-p
  • The "slider" UI component should probably be a psuedo element so it can sit on-top of (z-index) the ON/OFF labels. Either the "ON" or "OFF" component would then be represented by the span element itself. You'll have to tweak the positioning quite a bit to get this to work.
  • The CSS is an absolute mess and has only been tested in Chrome.

 

This was inspired by Damian Nicholson's blog post on the same topic.

Tags:

How to Setup jQuery.ready Callbacks Before jQuery is Loaded

Posted on July 1 2011 in JavaScript

We all know that "best practice" states that we should place all of our external javascript files at the end of the document body. And generally, we'd like to put all of our own scripts into a /js/scripts.js file and place it right after jQuery (and our other scripts).

This is all fine and dandy, but sometimes I just want to slap some DOM-dependent jQuery code right into the middle of my HTML. What if I'm lazy and don't want to put my code in my scripts.js file? What if I want to do something like this?

<body>
    <div id="main">
        <script>
            $(function(){
                $("#main").prepend( "<p>Heyo!</p>" );
            });
        </script>
    </div>
    <div>...more HTML...</div>
    <script src="/js/jquery.js"></script>
    <script src="/js/scripts.js"></script>
</body>

Obviously, since jQuery is loaded at the BOTTOM of the page it is not going to work; following the best practice as describe above isn't going to play nice when combined with our inline/in-body scripts without some extra effort.

Here is that extra effort:

<head>
    <script>
        (function(a){
            _q=function(){return a;};
            $=function(f){
                typeof f==="function" && a.push(arguments);
                return $;
            };
            jQuery=$.ready=$;
        }([]));
    </script>
</head>
<body>
    <div id="main">
        <script>
            $(function() {
                $( "#main" ).prepend( "<p>Heyo!</p>" );
            });
        </script>
        <div>...more HTML...</div>
    </div>
    <script src="/js/jquery.js"></script>
    <script>
        (function( i, s, q, l ) {
            for( q = window._q(), l = q.length; i < l; ) {
                $.apply( this, s.call( q[ i++ ] ) );
            }
            window._q = undefined;
        }( 0, Array.prototype.slice ));
    </script>
    <script src="/js/scripts.js"></script>
</body>

What the first <script> does is emulate jQuery's ready function by storing the arguments of any calls to $.ready where the first argument is a function into an array. This array is private to our globally scoped _q method, which, when called, returns the array.

The last inline  <script> loops through the array by calling _q() and then applies the arguments originally passed to our imposter $.ready to the real $.ready.

You can use these typical $.ready styles:

 

$(elementOrSelector).ready(function(){});
$(function(){});
$.ready(function(){});
$(function(){});

 

Warnings: $(document).bind("ready", function(){}) will not work. Get over it.

Here is the minified version:

         // first part (in document head)
         (function(a){_q=function(){return a;};$=function(f){typeof f==="function"&&a.push(arguments);return $;};jQuery=$.ready=$;}([]));

         // Second part (after jQuery)
         (function(i,s,q,l){for(q=window._q(),l=q.length;i<l;){$.apply(this,s.call(q[i++]));}window._q=undefined;}(0,Array.prototype.slice));
        

Tags:

IE9 - IE10pp4 CSS Hack

Posted on May 13 2011 in CSS

My GoogleFu turned up nothing today when searching for IE9 only CSS hacks. After a couple hours of searching, and combining the hacks found in Paul Irish's excellent post about Browser CSS Hacks I gave up. I just moved on. I settled for that 1px difference between every other browser and IE9.

Apparently my subconscience was still working on the problem for me. Suddenly had an idea, well, a question, actually:

"Whats new in IE9?"

I'll tell you whats new, media query expressions. Specifically, the and (min-width:value) part.

Update (18 May 2011): Mathias Bynens (twitter) posted a comment explaining that the :root selector is new in IE9! The @media all and (min-width:0) part as been removed in favor of this method. Thanks Mathias!

 

Update (13 June 2011):  This hack is a bit mysterious as doesn't work on all properties (like background, for instance) and isn't considered "safe" (yet). Checkout Mathias' post on safe css hacks.

Ta-freaking-Da:

#element {
    color:orange;
}
#element {
    *color: white;    /* IE6 + 7, doesn't work in IE8/9 as IE7 */
}
#element {
    _color: red;     /* IE6 */
}
#element {
    color: green\0/IE8+9; /* IE8 + 9 + IE10pp4  */
}
:root #element { color:pink \0/IE9; }  /* IE9 + IE10pp4 */

Demo:

 

Note that this hack also works in IE10pp4.

p.s. CSS hacks are usually bad. You should probably do this instead.

Tags:

Installing Page Speed in Firefox 4.0.1

Posted on May 6 2011 in Tips

You may be getting an error message when trying to instal Page Speed in Firefox 4.0.1.

 

"Page Speed could not be installed because it is not compatible with Firefox 4.0.1."

 

They lie. It is compatible. To force it to install on your system you just need to follow these instructions:

 

  1. Install Firefox 4.0.1

  2. Right Click, Save As https://dl-ssl.google.com/page-speed/current/page-speed.xpi

  3. Use 7-Zip (its free) to Open the Archive.
  4. Right click on install.rdf and select Edit
  5. Change <em:maxVersion>2.0.0.*</em:maxVersion> to <em:maxVersion>5.0.0.*</em:maxVersion> (any higher number should do)
  6. Save then close the file
  7. Confirm that you DO want to update the file in the archive.
  8. Open your modified page-speed.xpi with Firefox and Install.

Tags:

jQuery UI Modal Dialog with a Blurred Overlay

Posted on April 18 2011 in CSS

This is just a preview of what is to come. The following is a REAL screenshot of FF4 demonstrating the technique from my last post about appling gaussian blur on HTML elements with CSS.

jQuery Modal Dialog

I'll update this post as soon as I have something more complete to demonstrate.

Tags:

Applying Gaussian Blur to HTML elements with CSS

Posted on April 15 2011 in

UPDATE (05 January 2012): Christian "Schepp" Schaefer has an excellent article on implementing cross-browser blur with CSS, filters, and SVG titled Effects for the Web. Check it out!

 

UPDATE (21 April 2011): The the w3's editor's draft for filter effects is getting lots of attention lately - the main focus seems to be on CSS blur right now. The newly proposed CSS blur should behave very similarly to the moz-element hack outlined in this post but will be more performant, easier to implement, and super awesome. It will go something like this: #elementId { filter:blur(3px); }. Sweet.

 

Update (18 April 2011): It is apparent that I should note that this method should probably be considered a "hack" - and that I would LOVE to see a better (and more cross-browser) implementation. Please feel free to fork the jsfiddle below and 'best' my solution. :-)

 Also, I think I need to clarify as to what this blur effect is, and what it is not:

The effect is intended to work in HTML; no XML content-type, doctype, markup, or extra namespaces are needed.

The effect, as detailed below, will work with HTML5Boilerplate without any modifications whatsoever. You could write a jQuery plugin to automate the process and you wouldn't have to worry about strict XML validation and the other goodies that come with XHTML.

The effect is NOT a text-blur or shadow-like effect. It is much more than that; it can "blur" your images, video, text, backgrounds, gradients, borders, iframes, animations, etc.

 

Hey there. So, I recently purchased my first Android-powered phone computer (well, technically it was free with the 2 year contract) and while powering it off I noticed the background of the power-off confirmation screen has a gentle gaussian blur. This blurred-background UI is common in desktop (and smart-phone) applications, but not on the web.

Unfortunatly, we don't have a "blur" CSS property (yet) that we can apply to arbitrary elements, and canvas just doesn't cut it. So, if we want to implement a blurred background effect we have to provide an image of a blurred background. "But wait!", you say, "what about SVG?". I love html5boilerplate, I love short doctypes, I love text/html - SVG filters just aren't implemented completely in the HTML5 environment.

But what we do have, at least in Firefox 4, is background-image:-moz-element(#elementID).

New in Firefox 4, the -moz-element extension to the background-image property of elements allows us to display an element, and its descendants, including cross-domain iframes, as a background image. I'm not going to go over the restrictions and details of its implementation; you can read about it on the mozilla hack blog.

What I'll be showing you isn't really gaussian blur, get over it (it is probably some form of box blur).

Check out the Result tab in the jsfiddle for the demo, view the CSS for the good-stuff.

Note: THIS WILL ONLY WORK IN FF4!

So, what is happening here?

First, we reference our #content element via background-image's -moz-element extension, like this:

 

background-image:-moz-element(#content);

 

We then take advanatage of CSS3's multiple background-image support and add the additional -moz-elements required to render our blur.

To calculate how many -moz-elements you need:

 

numberOfElements = ( (radius * 2) + 1 )2 ) - 4

 

where radius > 0.

So, a radius of 1px gives needs 5 elements, 2 = 21, and 3 = 45.

We then need to position our background-images. The coordinates for a 1px radius are:

(0,0)
(0,1)
(0,-1)
(1,0)
(-1,0)

and 2px adds on an additional 16 coordinates:

(1,1)
(1,-1)
(-1,1)
(-1,-1)

(0,2)
(0,-2)
(2,0)
(2,1)

(2,-1)
(-2,0)
(-2,1)
(-2,-1)

(1,2)
(1,-1)
(-1,2)
(-1,-2)

...and so on.

Once we've got our background-images in place we need to adjust the opacity of the original element (#content):

 

opacity = 1 / numberOfElements;

 

 

So, for a 2px radius the opacity should be .048 (1px = , 3px = .22)

It should be pretty easy to write a plugin or script to generate the neccessary CSS for you; but I leave that up to the reader (aka I'm too lazy to do it right now).

Disclaimer: This can really hurt performance with a large blur radius. You may be able to omit some -moz-elements if performance becomes an issue.

Tags:

Testing Layout Post

Posted on December 17 2010 in

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras semper, diam vitae euismod pretium, est metus tempus eros, id porttitor turpis velit in sapien. Suspendisse potenti. In accumsan risus a libero facilisis tempus a sit amet dolor. Mauris vitae varius sapien. Suspendisse a elit id magna tincidunt volutpat at in justo. Nulla quis vulputate massa. In hac habitasse platea dictumst. Aenean vel imperdiet ante. Ut libero nunc, dictum mattis pellentesque quis, scelerisque in sem. Mauris luctus mi nec purus faucibus tempor. Curabitur pulvinar commodo urna nec porttitor. Pellentesque velit mauris, mattis eget convallis eget, ornare ac diam.

Praesent fringilla bibendum eros, eget lobortis metus mattis quis. Quisque sed orci sit amet nisl interdum aliquet. Nullam et erat a nisl ullamcorper egestas sit amet vitae eros. Curabitur eleifend turpis vel massa rhoncus vel mollis quam laoreet. Nulla auctor semper interdum. Proin turpis justo, pharetra eget laoreet non, ornare id mauris. Fusce consectetur arcu at mauris tempus ultrices. Curabitur consectetur quam non dolor consequat molestie. Nulla placerat tincidunt turpis, id lacinia purus consectetur nec. Etiam felis urna, consectetur id pellentesque at, lacinia ut lacus. Donec tellus leo, scelerisque in dictum vitae, aliquam a nisi. Maecenas id turpis ante. Suspendisse at purus at nunc porttitor malesuada nec non sem. Nulla eget augue augue, id iaculis sapien. Sed placerat risus porttitor massa sollicitudin ut lacinia massa vulputate.

Etiam tempor rhoncus laoreet. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nulla facilisi. Aliquam lobortis enim sed sem tincidunt pharetra. Maecenas mollis, enim non consectetur viverra, neque turpis congue ligula, eu volutpat nisl turpis aliquet nisi. Praesent tincidunt, odio id porta fringilla, nisi urna viverra est, sed vehicula metus nulla sed turpis. Etiam ac odio ut felis volutpat bibendum. Nullam nec elit diam, mollis aliquam augue. Proin blandit faucibus diam ac ultrices. Morbi accumsan ligula vitae nulla bibendum luctus. Morbi sed diam magna, venenatis hendrerit mi. Etiam eget augue felis, et imperdiet quam. Duis accumsan ullamcorper magna non tincidunt. Fusce nec varius velit. Sed pharetra pulvinar aliquam. Maecenas elementum felis non odio suscipit dignissim.

In elit nibh, ornare ut posuere vitae, molestie et ligula. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nam laoreet diam eu urna scelerisque ornare. Morbi mauris massa, bibendum nec feugiat eu, sagittis vel ligula. Aliquam id ante id leo porta dignissim ac et tellus. Cras porttitor blandit volutpat. Fusce at enim in augue tempor viverra vel id dolor. Integer lobortis iaculis sollicitudin. Aenean sit amet eros turpis, sit amet mollis sem. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut convallis lectus tristique turpis dapibus non fermentum neque tempor.

Sed vitae dui nulla, et ullamcorper turpis. Vivamus pulvinar mi vel neque accumsan ullamcorper. Fusce commodo urna vel mauris congue nec vestibulum nulla sodales. Aliquam sodales, tortor nec tincidunt fringilla, massa neque porttitor enim, id egestas nulla justo ac odio. Vestibulum a urna arcu. Curabitur sit amet felis ullamcorper quam egestas ornare vitae ac mi. Maecenas porttitor arcu sed sapien blandit vel ultricies nibh tempus. Vestibulum eu tortor at ipsum egestas adipiscing. Etiam porta volutpat dolor, vitae venenatis diam sollicitudin quis. Mauris venenatis, tellus non egestas volutpat, odio nunc vulputate dui, blandit iaculis libero risus a orci. Integer suscipit tortor sit amet tortor molestie sodales. Donec congue eros sit amet augue ultricies sed venenatis eros blandit. Nullam sagittis sodales libero, eleifend ultricies neque vestibulum ac. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam tincidunt volutpat porta. Maecenas at sollicitudin nisi.

Tags:

First Post

Posted on December 17 2010 in

This is my obligatory "Hello World" post.

Tags: