I am using a third party script that I don't want to modify.
In this script, there is a function that I need to listen for that adds a class to a dom node.
function _showElement(targetElement) {
targetElement.element.className += 'this-class';
}
What I would like to do is listen for this element to be called and whenever it does add a class to its parent with pure javascript.
function addThis() {
var doc = document;
liveEl = doc.getElementsByName("this-class");
// Code to add class to parent
}
The simplest "brute force" solution is to hijack (redefine) the function:
function _showElement(targetElement) {
console.log('original');
}
_showElement(); // hijacked
function _showElement(targetElement) {
console.log('hijacked');
}
Unfortunately i don't think you can save the original behavior of the function, so you need to copy-paste it:
function _showElement(targetElement) {
targetElement.element.className += 'this-class';
addThis()
}
Another solution, more specific to your problem (do something when the className of an element changes) you can listen for mutation events on the DOM:
https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
Basically you create a MutationObserver and observe attribute changes on your element. It might not be such a great solution if you need to track all the nodes, but in some cases it might come in handy.
You can't do this directly. You can't listen event like dom attribute change. However, there is a way that you can identify the event which is trigger _showElement and attach a one more handler to the event which should call addThis function.
Related
I would like to understand how to remove the event listeners which are added by class as shown below (remove event for div#mainMenu)
I have tried all available methods but none to my avail. Also I dont see this issue in the extJS4.x version but only in 3.4 version. Any plain JS code to handle this?
You can use getEventListeners function to get listeners map of an element. After that use removeEventListener method, to remove event from element. You can use this function to remove all events from an element.
function removeEventListeners(element, listenerMap) {
Object.keys(listenerMap).forEach(function (name) {
var listeners = listenerMap[name];
listeners.forEach(function (object) {
element.removeEventListener(name, object.listener);
});
});
}
removeEventListeners(elementRef,getEventListeners(elementRef));
Finally, I have figured out how to resolve this issue. The way to remove the listener is to go to parent div component & call removeAllListeners() method on the element. The element can be accessed in the afterRenderHandler function using this.el & then calling this.el.parent().removeAllListeners() so that it removes the un-required listeners interfering with the user experience. You could call the parent method in a chained fashion if the listener identified is a nth level parent (call the method n times).
Ex: this.el.parent().parent().removeAllListeners()
You could also try to access the element directly if you have the ID by calling: var el = Ext.get('mainMenu'); & then calling el.removeAllListeners();
Similar approach can be applied to any other JS frameworks by getting a handle to the element having an issue with the listeners.
Is it a good idea to manage all click events under the document element? The DOM is being constantly manipulated, so instead of constantly registering new events for each newly created DOM element, can't I just assign one event handler on the document element? For example:
document.onclick = function(event) {
switch(event.target.id) {
case 'someid':
// SOME ACTION
break;
case 'someotherid':
// SOME OTHER ACTION
break;
default:
// A CLICK WITH NO ACTION
}
};
Yes. This pattern is called event delegation, you can find a great article on the blog of David Walsh
You should also take a look at the Element matches / matchesSelector API
-https://developer.mozilla.org/es/docs/Web/API/Element/matches
-https://davidwalsh.name/element-matches-selector
You can do this, but it's not as efficient as binding events to specific elements. It means your function will run if someone clicks in a place that isn't mentioned in any of your cases. And even if it is, it will have to search sequentially through your cases until it finds the right one.
A somewhat better way to do it is to use an object keyed off the IDs.
var handlers = {
"someid": function(event) { // some action
},
"someotherid": function(event) { // some other action
},
...
}
document.onclick = function(event) {
if (handlers[event.target.id]) {
handlers[event.target.id](event);
} else {
// default action
}
}
This addresses the sequential searching problem, but it still runs when someone clicks on an unbound element. This probably isn't much of an issue for clicks, but imagine doing the same thing for mouse movement events, which occur almost constantly.
Also, this doesn't generalize easily to binding handlers to classes or more complicated selectors.
What you're doing is similar to how jQuery implements .on() event binding, when you write:
$(document).on("click", "someSelector", handlerFunction);
This form is generally only used when specifically needed, which is when the elements that match the selector are created dynamically -- it allows you to define the handler once, not add and remove it as elements change. But for static elements, we generally use the simpler
$("selector").on("click", handlerFunction);
because then the browser takes care of running the handler only when one of the selected elements is clicked.
I need some help with the callbacks. For some reason, they don't work really well.
I'm making a game with jQuery. I have a <div id='button'></div> for all the buttons that are going to be in the game. The game is going to have two buttons that make actions, and a question on top of it. The question is controlled by a <h3 id='text'></h3>. What I want to know, is that for some reason I can't set callback functions to the button's ID's. In example,
I'd have the yes or no, that have their own id's set through jQuery like this:
$('#button').html('<button id='yes'>Yes</button><button id='no'></button>');
But for some reason, I would be able to set this:
$('yes').click(function(){
//function I would want
});
Of course, that's not what my code has, that was just an example. Here's the real code:
$(document).ready(function(){
$('#main,#batman,#car,#cop,#hobo,#knife,#gangfight,#ganggun,#gangknife,#blood,#hr').hide(-100);
var hr=$('#hr');
var main=$('#main');
var batman=$('#batman');
var car=$('#car');
var hobo=$('#hobo');
var cop=$('#cop');
var knife=$('#knife');
var gangfight=$('#gangfight');
var ganggun=$('#ganggun');
var gangknife=$('#gangknife');
var blood=$('#blood');
var text=$('#text');
var button=$('#button');
$('#start').html('Are you ready to play?');
$('#button').html('<button id="yes">Yes</button><button id="no">No</button>');
$('#yes').click(function(){
$('#yes,#no').hide(function(){
$('#start').hide();
main.fadeIn(-100);
hr.fadeIn(-100,function(){
text.delay(1000).html("You were just wandering around in the streets of new york, when suddenly.. You see batman!! You've never really liked him, what do you do?")
button.html('<button id="fight">Fight</button><button id="leave">Leave</button>',function(){
batman.fadeIn(1000);
$('fight').click(function(){
});
$('leave').click(function(){
text.fadeOut(function(){
text.text('Good call. As you leave, you encounter a hobo. What do you do?');
});
});
});
});
});
});
$('#no').click(function(){
$('#yes,#no').hide();
$('#start').text('Oh, okay then. Come back later!');
});
});
I'm just wondering.. How can I set callback functions to the 'fight' and 'leave'.
If you're wondering why there's all these variables at the start, those are just the images and characters.
You can't set a click handler on an element that doesn't exist. What you should do is use .on to bind a element further up the tree. Something like:
$("#someparentelement").on("click", "#yes", function() {
// your code
});
Which version of jQuery are you using? You should probably use jQuery.on() in this situation since your click handler code probably gets executed before the button is actually available in the DOM.
$("#button").on("click", "#yes", function (event) {
// Your yes-button logic comes here.
});
For more details and possibilities, read about the .on(events [, selector ] [, data ], handler(eventObject)) method in the jQuery documentation:
If selector is omitted or is null, the event handler is referred to as direct or directly-bound. The handler is called every time an event occurs on the selected elements, whether it occurs directly on the element or bubbles from a descendant (inner) element.
When a selector is provided, the event handler is referred to as delegated. The handler is not called when the event occurs directly on the bound element, but only for descendants (inner elements) that match the selector. jQuery bubbles the event from the event target up to the element where the handler is attached (i.e., innermost to outermost element) and runs the handler for any elements along that path matching the selector.
In this case, you want to delegate the event since your element is not yet available in the DOM when you're binding the event.
Don't use the click(), use on('click') and attach it to the document.
Creating a handler this way, will ensure that any new elements will be able to trigger the event.
$('fight') selects fight tag, not the tag with fight id. Try to use $('#fight') instead.
I want to add mouse events on to the class .piece even if the divs are created after the DOM is loaded.
Here is my actual loop:
var piece = document.getElementsByClassName('piece');
function theLoop() {
for(var i=0; i<piece.length; i++) {
piece[i].addEventListener("mousemove", function(event) {
//do stuff
}
}
}
theLoop();
It works fine, but if I add new divs with the class .piece after the DOM loaded, the loop ignores them.
I add those new divs with .cloneNode() and .appendChild(), like this :
function createPiece(symbol, name) {
var clone = document.getElementById(symbol).cloneNode(true);
clone.setAttribute('id',symbol+'-'+name);
document.getElementById(name).appendChild(clone);
}
createPiece(symbol, name);
All the original cloned divs have the class .piece.
How can I add those new divs to my for loop, and have the mouse event attached to them?
Thanks.
If your loop runs on page load, it's not possible to make it account for elements added to the DOM dynamically afterwards. Usually, event delegation would be a solution, but it's not a good solution for the mousemove event (since it fires so often).
What you could do is create a function that adds the event handler to a passed element. You could then call that function from your current loop, and call it again after adding each new element to the DOM.
Put a mousemove event handler on a common parent of all the pieces and then examine e.target in the event data structure to see which piece the event actually occurred on.
This is called delegated event handling and allows you to install just one event handler on a static parent that will give you events from all children, even children that are dynamically added later.
The only other option is to install an event handler on each specific piece when it's later added to the page. You would need to trigger this from the actual code that adds the element to the page as there is no well-supported, cross-browser way to watch for DOM modifications. If you do it this way, you will need to make your event handling function a named function (rather than the anonymous function you are using now) so that you can use the same function in multiple places.
You don't need the loop for newly created elements. Nor do you need event delegation (though it can be useful).
Just make the handler a named function, and bind when you clone.
var piece = document.getElementsByClassName('piece');
function pieceHandler(event) {
//do stuff
}
function theLoop() {
for(var i=0; i<piece.length; i++) {
piece[i].addEventListener("mousemove", pieceHandler, false)
}
}
theLoop();
function createPiece(symbol, name) {
var clone = document.getElementById(symbol).cloneNode(true);
clone.addEventListener("mousemove", pieceHandler, false); // assign handler
clone.setAttribute('id',symbol+'-'+name);
document.getElementById(name).appendChild(clone);
}
createPiece(symbol, name);
I want to attach a function to a jQuery element that fires whenever the element is added to the page.
I've tried the following, but it didn't work:
var el = jQuery('<h1>HI HI HI</H1>');
el.one('load', function(e) {
window.alert('loaded');
});
jQuery('body').append(el);
What I really want to do is to guarantee that another jQuery function that is expecting some #id to be at the page don't fail, so I want to call that function whenever my element is loaded in the page.
To clarify, I am passing the el element to another library (in this case it's a movie player but it could be anything else) and I want to know when the el element is being added to the page, whether its my movie player code that it is adding the element or anything else.
I want to attach a function to a
jQuery element that fires whenever the
element is added to the page.
You want the livequery plugin, which does just this. The recent live function is similar, except it won't call you when the element is added. We use it all the time-- works great.
You'll use $('h1').livequery(function() {alert('just added');});
I do not know that there is this type of event, what comes to mind is creating the event "el-load" based on this tutorial, and then extend "append" to know if the item has this event make the call to it.
Use LiveQuery (jQuery plugin), and attach a load event to ur dom element (h1), in this case.
try overwriting the append method so you can add your own event?
jQuery.extend(jQuery.fn, {
_append: jQuery.fn.append,
append: function(j) {
this._append(j);
j.trigger('append');
}
});
var el = jQuery('<h1>HI HI HI</H1>');
el.one('append', function(e) {
window.alert('loaded');
});
jQuery('body').append(el);
If the tag is being created via ajax, you can use a related node to subscribe to the ajaxSuccess event.
http://docs.jquery.com/Ajax/ajaxSuccess
$('#somenode').ajaxSuccess(function(){
if ($('h1').length > 0) { ... }
});
If it's just being added to the DOM by a local script, I'm not sure it's possible to observe it's creation, with the exception of using a timer to poll for it.
Depending upon the browsers you need to support there are DOMNodeInserted and DOMNodeInsertedIntoDocument events. Can't vouch for how well they work myself but theoretically you could bind to these events and then either check the new node and the possible subtree that was inserted, or just check the entire document again with a $(selector) of your choosing to detect the node you're waiting to see added.
Try these:
var el = jQuery('<h1>HI HI HI</H1>');
jQuery('body').append(el);
setTimeout(function(){
window.alert('loaded');
},1000);//delay execution about 1 second
or to be safe this one:
var el = jQuery('<h1>HI HI HI</H1>');
jQuery('body').append(el);
window.checker = setInterval(function(){
if($('someselector').length>0){ //check if loaded
window.alert('loaded');
clearInterval(window.checker);
}
},200);
basically, this will loop every 200ms until the selector gets result, and terminates the loop when the result is available