2

Check out this piece of JavaScript code:

(function (w, d) {
    var loader = function () {
        var s = d.createElement("script"), tag = d.getElementsByTagName("script")[0];
        s.src = "https://example.org/script.js";
        tag.parentNode.insertBefore(s,tag);
    };
    w.addEventListener ? w.addEventListener("load", loader, false) :
                         w.attachEvent("onload", loader);
}) (window, document);

Why did the author of this code use this method to include a script in the document? And what is the usefulness of the line:

w.addEventListener ? w.addEventListener("load", loader, false) :
                     w.attachEvent("onload", loader);

Last point: I'm a JavaScript beginner, what is the (window, document) at the end?

d2kagw
  • 797
  • 1
  • 7
  • 26
Fred Collins
  • 5,004
  • 14
  • 62
  • 111

10 Answers10

3

The first question, the code checks to see if window.addEventListener is defined. If it is, it uses it, otherwise it uses window.attachEvent. This is for browser compatibility.

The 2nd question, this code is an anonymous function which takes 2 parameters, w and d. This function is immediately called, passing the parameters window and document.

James Montagne
  • 77,516
  • 14
  • 110
  • 130
2

Firstly, w.addEventListener ?to make sure if the browser supported the addEventListener method of window

Secondly, (window, document) is just parameter call of anonymous function he wrote before function(w,d) {}

Thanh Nguyen
  • 161
  • 1
  • 10
2

The following addEventListener line is to register the function so that it gets called when the page finishes loading. Internet Explorer and Firefox (et al) use different functions to do this.

w.addEventListener ? w.addEventListener("load", loader, false) :
                 w.attachEvent("onload", loader);

In javascript a function is an object in and of itself. As such it's 'value' can be assigned to a variable or consumed immediately. To consume it immediately it must be wrapped in parentheses (otherwise it won't do what you want) and then call it like it were a regular function.

(function (w, d) { ... }) (window, document);

It's more obvious what is going on if we break it up across two lines.

var a = function(w, d){ ... };
a(window, document);

It was done this way to as to not pollute the global scope with temporary values or functions. Not to mention not trashing anyone else variables. This can be broken into two parts:

  1. By encapsulating the code in a closure anything explicitly declared inside is in the closure's scope, not the global scope. var loader is in the closure's scope.
  2. By consuming the closure immediately it won't be stored in a variable in the global scope. Since it was declared anonymously it won't exist as a named function in the global scope.
  • So the author uses the closure because probably he won't pollute the global scope, you mean not create any function that uses some name? – Fred Collins Apr 09 '12 at 01:20
  • Yes. If it weren't in a closure the 'var loader' would appear in the global scope where it could be called again, by accident or maliciously. By using the closure it only exists in the closures scope. – BlindWanderer Apr 09 '12 at 01:37
1

It looks like the author is waiting for the page to be fully loaded before attaching the script tag. addEventListener (DOM level 2) and attachEvent (Microsoft stuff) are more flexible ways of attaching events. The code is similar to saying w.onload = loader.

The last bit is passing arguments into the anonymous function, where they are named w and d. By putting the () at the end, the anonymous function is invoked right away.

tau
  • 6,499
  • 10
  • 37
  • 60
  • What is `DOM level 2`? Also, if the author wants wait the page to be fully loaded he can also use jQuery `$(document).ready()`, right? – Fred Collins Apr 09 '12 at 01:03
  • @FredCollins dom level 2 is just a specification. if the author was using jquery, that would produce a similar (but faster) result. – tau Apr 09 '12 at 04:57
1

So the function is wrapped in a closure. Which in turn means w = window and d = document.

