Dynamically adding scripts

Hello,
I’m dynamically loading some scripts, and they are not running : ) I think my lack of knowledge of how browsers load/run stuff is my problem. If anyone can enlighten me, link to something, would be great.

I thought I’d give Nicholas Zakas’ isMedia() a try, which allows you to run scripts based on whether the browser is applying a media query or not. I have a test page running a script called “jquerylightbox.js” which is a single file containing a minified jquery library plus the Lightbox 2 script (why make the server do two calls? And we decided not to make one call to some CDN and another call to the lightbox, since anyone not cached are doing just that… and it’s already now an older jquery version so people are more likely to have newer versions cached anyway).

The original setup (calling this jquerylightbox.js in the head) works in the first browsers I’m testing (Firefox 3 and newest Chrome for Linux, whichever version that is).

The Lightbox part dies horribly on mobile, which is fine: I’d rather mobiles get default behaviour (user clicks on image and goes to just that image, need to hit back button or swipe “back” to get back to the main page). In fact, I’d rather mobiles or anything with a small screen doesn’t even bother making the request for the Javascript at all. Why? Waste of bandwidth, which for some people is hideously expensive. So, save them time and money.

So I hacked together Zakas’ isMedia in a window.onload and when Viewing Generated Source and checking what I load, I’m getting the markup I need and the scripts are getting loaded… but they are not getting run.

(code subject to change as I’m currently playing with it)
link to page


/* Copyright 2011 Nicholas C. Zakas. All rights reserved.
  * Licensed under BSD License.
  * https://gist.github.com/08602e7d2ee448be834c */
 
 var Brandweer = {
     init: function() {
 
         var isMedia = (function() {
             var div;
             return function(mquery) {
                 if (!div) {
                     div = document.createElement('div');
                     div.id = 'poez';
                     div.style.cssText = 'position:absolute;top:-1000px';
                     document.body.insertBefore(div, document.body.firstChild);
                 }
 
                 div.innerHTML = '_<style media="' + mquery + '"> #poez {width: 1px;}</style>';
                 div.removeChild(div.firstChild);
                 return div.offsetWidth == 1;
             };
         })();

         if (isMedia('screen and (min-width:751px)')) {
            var script1 =  document.createElement('script');
             script1.src = 'jquery.js';
             var script2 = document.createElement('script');
             script2.src = 'lightbox.js';
             var head = document.getElementsByTagName('head')[0];
             head.appendChild(script1);
             head.appendChild(script2);
         }
     }
 };
 window.onload = Brandweer.init;

This particular version I’m loading the jquery and lightbox stuff separately for testing (I tried a version where the jquery library is handwritten in the source, and the isMedia script only calls the lightbox… this worked in Chrome but not in Firefox, no idea why).

I’m assuming my problem is, the jQuery is waiting for the page to load (when it already has)? As for why it works in Chrome when I leave everyone to load the jQuery and let the lightbox get added dynamically… maybe that has to do with FF being different in how it reports the load-event setting??

The lightbox stuff is checked by scrolling to the bottom of the page and clicking one of the smaller thumb images.

That is unlikely to be a good solution considering JQuery has its own load event that will never fire. JQuery itself uses onload so by including jQuery after the onload event has occurred will prevent any handlers from getting executed that have been added to the jQuery onload stack.

I think though that the problem your seeing is because jQuery is not defined by the time the light box script is included. To test this open up the light box file and add to the top: alert(typeof(jQuery));

In regards to aggregation it is likely that the code in jquery and light box can be combined into a single file without hurting anything. However, aggregation of CSS would require updating any image references, based on the aggregated file location.

Thanks for the reply!

In regards to aggregation it is likely that the code in jquery and light box can be combined into a single file without hurting anything. However, aggregation of CSS would require updating any image references, based on the aggregated file location.

Oh, they run well together… it’s what the current site has been using since I last convinced someone to update my code… months ago. Just one (hard-coded) call to “jquerylightbox.js”.

If I write code for a site, I don’t have hundreds of separate stylesheets for different scripts… waste of server calls in my opinion. Separation makes sense if you’re loading a whole silly library for more than one script.

That is unlikely to be a good solution considering JQuery has its own load event that will never fire. JQuery itself uses onload so by including jQuery after the onload event has occurred will prevent any handlers from getting executed that have been added to the jQuery onload stack.

