Programmatically invoking events - javascript

Say I add events to an object using either addEventListener or attachEvent (depending on the browser); is it possible to later invoke those events programmatically?
The events handlers are added/removed using an object like this:
var Event = {
add: function(obj,type,fn) {
if (obj.attachEvent) {
obj.attachEvent('on'+type,fn);
} else {
obj.addEventListener(type,fn,false);
}
},
remove: function(obj,type,fn) {
if (obj.detachEvent) {
obj.detachEvent('on'+type,fn);
} else {
obj.removeEventListener(type,fn,false);
}
}
}
Or do I need to store copies of each handler and just add an Event.invoke(...) function?
Edit: Also, jQuery is not an option :D

As usual, you have to do it one way for Internet Explorer, and the correct way for everything else ;-)
For IE:
document.getElementById("thing_with_mouseover_handler").fireEvent("onmouseover");
See the MSDN library for more info.
For the real browsers:
var event = document.createEvent("MouseEvent");
event.initMouseEvent("mouseover", true, true, window);
document.getElementById("thing_with_mouseover_handler").dispatchEvent(event);
Note that, although the second, standards-based approach seems more long-winded, it is also considerably more flexible: check the documentation, starting with the Mozilla DOM Event Reference at https://developer.mozilla.org/en/DOM/event
Although only tangentially related to what you're trying to do (it's related to custom events, rather than normal ones) Dean Edwards has some example code at http://dean.edwards.name/weblog/2009/03/callbacks-vs-events/ that may be worth a look.

Can you not create functions that do the work required, run those from the events then run those same functions later when required?

Related

How to Get Event Listener of an Element

Is there any way to get the list of all event listeners of an element on the HTML page using JavaScript on that page.
Note: I know we can see them with Chrome dev tools event listeners but I want to log/access see list using the JavaScript of the page.
Also, I know we can get them through jQuery but for that, we also have to apply the events using jQuery, but I want something that would be generic so I could also access the event listeners applied to other elements such as web components or react components.
If you really had to, a general way to do this would be to patch EventTarget.prototype.addEventListener:
const listeners = [];
const orig = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(...args) {
if (this instanceof HTMLElement) {
listeners.push({
type: args[0],
fn: args[1],
target: this,
});
}
return orig.apply(this, args);
};
document.body.addEventListener('click', () => console.log('body clicked'));
console.log(listeners[0].fn);
click this body
To find listeners attached to an element, iterate through the listeners array and look for targets which match the element you're looking for.
To be complete, also patch removeEventListener so that items can be removed from the array when removed.
If you need to watch for listeners attached via on, then you'll have to do something similar to the above to patch the HTMLElement.prototype.onclick getter/setter, and for each listener you want to be able to detect.
That said, although you said you want a generic solution, rather than patching built-in prototypes, it'd be better to add the listeners through jQuery or through your own function.
What I did when I had a similar problem is add a data attribute when the listener was set, so I could identify it later.
At the end of the function that adds the listener:
elm.setAttribute('data-has_mask', true);
At the beginning of that same function:
if("true" == elm.getAttribute('data-has_mask')) {
return;
}
Maybe not exactly what the OP is looking for, but I was having a lot of trouble with this, and this is an obvious solution for a particular use case, and I guess it might help someone out.

JavaScript - how to check if event already added