When the method is called the it creates a function called loader which is the callback for one of the two possible event listener triggers (meaning, that'll be called when the load or onload event is called on the window).

The x ? y : z syntax is a shorthand if then else call.

If we expanded that out, it'd look like this:

if (w.addEventListener) {
  w.addEventListener("load", loader, false);
} else {
  w.attachEvent("onload", loader);
}

that statement is used cater the method for both IE and other browsers.

M.Babcock
  • 18,753
  • 6
  • 54
  • 84
d2kagw
  • 797
  • 1
  • 7
  • 26
1

This is effectively the same as:

function RegisterEventListener(w, d) {
    var loader = function () {
        var s = d.createElement("script"), tag = d.getElementsByTagName("script")[0];
        s.src = "https://example.org/script.js";
        tag.parentNode.insertBefore(s,tag);
    };
    if (w.addEventListener) {
       w.addEventListener("load", loader, false);
    } else {
       w.attachEvent("onload", loader);
    }
}

RegisterEventListener(window, document);

The only real differences are:

  1. If you define an anonymous function (using function () {};) without assigning it to anything it is only available for limited use (because there is no way to reference it). At the same time, anonymous functions also allow immediate execution (like the one in the code from your question function(a, b) {}(a, b);.
  2. The condition ? true : false (tertiary operator) is just shorthand for writing simple if statements so it requires less typing to write out, but some people also see it as being less readable.
M.Babcock
  • 18,753
  • 6
  • 54
  • 84
1

I may be wrong, but this is the implementation used by Google with their analytics script.

This is called a closure, a function that is autoexecuted and enclose the variables inside, so they can't mess with your code.

This codes essentially creates a script tag and append this to the DOM before the first script tag it finds in the document.

The answer for the first question is about browser compatibility. some browsers use addEventListener and others attachEvent to attach events to elements in the page (in this case, the window) and it will lounch on the load event of the window (when all content is loaded, after the document is ready). Take a look at this for a more detailed answer: window.onload vs $(document).ready()

The second answer is simple. This are the parameters used in the closure (the auto calling function) and can be read in this way:

function anonymous(w, d) {
    ...
}
anonymous(window, document);
Community
  • 1
  • 1
Ricardo Souza
  • 16,030
  • 6
  • 37
  • 69
  • Thanks. Can you explain "(when all HTML is loaded, but before the document is ready)"? It's not the equivalent for jQuery `$(document).ready()`? – Fred Collins Apr 09 '12 at 01:09
  • Sorry, I exchanged the meanings. The window.onload fires after the document.ready, when all content has finished loading. I will update the answer. – Ricardo Souza Apr 09 '12 at 01:14
1

The author of this code is using an anonymous function wrapped in a closure to fire off the function, which registers a load event. The script on the page will not actually be loaded until the window's onload event fires.

The reason that the author may be delaying the script load may be to give the web page more time to render before actually loading in the other script. This is a great technique to load page content quickly while delaying the loading of resources that are not immediately needed.

The technique the author is using is an anonymous function wrapped in a closure. Picture this:

myFunction (window, document);

Now, I'm going to replace the function call with an anonymous function:

function(w, d) {
    // do stuff
    alert("w = " + w); 
    alert("d = " + d);
}

Now I'm going to cause that function to run immediately, without actually giving it a name. I'm also going to pass two parameters into that anonymous function:

( function(w, d) {
      // do stuff
      alert("w = " + w);
      alert("d = " + d);
})  ("1234", "5678");

The output in this example would be:

w = 1234
d = 5678

If you watch the parentheses and match them up, the first outer parentheses matches the closing parentheses on the last line at character 2, that is the function name, and the following set of parentheses wrap the two values passed into the function as parameters.

This can be a tough concept to grasp at first; after you see it done a few times, it starts to make more sense.

jamesmortensen
  • 33,636
  • 11
  • 99
  • 120
  • But isn't there the issue that the page could be loaded and the user clicks on something that uses JavaScript code contained in the script that is loading? – Fred Collins Apr 09 '12 at 01:12
  • 1
    Absolutely! So as a developer, you have to make sure that the resource isn't needed immediately, as that could cause problems. My suggestion would be to only use that script loading techniques for things that don't involve user-initiated behavior. Someone used the example of analytics tracking code that silently runs in the background. That's a perfect candidate for this technique. – jamesmortensen Apr 09 '12 at 01:14
  • Thanks jmort253, so it's not recommended for load scripts that contains behavior for button, forms, etc. Only for silently "secondary" behaviors. – Fred Collins Apr 09 '12 at 01:26
  • 1
    Yes. However, if it defines behavior that is buried beneath other layers of UI, then you might be able to use the technique. For instance, if the user must click Menu, then Options, then Tools, and then your "Tools" menu is defined in your tools.js script, you might be fine. It depends on whether or not the user can be faster than the time it takes to load the script. – jamesmortensen Apr 09 '12 at 01:29
  • Okay, but in that case there's no reason to use this technique and not the standard one of including directly in the HTML. Apart a little bit of time for loading the script before the document. :-) Thanks! – Fred Collins Apr 09 '12 at 01:35
1
  1. Adding the script that way allows the author to include that script in the document without directly editing HTML files. Can also be used to dynamically load a script only when needed. (i.e. if you have a bunch of code to edit something on the page, you don't want to download it until the user actually clicks the edit button, so you don't waste bandwidth when it's not needed).

  2. addEventListener and attachEvent are ways to trigger a function to be called when the page has finished loading. In this case, there's a function named loader The reason for both is that some browsers support one and some support the other. I've been using jQuery long enough that I don't remember which is which.

  3. (window, document) is a way to encapsulate those variables in scope and/or refer to them by shorthand w and d. The author is creating a function that expects those parameters, then passing both window and document as the arguments for them.

The closure also helps the author keep from having his script clash with other scripts on the page. Think of every variable declared in there like it's a private variable in other languages.

Mike Ruhlin
  • 3,546
  • 2
  • 21
  • 31
1

The window, document at the end of the first block of code are arguments to the anonymous function defined in the rest of the code block. Since Javascript is pretty much a functional language programmers are allowed to define these anonymous functions and even use them without giving them a name.

The block of code you pasted with the question mark is an example of infix notation, a language construct that fits this pattern: condition ? ifTrueExpression : ifFalseExpression. Where condition is true, the entire expression will be equal to ifTrueExpression. Otherwise, the entire expression will be equal to ifFalseExpression.

The use of infix notation you pasted is common in detecting which type of internet browser is being used. Although, I'm not sure which browser this block of code is trying to detect, its intent is to implement an event handler in a browser specific way.

ctt
  • 1,405
  • 8
  • 18