1

I'm trying my best to learn unobtrusive JavaScript. The code below is an attempt to change an existing practice piece I had with the getArrays function called from within the HTML tag. This is the only version I could get to work. While it's nice that it works, I feel like there may be some unnecessary parts and don't fully understand why I need the last line of code in there (kind of new to the addEventListener stuff). I was working off of the Mozilla DOM reference example and know how to do this in jQuery already. Thanks!

JavaScript:

        <script>

        function getArrays() {
            var ddlValArray = new Array();
            var ddlTextArray = new Array();
            var ddl = document.getElementById("ddlFlavors");

            for (i=0; i<ddl.options.length; i++) {
                ddlValArray[i] = ddl.options[i].value;
                ddlTextArray[i] = ddl.options[i].text;
                alert(ddlValArray[i] + ' ' + ddlTextArray[i]);  
            }
        }

        function showArrays() {
            var link = document.getElementById("clickHandler");
            link.addEventListener("click", getArrays, false);   
        }

        document.addEventListener("DOMContentLoaded", showArrays, false);


    </script>

HTML

Select Flavor: 
<select id='ddlFlavors'>
<option value="1">Vanilla</option>
<option value="2">Chocolate</option>
<option value="3">Strawberry</option>
<option value="4">Neopolitan</option>
</select>

<br/><br/>
<a id="clickHandler" href="#">Show Flavors</a>

3 Answers3

5

You don't if you structure your HTML / JS correctly.

Move the script tag to the bottom of the body just before the </body> tag and you no longer need to wait for DOMContentLoaded event. The html above it will be parsed prior to the JS being executed.

This CAN cause other caveats but for your example it will work.

<body>
Select Flavor:
<select id='ddlFlavors'>
    <option value="1">Vanilla</option>
    <option value="2">Chocolate</option>
    <option value="3">Strawberry</option>
    <option value="4">Neopolitan</option>
</select>
<br/><br/>
<a id="clickHandler" href="#">Show Flavors</a>
<script>
        function getArrays() {
            var ddlValArray = new Array();
            var ddlTextArray = new Array();
            var ddl = document.getElementById("ddlFlavors");
            for (i=0; i<ddl.options.length; i++) {
                ddlValArray[i] = ddl.options[i].value;
                ddlTextArray[i] = ddl.options[i].text;
                alert(ddlValArray[i] + ' ' + ddlTextArray[i]);  
            }
        }
        function showArrays() {
            var link = document.getElementById("clickHandler");
            link.addEventListener("click", getArrays, false);   
        }
        showArrays();
    </script>
</body>

There are many other benefits to placing the scripts at the bottom as well. Read more about them here.

One thing to note: Even though the tags for iframes and images are parsed, the image itself might not be finished downloading, so you will have to wait for that. Also iframe content might not be loaded yet either. However DOMContentLoaded doesn't wait for these either. I just thought it would be nice to note.

rlemon
  • 17,518
  • 14
  • 92
  • 123
  • Though this answer offers an alternative to the approach the OP is using, it doesn't really answer the question _why do I need `addEventListener`?_ Sure, in this case, there is no _real_ need for event listeners, but they are _very_ powerful, and in today's world (ajax adding content dynamically) absolutely indispensable – Elias Van Ootegem Dec 20 '12 at 20:46
  • Meh, it does, you just need to read between the lines a bit more. I don't paint the entire picture of whats going on but I allude to it. Also the question was "why do I need two", one plausible answer is "you do not". – rlemon Dec 20 '12 at 20:48
2

This line of code: document.addEventListener("DOMContentLoaded", showArrays, false); ensures that your function showArrays() doesn't start executing before the DOM is ready. In other words, the function showArrays() will start executing after the (X)HTML document has been loaded and parsed.

I'd recommend that you put your JavaScript code in a separate file and use the following HTML code in the head section: <script type="text/javascript" src="yourfilepath.js"></script>. Then you could also use window.onload and onclick events instead of the document.addEventListener() method.

