开发者

How to define a CoffeeScript class that loads jQuery on first instantiation, then just fires a callback thereafter?

I'm still pretty new to classical OOP using JavaScript; this seems like a general OOP question, but ideally I'm looking for an actual code example using CoffeeScript to help me learn.

I want to define a class that can be instantiated by any other function/class, but which executes its main functionality only the first time it's instantiated. The immediate use case will be a LoadjQuery class that can be called by an other class, which handles loading jQuery dynamically, but only once per document. The code that instantiates this class first can provide options and settings (jQuery version, minified/dev version, and a callback), but any other attempts to instantiate the class will just fire the callback immediately if jQuery has already been loaded, or wait for it to finish loading, then fire the callback.

Here's the code that I've written so far (valid but untested). I'm not sure if this is the most elegant way. For example, is there a way to avoid having to use the @jquery_loading flag, and/or the setInterval that waits for jQuery to finish loading? The former only really exists to prevent the code from being executed more than once, which is what I'd like to find a more natural approach for rather than relying on a flag and a conditional branch. The latter seems unnecessary and awkward, and I have in mind an approach sort of like Google Analytics's _gaq queue (where a queue of pending operations is built, and once the Google Analytics library has loaded, it works through the queue; subsequent additions to the queue are then processed immediately), but I don't know how to go about implementing that, and it may not need to be that sophisticated.

# load jQuery dynamically (see: http://code.google.com/apis/libraries/devguide.html#jquery)
class LoadjQuery

  @jquery_loading = false
  @jquery_loaded = false

  constructor: (callback, version = 1, compressed = true) ->
    if @jquery_loading # only load once per document
      if @jquery_loaded
        callback()
      else
        check_jquery = ->
          if @jquery_loaded
            clearInterval check_jquery
            callback()
        setInterval check_jquery, 100
    else
      @jquery_loading = true # set flag
      script = document.createElement 'script'
      script.src = "http://ajax.googleapis.com/ajax/libs/jquery/#{version}/jquery#{if compressed then '.min' else ''}.js"
      script.onload = -> # set up event handler to ensure that jQuery is loaded before the callback is executed
        if not @jquery_loaded
          @jquery_loaded = true # set flag
          callback()
      script.onreadystatechange = -> # alternative event handler needed for Opera and IE
        if not @jquery_loaded and (script.readyState is 'loaded' or script.readyState is 'complete')
          @jquery_loaded = true # set flag
          callback()
      (document.getElementsByTagName('head') or document.开发者_开发百科getElementsByTagName 'body')[0].appendChild script

Is it maybe a singleton that I'm looking for? If so, what's the best-practice implementation (I've seen several different approaches), and can you give a code example to start from?

Thanks!


You're overcomplicating things.

Since you're already using their CDN, why not use Google's loader?

<script type="text/javascript" src="https://www.google.com/jsapi?key=INSERT-YOUR-KEY"></script>
<script type="text/coffeescript">
google.load "jquery", "1.6.4"
</script>

Here's a simple loader implementation:

# load scripts dynamically
class ScriptLoader

    libraries =
        jQuery: "http://ajax.googleapis.com/ajax/libs/jquery/$version/jquery.js"

    constructor: (options..., callback) ->

        [lib, version, compressed] = options
        if @libraries[lib] then lib = @libraries[lib]

        loadCallback = =>
            return if @loaded
            @loaded = true
            callback()

        s = document.createElement 'script'
        s.onload = loadCallback
        s.onreadystatechange = ->
            loadCallback() if /loaded|complete/.test(s.readyState)

        s.src = lib.replace('$version', version)
        if compressed then lib = lib.replace('.js', '.min.js')

        (document.getElementsByTagName('head')?[0] or document.body).appendChild s

You'd use this as

new ScriptLoader 'jQuery', '1.6', -> alert window.jQuery

I'm following your initial structure, but bear in mind you shouldn't instantiate objects for their side effects; best to have some kind of factory method that uses it internally:

loadScript 'jQuery', '1.6', -> ...
# or
ScriptLoader.load 'jQuery', -> ...
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