I want to add an listener exactly once for beforeunload. This is my pseudocode:
if(window.hasEventListener('beforeunload') === false) {
window.addEventListener('beforeunload', function() { ... }, false);
}
But hasEventListener does not exist obviously. How can I achieve this? Thanks.
In fact there is no need to check if an listener was added to a target:
If multiple identical EventListeners are registered on the same EventTarget with the same parameters, the duplicate instances are discarded. They do not cause the EventListener to be called twice, and since the duplicates are discarded, they do not need to be removed manually with the removeEventListener method.
Source:https://developer.mozilla.org/en-US/docs/Web/API/EventTarget.addEventListener#Multiple_identical_event_listeners
Using jquery you can do use data("events") on any object (here the window) :
var hasbeforeunload = $(window).data("events") && $(window).data("events").['beforeunload'];
But this works only for jquery added events.
In a more general case, you should simply store the information that you add a listener somewhere :
var addedListeners = {};
function addWindowListenerIfNone(eventType, fun) {
if (addedListeners[eventType]) return;
addedListeners[eventType] = fun;
window.addEventListener(eventType, fun);
}
I think there is no standard way in javascript to get the existing event handlers. At best you could surcharge the addEventListener function of Node to intercept and store the listeners but I don't recommend it...
EDIT :
From jQuery 1.8, event data are available in $._data(element, "events"). The change log has a warning that should be taken into account :
Note that this is not a supported public interface; the actual data
structures may change incompatibly from version to version.
In Chrome Dev tool, you can check all events attached to an element (For debugging)-
// print all events attached to document
var eventObjectAttachedToDocument = getEventListeners(document);
for (var event in eventObjectAttachedToDocument) {
console.log(event);
}

Trigger code when custom event is being bound in jQuery

JQuery has great support for custom events - .bind("foo", function(e).... However what if the mechanic of triggering the event is not ready yet and has to be constructed only on those elements that have the event bound on?
For example I want a scrollin event that gets fired when an element is scrolled into a viewport. To do this, I would onscroll have to check all the elements and trigger scrollin on those that were outside the viewport and now are inside. This is not acceptable.
There are some tricks to speed it up. For example one of the plugins for this checks all the elements in "private" $.cache and does the checking only on those that have scrollin event bound.
But that's also ugly. What I need is an additional callback for the binding of the event (additional to the callback for handling) that would take care of the scroll management, that is to put the element(s) into some elementsCheckOnScrol cache array.
I'm looking for something like:
$.delegateBind("scrollin", function(jqSelection) { ... });
element.bind("scrollin", function(e) {..}); //Calls ^ after internal bind management
Edit: This would be nice api!
$.bind("bind", function(onWhat) { ... })
:-)
If I'm not misunderstanding you, you could patch the bind method like this:
(function($) {
var oldBind = $.fn.bind;
$.fn.bind = function(name) {
if(name === "scrollin") {
delegateFunction(this);
}
oldBind.apply(this, arguments);
};
})(jQuery);
What it does is checking whether a scrollin is being bound, and if so, calls your delegate function. After that it simply calls the original bind function which does all jQuery things like it does regularly.
After having added this code, you could use it like this: http://jsfiddle.net/pimvdb/g4k2G/.
function delegateFunction(selection) {
alert(selection.length);
}
$('a').bind('scrollin', function() {});
Note that this does not support object literals being passed to .bind (only (name, func)), but you could implement that as well.
I found an $.event.special API, but I don't know "how much" public it is. It is not in the docs and has been changed at least once before. http://benalman.com/news/2010/03/jquery-special-events/

JavaScript add events cross-browser function implementation: use attachEvent/addEventListener vs inline events