This is what I wondered. Only reason jQuery is involved at all is, I’m not good enough to write my own lightbox in vanilla (and they want all the fancy stuff, so a CSS version is out).

Now when I had both isMedia and the jQuery library itself in the head, and isMedia only dynamically called the lighbox stuff, Chrome worked as expected, but Firefox (even tested in 5 just in case my 3 had some old bug) still didn’t play ball. And I’m not sure why. I can see it gets loaded (I can see the request coming in and succeeding) but still doesn’t seem to run. Maybe I’ll try adding it to the end of the body.

What I was hoping to achieve was only loading jQuery at all if the media query being loaded made sense with a lightbox. If I can only ever get this working with the library hard-coded, then I might as well try linking to a CDN online somewhere (but again, less likely than before that it’s been cached. I use whichever version worked for the lightbox).

*edit so I’ve just modified the page to do just that… works in Chrome, but not FF or Opera. Likely because I used window.onload… jQuery’s prolly taking that, so I’ll see if I can use jQuery to load isMedia…

Funny how posting in a thread starts a dead end getting new ideas…

Link posted in original post now set with two separate scripts. So, won’t run lightbox on smaller-than-750px screens, will on larger, but unfortunately everyone loads jQuery. Bah

While you may think its easy to do, for large sites with several assets both internal and third party its a maintenance nightmare unless there is a system to automate the process such as; assetic.

An alternative method is to detect mobile devices server site. Granted headers are not a very reliable means of detection for it won’t hurt this optimization feature enhancement if device detection is not spot on.

You may also be able to add a htaccess rule to detect mobile devices by inspecting headers and appending a value onto the query string. Then server-side choose to add the assets based on that value existing in the query string.

My page at Media Specific JavaScript shows how to combine Nicholas’ isMedia function and my [url=http://javascript.about.com/library/bladdjs.htm]Adding Javascript from Javascript function so that scripts (eg. jQuery) only get added to the web page if the media query is satisfied.

Thanks Fegall. Though what you have, it’s pretty similar to what I had. jQuery script tags got added just fine, but they wouldn’t run. It seemed they were waiting for a page load event… which never came, because it was already there.

I know browsers set some flag or something when they’ve loaded the page, since I remember reading it on someone’s (Zakas’?) page, and that browsers weren’t consistent in what they called it and when they set it. If jQuery checked for that, it would probably work.

Since Zaka’s technique builds a div and checks if the media-query ran, it doesn’t seem to work if I try to run it without first waiting for page load. So initially I had Zakas’ script bundled inside a window.onload, otherwise I could not add the other scripts to the head… there was no head yet.

But if the site isn’t large and doesn’t have those maintenance problems, then it’s a waste. I’m not calling many scripts using the jQ library. I’m calling just one. So here it makes sense to squish them together in one file.

Similarly, sites I see where there are 15 scripts and each have their own CSS, I find the extra calls to the CSS silly once the site it deployed (keep them separate in the development version by all means, but when users are accessing it, let all the CSS get cached as one file called just once).

When 3rd-party solutions are involved, it always reeks of nightmare to me.

An alternative method is to detect mobile devices server site.

Yeah that very briefly crossed my mind, but as a front ender I don’t have that kind of access to the server here. However if “responsive images” were very necessary then I might have pushed for it, since a desktop version might get to the point where it’s just too big to warrent sending to any mobiles, no matter their speed connections and JS and battery-life capabilities.

And you’re right, UA’s lie. I like that the isMedia solution is more of a object-detection setup rather than a UA identifyer setup. Esp since I built as mobile-first, only those who
-support media queries
-believe they have a screen wider than 750 device-width pixels
-run Javascript
go call the scripts. A very nice idea.

Hey stomme, some good scripts to check out for inspiration:

Lab.js … A js loader, has the option to wait for dependencies to load (e.g. Inject Jquery, then wait for it to execute before injecting plugins)

Yepnope.js … Think this is now part of Modernizr, similar to what you’re doing now, adds a function that you pass a boolean (e.g. The result of a test for media queries), and then pass in scripts to load if the boolean is true and if it is false. Allowing you to load in a polyfill or alternative script.

Let me know if any Qs, glad to help more :slight_smile:

P.s I would have included some links but using forums from mobile at the mom. :stuck_out_tongue:

The attraction of isMedia was that it was one tiny function : )

But seeing’s how one tiny function can’t deal with how any library one would like to load works… I’ll check out lab.js