When you do that, you can put the code within the showArrays function outside its body and remove the function.

For example:

window.onload = function()
{
  var link = document.getElementById("clickHandler");
  link.onclick = getArrays;
}

That is considered a much better practice.

Full Example

In script.js file:

//This is the beginning of the script...

    function getArrays() {
        var ddlValArray = new Array();
        var ddlTextArray = new Array();
        var ddl = document.getElementById("ddlFlavors");

        for (i=0; i<ddl.options.length; i++) {
            ddlValArray[i] = ddl.options[i].value;
            ddlTextArray[i] = ddl.options[i].text;
            alert(ddlValArray[i] + ' ' + ddlTextArray[i]);  
        }
    }

window.onload = function()
{
  var link = document.getElementById("clickHandler");
  link.onclick = getArrays;
}

Inside your HTML head:

<script type="text/javascript" src="script.js"></script>

It's better to write JavaScript separate from the HTML file in most cases, just like CSS. That way, you make your HTML code look tidier and more organised.

Luka
  • 1,718
  • 3
  • 19
  • 29
  • `window.onload` does cause memory-leaks in IE, though, whereas `addEventListener` and `removeEventListener` [can solve this](http://stackoverflow.com/questions/11186750/memory-leak-risk-in-javascript-closures) relatively easy – Elias Van Ootegem Dec 20 '12 at 20:41
2

Event listeners are crucial to JS. Every time a user interacts with your page, you need to pick-up on that event, and respond to it.
Of course, in this snippet, your functions are assuming there is an element with a given id in the DOM, ready, set and waiting. This might not be the case, that's why you have to wait until JS receives the OK (the DOMReady event).

That's just one, very simple, example of why might use addEventListener. But IMO, event-listeners really come into their own when you start using Ajax calls to add new content to your page as you go along:
Suppose the DOM is ready, but some elements might, or might not be requested later on (depending on user input). In jQuery, you'd use things like this:

$('#foo').on('click',functionRef);

This doesn't require the foo element to be available when this code is run, but as soon as the element is added to the dom, the click events will be dealt with as you desire.
In non-jQ, you'll use addEventListener for that. Because of the event model (see quirksmode for details on propagation and bubbling), the listener needn't be attached to the element directly:

document.body.addEventListener('click',function(e)
{
    if ((e.target || e.srcElemet).id === 'foo')
    {
        //function that deals with clicks on foo element
    }
},false);//last param is for bubbling/propagating events

This way, you can bind all events for elements that might be added to the dom asynchronously, without having to check each response of each ajax call...

Let's take it one step further even: delegation. As you can see, in the snippet above, we're listening for all click events that occure somewhere inside the body. That's as near as makes no difference: all clicks, so you don't need to add 101 distinct click listeners for 101 elements, just the one. The only thing you need to do, is write the handler in such a way that it deals with each element accordingly:

document.body.addEventListener('click',function(e)
{
    e = e || window.event;
    var elem = e.target || e.srcElement;
    switch (true)
    {
        case elem.id === 'foo':
            //either code here or:
            return fooCallback.apply(elem,[e]);//use another function as though it were the handler
        case elem.id.className.match(/\bsomeClass\b/):
            return someClassCallback.apply(elem,[e]);//~= jQuery's $('.someClass').on('click',function);
         case elem.type === 'text':
             //and so on
    }
},false);

Pretty powerful stuff. There is, of course, a lot more too it, and there are downsides too (mainly X-browser stuff), but the main benefits are:

  1. Handling events for elements that are dynamically added/removed
  2. A single event listener that deals with all events for all elements makes your code more efficient, sometimes boosting performance by a lot. here's a nice example of when delegation is in order, sadly, it also shows the pains that X-browser development brings to the party...
Community
  • 1
  • 1
Elias Van Ootegem
  • 74,482
  • 9
  • 111
  • 149