Detect newly inserted element in the document - javascript

I'm trying to detect if an element with a specific tag name has been inserted into the document. I am aware of DOMSubtreeModified and MutationObserver and I know that they can detect changes in the elements, but if the document is big and many changes are applied to the document, these two methods can become quite heavy.
One of the ideas I had was to collect all elements using getElementsByTagName and then detect a change of HTMLCollection's length property but I didn't find any method that could watch this property and trigger an event.
Another idea I had was to set an interval, but the problem with this is that an item can be deleted and inserted in between the timer and this wouldn't be detected in the interval's function.
Is there any efficient way of detecting new element insertion in the whole document? Alternatively, how can I detect change of HTMLCollection's length property?
Thanks for any answer.

Here is a thought:
var cnt=0;
var f = Element.prototype.appendChild;
Element.prototype.appendChild = function(){
f.apply(this, arguments);
console.log("added",++cnt)
};
However you will need to see if it is the same element that is added and I have not figured out how to check the remove since that is parentNode.removeChild
If you want events on all selects in jQuery all you have to do is delegate
$(document).on("change","select",function() {
// all current and future selects will have this event
});

Related

js, check the child name or selector from parent from a delegate assigned event

I have a difficult question.
events = $._data( element[0], 'events');
$.each(events, function(_event_name, _event_handler){
var _handlers=[];
for(var i= 0 ;i < _event_handler.length;i ++)
_handlers.push(_event_handler[i].handler);
event_handler.push(_handlers);
event_name.push(_event_name);
});
element.off();
I have above code to successfully read all what event name and its handler assigned for a element.
Then I save each of them into event_name and event_handler before I turn the events off;
However, this method is only work on when the events are directly assigned for the element.
When the events are delegated assigned, how can I develop the code to do that?
$(document).on('click', '#id', handler);
Above code will only show the event name click and its handler handler, but no the name or selector of the delegate assigned element #id.
I want to know how can I read the name of delegate assigned element out that I can do delegate off or all the events on parent will be off.
Thank you very much for your advice.
The selector is stored in the object within the array of click events under selector as seen below.
It seems that the selector field is empty when you look at click events bound directly to the element.
I would assume you can build a check into your code somehow. Mind you, the selector property is apparently only intended for internal use as I had a question myself in the past about it, when I found it contained occasionally strange values.
To that end, though it might work for you, I do not know how reliable it is or even if it is going to be used in future versions of jQuery.
However, if you only use it for delegate events you might find it contains useful values but keep an eye on it.

jQuery on() method - which way to use is better for performance?

Is it better to attach the on() event to the document or a closer parent?
Note: Initially this question had another aspect and a different topic. It became obsolete really quickly (typo in the source code)
The best key for performance using jQuery is to use an id as the initial identifier. For example:
$('#my_id').on('click', 'tag.my_class', function () {
...
});
This allows jQuery to go straight to the container, and then begin trawling from there.
if you bind the "on" event to the closest parent will produce exactly what are you looking for, click function will works fine even if it is appended to document, but in future if you append any elements with class "clickable" will also get binded. so its always good practice to append the "on" event to closest parent rather than whole document.
if you want more specific you can use
$("ul.media-grid").on('click', 'li.clickable', function () {
alert("works")
});
as it will get the ul with the class "media-grid" and appends the event to the li's with class "clickable"

jQuery: Detach without string selector

I'm trying to detach a DOM element to append it to another DOM element. But jQuery refuses to do anything, silently.
Thing is, I can't use a string selector, because I don't know how to select this element. I've stored it in a variable when I first appended some html code to the initial parent (through "appendTo".
this.element = $(my_html_string).appendTo(some_dom_parent);
And that works fine. The code that is not working as expected, is following:
this.transferTo = function(dom_parent)
{
$(this.element).detach()
$(this.element).appendTo(dom_parent);
}
What happens is:
The element is NOT removed from wherever it is.
The element IS appended to the new parent.
Previously bind click events are triggered on both elements.
That click event appends a popup dialog to the element. It's being appended to the element in the new parent, always, regardless which one I click.
I tried some hardcoded detach like:
$('#very_specific_id').detach()
... and it works. But thing is, I don't have IDs placed around, and sounds like a very bad way to do this.
So the problem seems to rely on the fact I'm saving a jQuery DOM Element and trying to use .detach from it, instead of using a $(".query") like everyone else.
Ideas? Workarounds? Thanks!
Try changing this:
this.transferTo = function(dom_parent)
{
$(this.element).detach()
$(this.element).appendTo(dom_parent);
}
to this:
this.transferTo = function(dom_parent)
{
var $thisElement = $(this.element);
$thisElement.detach()
$thisElement.appendTo(dom_parent);
}

jQuery: How to listen for DOM changes?

