Assume I have a javascript function createMyElement which returns a node that can be inserted into an HTML document.
In order to function properly, the code of the node created by createMyElement has to listen for events on the global document at least as soon as it is inserted in the document.
My first attempt was to add DOMNodeInsertedIntoDocument and DOMNodeRemovedFromDocument listeners to the node at creation time that add and remove the needed listener on document in turn.
However, the mutation events are deprecated by now (and don't seem to work reliably across browsers), so I am looking for a better solution.
Adding the listener for events at document at the creation time of the node would work. However, this doesn't seem to be a good solution as it would create memory and performance leaks: Even after the node was being removed from the document again and not needed anymore, the listener (and its references to the node) on the document would still persist.
Use a series of function callbacks or on-demand script callbacks to serialize the events.
Since I originally asked my question, the substitute for mutation events, namely mutation observers as defined in http://www.w3.org/TR/domcore/#mutation-observers, have been implemented in a number of browsers.
So a simple answer to my own question is to simply use mutation observers on the document to listen for nodes to be inserted or removed.
An even better way, however, is to use the new custom elements from https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html, which gives one insertedCallbacks and removedCallbacks for custom elements.
Related
Given this sample code:
function someMethod(elements) {
var observer = new MutationObserver(function(events) {
SomeLib.each(events, function(event, k, i) {
if ( event.removedNodes ) {
SomeLib.each(event.removedNodes, function(removedElement, k, i) {
console.log(222, removedElement)
});
}
});
});
SomeLib.each(elements, function(element, k, i) {
console.log(111, element)
observer.observe(element, {
childList : true,
subtree : false
});
});
}
I've noticed that if I call someMethod(parentElement) and then call it again later someMethod(parentElement.querySelector('someChildElement'))
The first one is the only one that triggers events and appears as if the second call does not trigger any events.
This is unfortunate as I am mostly interested in an event when the actual node is removed. Nothing else. Child nodes are really not of interest either, but childList or data... option has to be true so I am forced to I guess.
I can not organize my code around keeping track of who's a parent is already tracked or not, and therefore I would have found it much easier to simply listen to remove events on any particular node, whatever way it is eventually deleted.
Considering this dilemma, I am considering registering a MutationObserver on the document element and instead rely on detecting the element I wish to observe myself through my own event handler.
But is this really my best option?
Performance is obviously of concern since everything will fire this document listener, but perhaps just having one MutationObserver potentially efficient since I will only be triggering my own function when I detect the element of interest.
It requires iteration however, on removedNodes and addedNodes potentially, so it has a real effect on everything rather than just me observing the node.
This begs the question, is there not already a global mutation observer already registered?
Do I really have to manually observe the document myself?
What if other libraries also start to observe things similarly either on body or child elements?
Won't I destroy their implementation? (Not that I have just dependency) but this is worrying how horrible this implementation really seems to be, but not surprising considering how everything has been horrible with the web since the dawn of day. Nothing is ever correctly implemented. Thank you w3c.
Is MutationObserver really the way here? Perhaps there are node.addEventListener('someDeleteEvent') I can listen to instead?
Why are we being recommended away from DOMNodeRemoved like events, but can we really do the replacement? Especially since performance penalty seems real using MutationObserver I wonder why everywhere "smart people" are recommending us away from DOMNodeRemoved?
They are not the same. What is the idea of deprecating those anyway since this seems kind useless and potentially problematic to use.
For now, I have already implemented this global document listener that allows me to detect nodes I am interested in only, and fire the functions I desire when found. However, performance might be hit. I am not sure.
I am considering scrapping the implementation and instead rely on "deprecated" DOMNodeRemoved regardless unless someone can chip in with some thoughts.
My implementation simply registered on document and then basically looks at each element if they have the custom event key on them, and fire it if they do. Quite effecient but requires iteration similar to:
On each mutation observed across entire document.
TLDR Below
JS Fiddle To Demo
I've been really involved in recreating the tools that are foundations of premiere JS Libraries to better improve my skills. Currently I'm working on functional data-binding a la Angular.
The idea of data-binding is to take data and bind it to elements so that if manipulated all elements subscribed will change accordingly. I've gotten it to work but one thing I hadn't considered going into it was the issue with innerHTML vs value. Depending on the element you need to change one or the other( in the demo above you'll see that I needed to specifically single out the button element in a conditional statement because it has both, but that's kind of a fringe case )
The issue is that in order to capture a SPAN tag update I needed to trigger an event to happen, and the easiest one to manipulate for Text Boxes/Textareas was 'keyup'.
In my function then, if you pass in an element with no value property we assume you're going to be updating innerHTML, and we setup an observer to determine if the element ever mutates, and if it ever does, the observer will emit a 'keyup' event.
if (watchee.value == void(0)) {
var keyUpEvent = new Event('keyup');
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
watchee.dispatchEvent(keyUpEvent);
});
});
observer.observe(watchee, {
childList: true
});
}
Now it may just be my paranoia, but it seems like I might be tunneling into a can of worms by faking 'keyup' on an element that doesn't natively have that support.
TLDR:
I'm curious if there's an alternative way to make, a.e. a span tag reactive other than faking a 'keyup'/'keydown'/'change' event? For instance, is there a way that I can make my own pure event(by pure I mean not reliant on other events) that checks if innerHTML or value has changed and then performs a function? I know that this is probably possible with a timer, but I feel like that might hinder performance.
EDIT: just an aside. In the demo the function called hookFrom works by taking a DOM node and returning a function that will take the receiving dom node and continues to return a function that will take additional receiving dom nodes. :
hookFrom(sender)(receiver);
hookFrom(sender)(receiver)(receiver2);
hookFrom(sender)(receiver)(receiver2)(receiver3)(receiver4)...(receiver999)...etc
JS Fiddle To Demo (same as above)
There is nothing inherently wrong with creating a similar event on a DOM node that doesn't natively have that functionality. In fact this happens in a lot of cases when trying to polyfill functionality for separate browsers and platforms.
The only issue with doing this sort of DOM magic is that it can cause redundancy in other events. For instance the example given in this article: https://davidwalsh.name/dont-trigger-real-event-names shows how a newly minted event using the same event name can cause problems.
The advice is useful, but negligible in this specific case. The code adds the same functionality between text boxes, divs, spans, etc... and they are all intentionally handled the same way, and if the event would bubble up to another event, it would be intentional and planned.
In short: There is a can of worms that one can tunnel into while faking already explicitly defined event names, but in this case, the code is fine!
I have a div with class="backdrop". This will be added to DOM when I click a button. To this is bound an event of 'wheel'.
I cannot avoid the binding of the event(happening through library) So i will want to unbind this globally.
I tried : $(".modal-backdrop.am-fade").unbind('wheel');
This works but I have to write this statement after each time the div is added to the DOM. I want something which I can write only once and would apply to all the divs which would be added to the DOM in future
I want something which I can write only once and would apply to all the divs which would be added to the DOM in future
If code in the library you're using is adding elements and binding events to them, you'll have to unbind them as you go, there's no alternative.
If you already have some way of triggering that (the library tells you when it adds a div), then you'll just have to have that code respond to that event.
If you don't already have some way of triggering it, you can use mutation observers to watch for the divs being added so you can unbind them. They're well-supported by modern browsers. Slightly less modern browsers may have sufficient support for the old mutation events that you can use a library that provides a mutation observer polyfill using mutation events (just search for "mutation observer polyfill"). Worst case, on really old browsers, you'll have to poll.
Of course, the best answer is to either stop using the library if it doesn't do what you want, or modify it (this is JavaScript, after all) so it doesn't do what you don't want.
I have a question on how we can possibly figure out which elements, among existing ones on the DOM, have an attached function through 'EventListener'. I know that it is not easy at all and I know that I can check it using Visual Event.
But, I would like to know how Browsers or even more interesting, how Visual Event can detect that?
Let's say we are going to load a webpage and extract all the clickable elements from the DOM. How possibly we can determine that some of the existing elements won't change the status of the DOM?
For example, if we don't know anything about the elements, we would need to try to click even on P tags since there might be some functions attached to those. But, if we can determine whether or not this particular element will do anything after clicking on that, then we would be able to ignore it if it is not going to do anything.
Is there any straight way by which we can do something which Visual Event is doing?
There are no built-in functions that support this.
The neatest way I know of is to overwrite the addEventListener method on Element.prototype with your own method that records the additions/removals of event listeners on DOM nodes. You can then expose a function to enumerate them.
This modification will of course need to be run before the relevant event listener activity in your application.
See How to find event listeners on a DOM node when debugging or from the JavaScript code?
Note that Chrome has built in support for this functionality via getEventListeners and a dedicated UI in the dev tools.
Would like all new elements with class .link to have a tabindex.
Delegate/Live does not seem to work:
$('body').delegate('.link', 'load', function(event){
$(this).attr('tabindex',0);
});
Trying to apply this to AJAX loaded elements. And using what I found in this answer, which suggests the "load" event may be possible.
I'd like to avoid using trigger, or modifying the AJAX callback.
The problem here is that no events are triggered when a new element is inserted into the DOM1. The "solution" (not the one you're looking for, unfortunately) is to set the tabindex from the complete callbacks of your ajax operations. You may use .ajaxComplete() to setup a global/default callback, but that may introduce new problems (such as having to deal with the order events are fired).
Well, that's not 100% accurate; there are the Mutation Events, which are not implemented consistently across different browsers, and are supposed to be replaced by Mutation Observers.