views:

37

answers:

4

I have an AJAX callback that returns a list of results that are written out as links within a specific div. The code goes something like this:

$.each(data, function (idx, item) {
    var a = document.createElement("a");
    a.innerText = item.name;
    a.href = "#";
    // TODO set handler for click event
    d.appendChild(a);
    d.innerHTML += "<br/>";
});

This works fine, and the links appear. However, I want to perform some actions when the links are clicked. item has some other properties that I will need in the click handler, so I would like to close over them. Here's a definition of the handler:

    // defined in loop as a closure
    var clickHandler = function() {
        $('#siteId').value = item.siteId; // closed over this property
        $('#siteName').value = item.name; // and this one
        return false;
    };

I have tried a few methods of providing this handler for the click event. Firstly:

    a.onclick = clickHandler;

Secondly:

    a.addEventListener("click", clickHandler, true);  

I've also tried mouseup, mousedown events, but they don't work either.

The behaviour I see is that the browser navigates to the # anchor (it is appended to the URL).

What is going on here? Also, is there a jQuery solution that'd be more robust across browsers?

+1  A: 

is there a jQuery solution that'd be more robust across browsers?

You can obtain a jQuery object from your existing DOM element and then bind the click handler in the standard way:

$(a).click(clickHandler);

Also, I suspect your click handler itself is incorrect. If you're trying to set the value of an element, you should be using the val method, not assigning to the value property:

var clickHandler = function() {
    $('#siteId').val(item.siteId); // closed over this property
    $('#siteName').val(item.name); // and this one
    return false;
};
casablanca
I'd tried that, but it wasn't working. I'm not sure why. Perhaps something to do with the timing of creation/addition to DOM, or perhaps because it's very late here. Anyway creating the element with jQuery as Nick suggests worked nicely, and I expect your solution would work had I created it that way in the first place. You were right about the error in my handler though. Appreciated.
Drew Noakes
@bobince's answer explains why the above didn't work for me. Thanks again for your help.
Drew Noakes
+1  A: 

You could do something like this using jQuery:

$.each(data, function (idx, item) {
  $("<a />", { text: item.name, href: '#', click: clickHandler })
     .appendTo(d).after('<br />');
});

This uses the $(html, props) as the creation method for the element, setting it's text and href properties and assigning the click handler on creation. After this we're using .appendTo() to add it to the d element, then adding a <br /> following it using .after().

Nick Craver
Works great. Thanks for the intro to some good jQuery concepts/APIs too.
Drew Noakes
A: 

Use jQuery.live to attach an event handler to all events that match a selector now, or in the future. See the jQuery docs:

http://api.jquery.com/live/

This will attach the handler properly, even if the element is added to the DOM after the event handler is defined

RMorrisey
+2  A: 
d.innerHTML += "<br/>";

That's your problem. Never ever innerHTML+= anything. What it does is serialise the document objects in the parent element into HTML, add the string, and then parse the whole string back into new DOM objects. This is slow and loses all information that cannot be stored in serialised HTML markup, like field values, JS references and event handlers. So any event handler you added on the <a> gets destroyed immediate by the next line.

HTML is not the definitive storage of page data in a browser; you can't edit it directly. You can only serialise part of the document content into new HTML (which may look little like the HTML you put in to the parse the page), and parse HTML into completely new objects.

So instead, continue to use DOM methods:

d.appendChild(document.createElement('br'))

Aside:

a.innerText = item.name;

That's an IE extension that isn't supported everywhere. You can use it as fallback for IE where the standard textContent property isn't supported, or, for maximum compatibility just use plain old document.createTextNode() and append that to a.

Since you appear to be using jQuery, use text() to do this, and click() to bind the event, as in the other answers.

bobince
This makes great sense and digs me out of the pit of total confusion I was wallowing in, and up into the light. Much appreciated. I will avoid `innerHTML +=`, and in fact string HTML in general.
Drew Noakes