Mar 11, 2011 @ 01:42pm
I've just about finished my first site of 2011 and as ever learnt some things along the way.
Lesson one: Killing IE6 only makes things slightly easier
The Internet hates Internet Explorer 6. Not supporting it was a weight off my mind but look at your browser list without it and you'd be forgiven for not jumping for joy. I can't remember the last time I didn't have to make something with gradient backgrounds and fades nevermind, the now standard, rounded corners and drop shadows.
So, positives, I get to use descendant selectors and multiple classnames and feel like I'm programming and not throwing vague rules around. Negatives are I still have far too many background images, I've got Raphael to draw SVG and fall-back to VML, and jQuery so I don't have to write three times as much JavaScript to do things.
Update: I forgot to say that you should use the button tag where previously you'd use input type submit, do it, if only so you can put the span inside the button so it looks right when you focus on it!
Lesson two: Do not be afraid of SVG
SVG are in my opinion one of finest parts of the modern day web toolkit. You can keep your CSS transitions (lets face it, they're propriety and there is too much code for them... keyframes though, they're good) and your webGL (all very Flashy, in that it looks like it's not going to interact well with the other page elements).
The code to create the lines and curves of SVG takes a bit of a jump but if you can keep your mind in it's coordinate language (I wish the 0,0 point was bottom left not top left, I was going loopy trying to draw the graph axis) then you'll soon be comfortable. I just wish my maths was better. Although if it was maybe I'd prefer webGL
Lesson three: Use events to communicate between logical parts of your application architecture
I've been a fan of event driven programming for a while but I've struggled to come up with a good example for using it, but I think I finally have one!
The key part of this project was that we were visualizing data, and that that data might change dependent upon a user or server action. By separating the data from the controller or elements that use it we can simplify our connections to the data and compartmentalize it's operations. The events come into play when those data stores are updated, we broadcast that data has been updated using a traditional event, if a controller has registered to listen for that event then it will perform it's action from that point. It's an alternative to registering the handler directly to the data update function, leaving the responsibility on the listener itself rather than the class handling the data updates.
Lesson four: Learn for the next one
I may have exclaimed that the lightbox was the finest I'd ever built, most of the styling and positioning done through CSS etc. But of course it's not that simple, I'd used position fixed so when suddenly we got some long lightbox designs it's not going to expand the page beyond the viewport. Turns out the JavaScript controlled one I did last year worked better in the real world. Lesson learned
Sep 20, 2010 @ 12:30pm
Update, 10/11/11: Since I first wrote this post several excellent script loaders have been released, notably YepNope.js and my last post on document.write I’ve been trying to work out why I was having initialization problems with some lazy-loading functions I’d written. Document.write had been the main suspect, but there was something stranger going on.
JQuery’s getScript method
There are two key things about getScript to know, first is that the AJAX call to get the script is asynchronous, second is that the callback is fired on load*. These are both important points to consider when doing lazy loading. When called asynchronously there is no guarantee that the scripts will return in the same order they’re called. Also the callback is fired onload not when the script has been parsed and is available to use.
The situation
In my code I’d relied upon the callbacks to provide the initialization. All was going well until the third-party scripts were integrated and every so often the script wouldn’t initialize. Having looked in the ever useful Net tab in Firebug it happened when a certain pair of scripts were being initialized after DOM content loaded (dom ready) and before load, right when the lazy loading was running.
The problem, delay in parsing
The initialisation problem was caused by the disconnect between the onload call and the script completing parsing. The third party script is loading and doing a big eval on a string, tieing up the parser and exacerbating, but thankfully showing me, the problem.
Some kind of solution
The easiest solution is to have the script initialize itself, unfortunately I couldn’t do that as I needed three files, one a second library that the code was written in, second the data required by the function and third the actual code that makes it all work**.
What I decided to do was to raise a custom event reporting that the script had loaded and the name of the script. Then in my original script changed the event the initialization was called on to that event.
//trigger event
jQuery('body').trigger('scriptLoaded',sScriptName);
//event handlers
//(mainLoaded is another custom event, fired one the primary
//JavaScript interaction function have been called
jQuery('html')
.bind('mainLoaded', function(){
jQuery.getScript('lazymodule.js');
})
.bind('scriptLoaded', fScriptLoadedCallback);
The future! It’s only going to be more of a problem...
Lazy-loading is going to be much more mainstream, the defer and async attributes have made it into the webkit builds so the problems described above will become easier to cause! Event driven programming can work around this problem and maintain the structure of your files.
*That the onload relates to the file and not the parsing of the code is true, but only as far as i know. No guarantees that's consistent.
**Why three files? It’s going into a CMS and needs to be maintained, I could have lumped the data in with the code but that’s easier for people to break. I could have merged them all server side but the CMS guys have enough to do. And I could have rewritten the whole thing instead of using another library, but it’s 4500 lines of code that I’d be happy never to read again.
Sep 8, 2010 @ 12:00pm
This post has been updated since i first wrote it, originally I suggested using innerHTML to insert the content, the problem there is that scripts added in that way do not execute.
Document.write has been causing me pain this week, prepare for a small amount of constructive venting.
You might remember the Write method from those days you were learning how to write JS, whack it in and it writes whatever you give it to the screen. That’s fine but the problems occur when you think a bit more about how it does that and almost always you should be using something else.
If I write a script using Write and include it in the page body, any writes will be made in that same position. That makes sense as it’s part of the source parse and script evaluation, it’s where the browser is up to.
What it totally screws up is when I then want to make your damn 3rd party script deferable / lazy loadable! Why? Because once the page has been loaded any subsequent calls to document.write will replace the entire page.
Fix document.write
We can overwrite the document.write function but the difficulty is what with. My best guess at the moment is that;
- Before the page is loaded write calls are performed normally
- Once loaded, we replace Write with an new function
- When called we append the content to the body element.
Example with jQuery.
var fDocumentWrite = function(str,bOverrule){
if (bOverrule === true) {
//superwrite is a reference to the original document.write function
fSuperWrite.call(this,str);
}
else {
jQuery(document.body).append(str);
}
};
Why not something else?
- We cannot be sure of the content, if it’s text it could just be a text node that we append, but if it’s HTML it needs to be parsed and each created... Or shoved in a container anyway.
- We can’t use innerHTML, doing so on the body will destroy event hooks appied to that code, innerHTML replaces all that content so it’ll also be slow. Also scripts dont execute when inserted using innerHTML.
- Don’t defer the script? It’s safest, but with a decent amount of testing we can make this safe enough. Though there will always be a vender somewhere crazy enough to break anything
If you have a better fix for this then let me know, I've about four scripts I want to defer and it's only Wednesday.
In summary, don't use document.write, Doug Crockford says not to do and, even though he has a beard, you should trust him on this.