Is it possible and how can I listen for changes through the entire DOM tree with jQuery?
My specific issue: I have a 'tooltip' function that displays the contents of the title attribute in a stylish way when you do a hover on any html element. When you do a hover, however, by standard the browser renders the title in its own box. I would like to supress that. So what I've thought of is to move the contents of the title attribute to a custom (HTML5) data-title attribute the first time the page is loaded, and then my tooltip function will work with data-title.
The problem is that later on I might add / remove / change the HTML dynamically, so I need to 'rebind' those elements - change those title attrs again. It would be nice if there was an event listener that would listen for such changes for me and rebind the elements automatically.
My best guess is that you want to listen to DOM mutation events.
You can do that by DOM mutation event as any normal javascript event such as a mouse click.
Refer to this : W3 MutationEvent
Example:
$("element-root").bind("DOMSubtreeModified", "CustomHandler");
[edited in reply to research by member Tony]
So, without additional code, this is a bit of a blind shot, but it seems to me there are two things to think about here: 1. the default browser tooltip behaviour; 2. a potentially updated DOM and the ability for your custom tooltips to continue functioning.
Regarding #1: when you bind your custom event to the element, you can use event.preventDefault() so that the tooltips don't appear. This doesn't work properly. So, the workaround to keep using the "title" attribute is to grab the value, push it into the data object (the $.data() function), and then null the title with an empty string (removeAttr is inconsistent). Then on mouseleave, you grab the value out of the data object and push it back into the title. This idea comes from here: How to disable tooltip in the browser with jQuery?
Regarding #2: instead of re-binding on DOM change, you just need to bind once to a listener element that is never expected to be destroyed. Usually this is a container element of some sort, but it can even be document (approximating .live() which is now deprecated) if you really need an all-encompassing container. Here's a sample that uses some fake markup of my own devising:
var container = $('.section');
container.on('mouseenter', 'a', function() {
var $this = $(this);
var theTitle = $this.attr('title');
$this.attr('title', '');
$('#notatooltip').html(theTitle);
$.data(this, 'title', theTitle);
});
container.on('mouseleave', 'a', function() {
$('#notatooltip').html('');
var $this = $(this);
var storedTitle = $.data(this, 'title');
$this.attr('title', storedTitle);
});
My unrealistic markup (just for this example) is here:
<div class="section">
Hover this foo!
<div id="notatooltip"></div>
</div>
And a fiddle is here: http://jsfiddle.net/GVDqn/
Or with some sanity checks: http://jsfiddle.net/GVDqn/1/
There's probably a more optimal way to do this (I honestly didn't research if you could bind two separate functions for two separate events with one selector) but it'll do the trick.
You shouldn't need to re-bind based on DOM change, the delegated listener will automatically handle it. And you should be able to prevent default tooltip functionality just by preventing it.
You need to look at this here: http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-eventgroupings-mutationevents
As noted by Greg Pettit, you should be using the on() function on the element.
What this does is allows you to bind a selector to an event, then jQuery will add this event handler when the objects returned by the selector are available.
If you wanted a function to fire on a mouse over event and you wanted it to fire on all elements with the class of *field_title* you would do this:
$('.field_title').bind('mouseenter', function() { doSomething(); });
This will trigger on the over mouse over event on any objects that have the class of *field_title* and execute the function doSomething().
Hope that makes sense :)

'document' vs. 'content.document'

I'm trying to write a Firefox extension that adds elements to the loaded page. So far, I get the root element of the document via
var domBody = content.document.getElementsByTagName("BODY").item(0);
and create the new elements via
var newDiv = content.document.createElement("div");
and everything worked quite well, actually. But the problems came when I added a button with on onclick attribute. While the button is correctly displayed, I get an error. I already asked asked here, and the answer with document.createElement() (without content) works.
But if I remove the 'content.' everywhere, the real trouble starts. Firstly, domBody is null/undefined, no matter how I try to access it, e.g. document.body (And actually I add all elements _after_the document is fully loaded. At least I think so). And secondly, all other elements look differently. It's seem the style information, e.g., element.style.width="300px" are no longer considered.
In short, with 'content.document' everything looks good, but the button.onclick throws an error. with only 'document' the button works, but the elements are no longer correctly displayed. Does anybody see a solution for that.
It should work fine if you use addEventListener [MDN] (at least this is what I used). I read somewhere (I will search for it) that you cannot attach event listener via properties when creating elements in chrome code.
You still should use content.document.createElement though:
Page = function(...) {
...
};
Page.prototype = {
...
addButton : function() {
var b = content.document.createElement('button');
b.addEventListener('click', function() {
alert('OnClick');
}, false);
},
...
};
I would store a reference to content.document somewhere btw.
The existing answer doesn't have a real explanation and there are too many comments already, so I'll add another answer. When you access the content document then you are not accessing it directly - for security reasons you access it through a wrapper that exposes only actual DOM methods/properties and hides anything that the page's JavaScript might have added. This has the side-effect that properties like onclick won't work (this is actually the first point in the list of limitations of XPCNativeWrapper). You should use addEventListener instead. This has the additional advantage that more than one event listener can coexist, e.g. the web page won't remove your event listener by setting onclick itself.
Side-note: your script executes in the browser window, so document is the XUL document containing the browser's user interface. There is no <body> element because XUL documents don't have one. And adding a button won't affect the page in the selected tab, only mess up the browser's user interface. The global variable content refers to the window object of the currently selected tab so that's your entry point if you want to work with it.

Categories

Resources