jQuery injection for profit and profit, cause fun is overrated

Working on a project for an important client (more on that in the next months), we came up with the need to inject jQuery on a page, making it available to your functions.

In addition to that we wanted to be sure it didn’t conflict with other jQueries the page was already using, and, if a new version of jQuery was already available, use that instead of injecting a new one.

This whole injection thing is not very complicated per se. You just have to be sure you’re not messing up with what’s already in the page.

Let’s walk through our solution to see how we implemented it. All the code you see here will be CoffeeScript, the javascript translation is really straightforward and won’t be provided :)

We start with an empty html document, something like this:

<!DOCTYPE html>
<head>
  <meta charset="utf-8" />
  <script src="jquery-injection.js"></script>
</head>
<body>
</body>

jQuery-injection.js is the compiled version of our coffeescript source.

The simplest way to inject jQuery would be something like this:

@Injector = 
    injectjQuery: ->
        script = document.createElement('script')
        script.src = "//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"
        head = document.getElementsByTagName('head')[0]
        
        head.appendChild(script)

We’re creating a globally available Injector object with an injectjQuery function that creates a script element, sets its src attribute and adds it to head.

This is all fine and dandy, but what if we already have jQuery? This would load jQuery twice and there would be namespace conflicts. What we want to do is check for an existing jQuery and use jQuery.noconflict() to make sure they don’t clash.

checkAndInjectjQuery: ->
    if jQuery?
      if @versionCompare(jQuery.fn.jquery, "2.0.3") < 0
        @injectjQuery(true)
      else
        @jq = window.jQuery
    else
      @injectjQuery(false)   

What’s going on in here? We check if jQuery exists, and that its version is at least 2.0.3. The version check is done via versionCompare, a coffeescript simplified port of this code I found on StackOverflow.

If jQuery exists and is sufficiently new, we just map this.jq to window.jQuery. Our local jq variable is what we’re gonna use inside our functions instead of using $.

If you paid enough attention, you will have noticed we’re calling injectjQuery with a parameter.

  injectjQuery: (saveOriginal) ->
    script = document.createElement('script')
    script.src = "http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"
    head = document.getElementsByTagName('head')[0]

    if saveOriginal
      @jq = $.noConflict(true)

    head.appendChild(script)

injectjQuery(true) will assume that there’s another jQuery in the page, so it saves it (temporarily) in this.jq, and then cleans after it, making sure it won’t be there when the new jQuery loads.

Wait, did I say “when the new jquery loads”? You can bet I did! Everything we’re doing is asyncronous, so we can’t be sure that the new jQuery is already available.

Avoiding potential issues with that requires some callback magicks. First we edit injectjQuery, adding, immediately after the last line, the following code

@checkLoaded(saveOriginal)

checkLoaded is a function that keeps calling itself until jQuery is effectively loaded.

 checkLoaded: (saveOriginal) ->
    window.setTimeout( =>
      if !jQuery?
        @checkLoaded(saveOriginal)
      else
        if saveOriginal
          temp = @jq
          @jq = $.noConflict(true)
          window.jQuery = temp
          window.$ = jQuery
        else
          @jq = jQuery

        @continueInitialization()
    , 500)

It recognizes the same saveOriginal parameter we used before, restoring the original jQuery where it belongs, and leaving us with our new jQuery, available through this.jq.

continueInitializazion is the function that should handle all of your post-jquery-loaded needs.

In this gist you’ll find the complete Injector class, with some bonus console.log calls to better understand what’s going on.

Thank you for reading up to this last paragraph, and have fun injecting jQuery where you please.