13

I have a blank <a> tag that content is loaded into via an external piece of javascript. I want to observe the <a> and when its content changes perform another task. The content will only ever change once.

Can this be done?

I am using also using jQuery.

Thanks in advance

skaffman
  • 398,947
  • 96
  • 818
  • 769
Lizard
  • 43,732
  • 39
  • 106
  • 167

4 Answers4

6

You can use a mixture out of jQuery && DOM Level 3 events (see browser support below).

If you want to check for any changes within the content, you could do this:

var $a = $('a');

$a.one('DOMNodeInserted', function(e) {
    console.log('content changed!: ', e);    

    console.log('new content: ', $(this).html());   
});

$a.one('DOMAttrModified', function(e) {
    console.log('attribute changed!: ');        

    console.log('attribute that was changed: ', e.attrName);
});

See this code in action: http://jsfiddle.net/wJbMj/1/

Reference: DOMNodeInserted, DOMAttrModified


While the above solution is actually pretty convinient to me, it'll only work in browser that support those events. To have a more generic solution, you can hook into jQuerys setter methods. The downside in this solution is, that you will only catch changes that were done through jQuery.

var _oldAttr = $.fn.attr;
$.fn.attr = function() {
    console.log('changed attr: ', arguments[0]);
    console.log('new value: ', arguments[1]);
    return _oldAttr.apply(this, arguments);
};

You could hook into .text() and .html() the exact same way. You would need to check if the this value within the overwritten methods represent the correct DOMnode.

jAndy
  • 231,737
  • 57
  • 305
  • 359
  • @jAndy In Chrome's console only the DOMNodeInserted event is logged, and in Firebug only the DOMAttrModified event is logged. Do you have the same results? – Šime Vidas Mar 01 '11 at 14:00
  • 1
    @Šime Vidas: FireFox works for both event types, Chrome seems only to work with `DOMNodeInserted`. Unfortunately, these event types are deprecated without any replacement (afaik). – jAndy Mar 01 '11 at 14:56
  • 1
    @jAndy The DOM 3 Events spec says: ["A new specification is under development with the aim of addressing the use cases that mutation events solves, but in more performant manner."](http://www.w3.org/TR/DOM-Level-3-Events/#events-mutationevents). Do you know what other spec that is? – Šime Vidas Mar 01 '11 at 15:03
  • @jAndy Btw, I checked in Firefox again and DOMNodeInserted works. – Šime Vidas Mar 01 '11 at 15:03
  • @Šime Vidas: Ahhh. I knew I read something similar but I couldn't find it anymore. But nice to know that those events will still be available in some form. But unfortunately, I don't know how that will look like. – jAndy Mar 01 '11 at 16:10
  • @jAndy if your going to overwrite native jQuery methods at least mention `$.sub` and that you should avoid overwriting method or other code on the page might break. – Raynos Mar 05 '11 at 18:09
  • @Raynos: no need at all to invoke `.sub()` neither to mention not to overwrite other code. In this particular instance the OP just wants to passively know what is the element of invocation. As soon as you start to modify code, I'm with you. – jAndy Mar 05 '11 at 18:16
  • @jAndy your modification is safe. It's just very easy to make non-safe modifications to known jQuery methods. I recommend using `.sub` and slapping on a more obvouis warning symbol. You have to remember that there are other jQuery extensions/plugins on the page that could making all kinds of stupid assumptions. – Raynos Mar 05 '11 at 18:28
5

You can try monitoring the .html() of the tag to see if it changes to anything else...

Maybe have a timed function (executing every n-seconds) that monitors the content (.html()) of the element until it changes and then stops monitoring it. Maybe something in the vein of:

var monitor = true;
var doMonitor = function() {
  monitor = $("#theanchor").html() != "the initial value";
  if (monitor)
    setTimeout(doMonitor,500);
};
setTimeout(doMonitor,500);

In theory this should work, but I haven't tested it.

FarligOpptreden
  • 5,013
  • 22
  • 23
  • Change that to `setTimeout(doMonitor, 500)` and it's a great answer! You should always pass functions rather than strings to `setTimeout`. – lonesomeday Mar 01 '11 at 13:19
  • Haha! Haven't done "setTimeout()" in a long time. Like I said, it should work in theory as I haven't tested it. Changed it now based on your recommendation. – FarligOpptreden Mar 01 '11 at 13:20
3

You could make use of the DOMSubtreeModified event. That event fires at an element when it's contents change.

$('a').bind('DOMSubtreeModified', function() {
    // contents changed   
});

Note: this event does not fire in Opera and older versions of IE (it works in IE9 though).

Live demo: http://jsfiddle.net/simevidas/pLvgM/

Šime Vidas
  • 182,163
  • 62
  • 281
  • 385
  • +1 for the **Note**... This will work like the W3C spec, but, as it relies on the browser, this won't work in many places. The JQuery plugin (look at my answer) is more limited (won't listen by add, remove and such DOM events) but will trigger as expected in any (IE6+, modern?!) browser. – NemoStein Mar 01 '11 at 13:26
  • @NemoStein How does that plug-in work? Could you make a jsfiddle? – Šime Vidas Mar 01 '11 at 13:34
  • Sorry @Šime, the plugin don't work anymore... I proposed the deletion of my answer... – NemoStein Mar 01 '11 at 13:51
  • @Nemo It's made for jQuery 1.3 so it presumably works in that older version. – Šime Vidas Mar 01 '11 at 13:57
0

What do you mean content of an a tag? You can do this like so:

$('#linkElementID').html();

or get an attribute like so:

$('#linkElementID').attr("title"); //Title Attribute 
$('#linkElementID').attr("href"); //href Attribute etc etc 

I don't think there is an event that is fired when a tag changes, the .change() event is only fired by input areas and select lists.

You would need to check after the event that loads the newly inserted content.

Damien
  • 13,927
  • 14
  • 55
  • 88