In order to add events we could use this simple first solution:
function AddEvent(html_element, event_name, event_function)
{
if(html_element.attachEvent) //Internet Explorer
html_element.attachEvent("on" + event_name, function() {event_function.call(html_element);});
else if(html_element.addEventListener) //Firefox & company
html_element.addEventListener(event_name, event_function, false); //don't need the 'call' trick because in FF everything already works in the right way
}
or this second solution (that adds inline events):
function AddEvent(html_element, event_name, event_function)
{
var old_event = html_element['on' + event_name];
if(typeof old_event !== 'function')
html_element['on' + event_name] = function() { event_function.call(html_element); };
else
html_element['on' + event_name] = function() { old_event(); event_function.call(html_element); };
}
These are both cross-browsers and can be used in this way:
AddEvent(document.getElementById('some_div_id'), 'click', function()
{
alert(this.tagName); //shows 'DIV'
});
Since I have the feeling attachEvent/addEventListener are used more around in events handling implementations, I'm wondering:
Are there any disadvantages/drawbacks against using the second solution that I might better be aware of?
I can see two, but I'm interested in more (if any):
the second solution screws up innerHTML of elements by adding events inline
Using second solution I can easily remove all functions associated with a certain event type (html_element['on' + event_name] = null), but I can not use detachEvent/removeEventListener to remove exactly a specific function.
Any answers like: "use jQuery" or any other framework are pointless!
With the 2nd solution, you have to manually call the previous functions, making it hard to remove specific listeners (which, to me, sounds like something you'd rather want than clearing all listeners), while on the first solution, you can only clear them all at the same time, unless you want to emulate the first functionality.
Personally, I always use the first solution, because it has the advantage of not having to worry about clearing possible other event listeners.
The mozilla wiki also lists the advantages that the first solution works on any DOM element, not just HTML elements, and that it allows finer grained control over the phase when the listener gets activated (capturing vs. bubbling) with the third argument.
i would use both codes like this
function addEvent(html_element, event_name, event_function) {
if (html_element.addEventListener) { // Modern
html_element.addEventListener(event_name, event_function, false);
} else if (html_element.attachEvent) { // Internet Explorer
html_element.attachEvent("on" + event_name, event_function);
} else { // others
html_element["on" + event_name] = event_function;
}
};

How to Fire Personal Event in Javascript

I can't fire personal events using Javascript in IE. In Firefox work great.
My code is:
var evento;
if(document.createEventObject)
{
evento = document.createEventObject();
document.fireEvent('eventoPersonal', evento);
}
//FF
else
{
evento = document.createEvent('Events');
evento.initEvent('eventoPersonal',true,false);
document.dispatchEvent(evento);
}
But when try to execute document.fireEvent('eventoPersonal', evento); in IE, it doesn't work. How can I fire NO custom events in IE?
In Internet Explorer I get the error: "Invalid arguments" in the line where execute document.fireEvent('eventoPersonal', evento);
Dean Edward's describes how to fire cutsom events in IE
http://dean.edwards.name/weblog/2009/03/callbacks-vs-events/
Its near the bottom of the article
var currentHandler;
if (document.addEventListener) {
// We've seen this code already
} else if (document.attachEvent) { // MSIE
document.documentElement.fakeEvents = 0; // an expando property
document.documentElement.attachEvent("onpropertychange", function(event) {
if (event.propertyName == "fakeEvents") {
// execute the callback
currentHandler();
}
});
dispatchFakeEvent = function(handler) {
// fire the propertychange event
document.documentElement.fakeEvents++;
};
}
I think the answer is - in IE you can not fire events that are not on this list:
MSDN - DHTML Events
From what I can gather, frameworks store a registry of the "custom" event names and you must use their implementation specific trigger and handle functions for custom events. For example, prototype uses the ondatavailable event to pass through their custom events behind the scenes.
You may want to consider using a library to abstract this. Both prototype an jquery will handle this for you. Jquery is especially good at allowing you to create an event with very simple code.
Jquery's documentation is available here:
http://docs.jquery.com/Events
In IE11 document.dispatchEvent still doesn't work, but now attachEvent is missing too, so the other solution is not going to work either. However, I came up with one even uglier. :) It involves replacing the addEventListener method and goes on like this:
var oldEventListener = document.addEventListener;
document.addEventListener = function (event, func, capture) {
if (event == "MyPreciousCustomEvent") {
document.MyPreciousCustomEvent = func;
}
oldEventListener.call(document, event, func, capture);
};
...
$(function () {
try {
document.MyPreciousCustomEvent("MyPreciousCustomEvent", {});
} catch (e) {}
});
Hope this helps someone.
As I read the relevant MSDN article page on the createEventObject method, it appears as though it isn't used for creating custom event - it is used for creating custom objects that can be passed to already existing events.
Description:
Generates an event object to pass event context information when you use the fireEvent method.
http://msdn.microsoft.com/en-us/library/ms536390%28VS.85%29.aspx
Update: You are getting the "invalid arguments" error because 'eventoPersonal' is not an acceptable event to fire.
Yeah referring to #Don Albrecht, you can use jquery trigger() method more on http://api.jquery.com/trigger/

Categories