I have a link that has a listener attached to it (I'm using YUI):
YAHOO.util.Event.on(Element, 'click', function(){ /* some functionality */});
I would like to the same functionality to happen in another scenario that doesn't involve a user-click. Ideally I could just simulate "clicking" on the Element and have the functionality automatically fire. How could I do this?
Too bad this doesn't work:
$('Element').click()
Thanks.
MDC has a good example of using dispatchEvent to simulate click events.
Here is some code to simulate a click on an element that also checks if something canceled the event:
function simulateClick(elm) {
var evt = document.createEvent("MouseEvents");
evt.initMouseEvent("click", true, true, window,
0, 0, 0, 0, 0, false, false, false, false, 0, null);
var canceled = !elm.dispatchEvent(evt);
if(canceled) {
// A handler called preventDefault
// uh-oh, did some XSS hack your site?
} else {
// None of the handlers called preventDefault
// do stuff
}
}
You're looking for fireEvent (IE) and dispatchEvent (others).
For YUI 3 this is all wrapped up nicely in Y.Event.simulate():
YUI().use("node", function(Y) {
Y.Event.simulate(document.body, "click", { shiftKey: true })
})
You can declare your function separately.
function DoThisOnClick () {
}
Then assign it to onclick event as you do right now, e.g.:
YAHOO.util.Event.on(Element, 'click', DoThisOnClick)
And you can call it whenever you want :-)
DoThisOnClick ()
In case anyone bumps into this looking for a framework agnostic way to fire any HTML and Mouse event, have a look here: How to simulate a mouse click using JavaScript?
1) FIRST SOLUTION
The article http://mattsnider.com/simulating-events-using-yui/ describes how to simulate a click using YAHOO:
var simulateClickEvent = function(elem) {
var node = YAHOO.util.Dom.get(elem);
while (node && window !== node) {
var listeners = YAHOO.util.Event.getListeners(node, 'click');
if (listeners && listeners.length) {
listeners.batch(function(o) {
o.fn.call(o.adjust ? o.scope : this, {target: node}, o.obj);
});
}
node = node.parentNode;
}
};
As you can see, the function loops over the node and its parents and for each of them gets the list of listeners and calls them.
2) SECOND SOLUTION
There is also another way to do it.
For example:
var elem = YAHOO.util.Dom.get("id-of-the-element");
elem.fireEvent("click", {});
where function is used as
3) THIRD SOLUTION
Version 2.9 of YUI2 has a better solution: http://yui.github.io/yui2/docs/yui_2.9.0_full/docs/YAHOO.util.UserAction.html
4) FORTH SOLUTION
YUI3 has also a better and clean solution: http://yuilibrary.com/yui/docs/event/simulate.html
Of course $('Element').click() won't work, but it can if you include jquery, it works well alongside yui.
As I untestand you need to do the following:
function myfunc(){
//functionality
}
YAHOO.util.Event.on(Element, 'click', myfunc);
Then call myfunc when something else needs to happen.
The inability to simulate a user-click on an arbitrary element is intentional, and for obvious reasons.
Related
JSFiddle here - uses console.log().
window.evtqueue = [];
window.eventHold = function(e){
console.log(e.held);
if (typeof(e.held)==typeof(undefined)){
e.held = 1;
window.evtqueue.push(e);
console.log(e.type+" - "+e.which);
window.setTimeout(function(){
console.log("Triggering: "+e.type+" = "+e.which);
var evt = window.evtqueue.splice(0,1)[0];
$('#edittext').trigger(evt);
}, 1000);
return false;
} else {
console.log("Event actually triggered!");
}
}
$('#edittext').on('keydown keyup', window.eventHold)
I'm making a rich text editor, and was having some issues with quick keystrokes vs. asynchronous code (which is out of my control), so, I decided to make an event queue. However, I'm having trouble getting saved events to fire with .trigger(), and when I can manage that, they don't seem to fire their defaults.
What am I missing? Does this require more events to be bound to actually trigger the defaults, like keypress? Is it failing because the original bound event thing returns false, despite this being asynchronous?
Yes, it's because you returned false before you trigger the event
If you return false, the
event.isPropagationStopped() and event.isDefaultPrevented() is true
The source code show, if both of them is true, the trigger method will do nothing
I'm the using JWPlayer. After setup the player I need to add listeners to some events, to give an example I listen to events.JWPLAYER_MEDIA_TIME like so:
jwplayer('video-container').onTime(this.onTimeHandler);
After a while I need to remove this event listener, reading the documentation I couldn't find any solution.
Looking at the code, it doesn't seem possible to remove an event listener: a callback is pushed onto an array when you call onTime (or any of the other methods to setup event handlers), so calling it a second time doesn't overwrite a previous listener but just adds a new listener to the array.
Perhaps an alternative could be to set a flag once your listener doesn't have to perform its task anymore:
onTimeHandler : function() {
if (! this.handleOnTimeEvents)
return;
...
}
Here is how I handled it.
create a pseudo function whose sole purpose is to be a pointer. I was concerned with the onComplete event, so I wrote the code like so below:
function createJWPlayer(surl, stitle, autos, pw, ph) {
jwplayer("videocontainer").setup({
file: surl,
title: stitle,
width: pw,
height: ph,
autostart: autos,
stretching: "uniform",
skin: "/Scripts/JWPlayer/six.xml"
});
jwplayer().onComplete(function (e) {
jwcompleteevent(e);
});
}
function jwcompleteevent(e) {
// method to remain empty, sole purpose is to provide a pointer for the handler
}
Then in the function where I created it, I wrote this:
var mcomplete = (selobj.HasQ == false) ? InterActNoTimeAutoS : jwpCompleteInterA;
createJWPlayer(selobj.Upath, ti.TestTitle, true, "100%", "100%");
jwcompleteevent = mcomplete;
If I needed to load another video, I would do this
mcomplete = (selobj.HasQ == false) ? InterActNoTimeAutoS : jwpCompleteInterA;
jwcompleteevent = mcomplete;
loadJwPlayerUrl(selobj.Upath, true);
If anyone sees a problem with this, please tell me, it seems to be working as needed in the development environment
I am writing a JS which is used as a plugin. The JS has an onbeforeunload event.
I want suggestions so that my onbeforeunload event doesn't override the existing onbeforeunload event (if any). Can I append my onbeforeunload to the existing one?
Thanks.
I felt this has not been answered completely, because no examples were shown using addEventListener (but The MAZZTer pointed out the addEventListener solution though). My solution is the same as Julian D. but without using jQuery, only native javascript.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Before Unload</title>
</head>
<body>
<p>Test</p>
<script>
window.addEventListener('beforeunload', function (event) {
console.log('handler 1')
event.preventDefault()
event.returnValue = ''
});
window.addEventListener('beforeunload', function (event) {
console.log('handler 2')
});
</script>
</body>
</html>
In this example, both listeners will be executed. If any other beforeunload listeners were set, it would not override them. We would get the following output (order is not guaranteed):
handler 1
handler 2
And, importantly, if one or more of the event listener does event.preventDefault(); event.returnValue = '', a prompt asking the user if he really wants to reload will occur.
This can be useful if you are editing a form and at the same time you are downloading a file via ajax and do not want to lose data on any of these action. Each of these could have a listener to prevent page reload.
const editingForm = function (event) {
console.log('I am preventing losing form data')
event.preventDefault()
event.returnValue = ''
}
const preventDownload = function (event) {
console.log('I am preventing a download')
event.preventDefault()
event.returnValue = ''
}
// Add listener when the download starts
window.addEventListener('beforeunload', preventDownload);
// Add listener when the form is being edited
window.addEventListener('beforeunload', editingForm);
// Remove listener when the download ends
window.removeEventListener('beforeunload', preventDownload);
// Remove listener when the form editing ends
window.removeEventListener('beforeunload', editingForm);
You only need to take care of this if you are not using event observing but attach your onbeforeunload handler directly (which you should not). If so, use something like this to avoid overwriting of existing handlers.
(function() {
var existingHandler = window.onbeforeunload;
window.onbeforeunload = function(event) {
if (existingHandler) existingHandler(event);
// your own handler code here
}
})();
Unfortunately, you can't prevent other (later) scripts to overwrite your handler. But again, this can be solved by adding an event listener instead:
$(window).unload(function(event) {
// your handler code here
});
My idea:
var callbacks = [];
window.onbeforeunload = function() {
while (callbacks.length) {
var cb = callbacks.shift();
typeof(cb)==="function" && cb();
}
}
and
callbacks.push(function() {
console.log("callback");
});
Try this:
var f = window.onbeforeunload;
window.onbeforeunload = function () {
f();
/* New code or functions */
}
You can modify this function many times , without losing other functions.
If you bind using jQuery, it will append the binding to the existing list, so there is no need to worry.
From the jQuery Docs on() method:
As of jQuery 1.4, the same event handler can be bound to an element
multiple times.
function greet(event) { alert("Hello "+event.data.name); }
$("button").on("beforeunload", { name: "Karl" }, greet);
$("button").on("beforeunload", { name: "Addy" }, greet);
You can use different javascript frameworks like jquery or you could probably add a small event add handler to do this. Like you have an object thatcontains a number of functions that you have added and then in the onbefore unload you run the added functions. So when you want to add a new function to the event you add it to your object instead.
something like this:
var unloadMethods = [];
function addOnBeforeUnloadEvent(newEvent) { //new Event is a function
unloadMethods[unloadMethods.length] = newEvent;
}
window.onbeforeunload = function() {
for (var i=0; i<unloadMethods.length; i++) {
if(typeof unloadMethods[i] === "function") {unloadMethods[i]();}
}
}
Those frameworks mentioned use addEventListener internally. If you are not using a framework, use that.
https://developer.mozilla.org/en-US/docs/DOM/element.addEventListener
For older versions of IE you should have a fallback to use attachEvent instead:
http://msdn.microsoft.com/en-us/library/ie/ms536343(v=vs.85).aspx
I liked Marius's solution, but embellished on it to cater for situations where the var f is null, and to return the first string returned by any function in the chain:
function eventBeforeUnload(nextFN){
//some browsers do not support methods in eventAdd above to handle window.onbeforeunload
//so this is a way of attaching more than one event listener by chaining the functions together
//The onbeforeunload expects a string as a return, and will pop its own dialog - this is browser behavior that can't
//be overridden to prevent sites stopping you from leaving. Some browsers ignore this text and show their own message.
var firstFN = window.onbeforeunload;
window.onbeforeunload = function () {
var x;
if (firstFN) {
//see if the first function returns something
x = firstFN();
//if it does, return that
if (x) return x;
}
//return whatever is returned from the next function in the chain
return nextFN();
}
}
In your code where required use it as such
eventBeforeUnload(myFunction);
//or
eventBeforeUnload(function(){if(whatever) return 'unsaved data';);
What would be the best way to implement a mouseenter/mouseleave like event in Javascript without jQuery? What's the best strategy for cross browser use? I'm thinking some kind of checking on the event.relatedTarget/event.toElement property in the mouseover/mouseout event handlers?
Like to hear your thoughts.
(Totally changed my terrible answer. Let's try again.)
Let's assume you have the following base, cross-browser event methods:
var addEvent = window.addEventListener ? function (elem, type, method) {
elem.addEventListener(type, method, false);
} : function (elem, type, method) {
elem.attachEvent('on' + type, method);
};
var removeEvent = window.removeEventListener ? function (elem, type, method) {
elem.removeEventListener(type, method, false);
} : function (elem, type, method) {
elem.detachEvent('on' + type, method);
};
(Pretty simple, I know.)
Whenever you implement mouseenter/mouseleave, you just attach events to the
normal mouseover/mouseout events, but then check for two important particulars:
The event's target is the right element (or a child of the right element)
The event's relatedTarget is not a child of the target
So we also need a function that checks whether one element is a child of
another:
function contains(container, maybe) {
return container.contains ? container.contains(maybe) :
!!(container.compareDocumentPosition(maybe) & 16);
}
The last "gotcha" is how we would remove the event listener. The quickest way
to implement it is by just returning the new function that we're adding.
So we end up with something like this:
function mouseEnterLeave(elem, type, method) {
var mouseEnter = type === 'mouseenter',
ie = mouseEnter ? 'fromElement' : 'toElement',
method2 = function (e) {
e = e || window.event;
var target = e.target || e.srcElement,
related = e.relatedTarget || e[ie];
if ((elem === target || contains(elem, target)) &&
!contains(elem, related)) {
method();
}
};
type = mouseEnter ? 'mouseover' : 'mouseout';
addEvent(elem, type, method2);
return method2;
}
Adding a mouseenter event would look like this:
var div = document.getElementById('someID'),
listener = function () {
alert('do whatever');
};
mouseEnterLeave(div, 'mouseenter', listener);
In order to remove the event, you'd have to do something like this:
var newListener = mouseEnterLeave(div, 'mouseenter', listener);
// removing...
removeEvent(div, 'mouseover', newListener);
It's hardly ideal, but all that's left is just implementation details. The
important part was the if clause: mouseenter/mouseleave is just
mouseover/mouseout, but checking if you're targeting the right element, and if
the related target is a child of the target.
The best way, imho, is to craft your own event system.
Dean Edwards wrote one some years ago that I've taken cues from in the past. His solution does work out of the box however.
http://dean.edwards.name/weblog/2005/10/add-event/
John Resig submitted his entry to a contest, in which his was judged the best (Note: Dean Edwards was one of the jury). So, I would say, check this one out too.
Also its doesn't hurt to go thru jQuery, DOJO source once in a while, to actually see the best practices they r using to make it work cross-browser.
another option is to distinguish true mouseout events from fake (child-generated) events using hit-testing. Like so:
elt['onmouseout']=function(evt){
if (!mouse_inside_bounding_box(evt,elt)) console.debug('synthetic mouseleave');
}
I've used something like this on chrome and, caveat emptor, it seemed to do the trick. Once you have a reliable mouseleave event mouseenter is trivial.
I am trying to simulate keypresses in a web application, it is for an embedded system but it uses a Webkit derived browser. I have tested the code in Chrome and get the same error.
I tried to use code snippets from this example from Yahoo, but I keep getting the same error when firing the event using dispatchEvent. "target" is an HTML element in the DOM tree.
function fireEvent(target) {
var evt = document.createEvent("UIEvent");
evt.initEvent("keypress", true, true);
target.dispatchEvent(evt);
}
It always throws:
"Error: UNSPECIFIED_EVENT_TYPE_ERR: DOM Events Exception 0"
I have tried createEvent("Events") as well and it always boils down to the same exception, both on the embedded system and in Chrome.
Ok, when doing further testing, it seemed that when all key event parameters was properly initialised, then dispatchEvent worked without fireing an exception.
The following code works.
function fireEvent(target) {
var evt = document.createEvent("Events");
evt.initEvent("keypress", true, true);
evt.view = window;
evt.altKey = false;
evt.ctrlKey = false;
evt.shiftKey = false;
evt.metaKey = false;
evt.keyCode = 0;
evt.charCode = 'a';
target.dispatchEvent(evt);
}
Keypress is an UIEvent. You should use initUIEvent( 'type', bubbles, cancelable, windowObject, detail ) rather than initEvent(). But for firefox, which implements a keyEvents, you should create a KeyEvents and initKeyEvents().
This one is old thread, just to update it I am adding another answer so that it makes more sense to any one.
initEvent() is deprecated It is still supported in some browsers but avoid using it.
There is better concise way to create events like this
function fireEvent(target) {
var event = new Event('build');
// Listen for the event.
target.addEventListener('build', function (e) { ... }, false);
// Dispatch the event.
target.dispatchEvent(event);
}
To add more data to the event object, the CustomEvent interface exists and the detail property can be used to pass custom data.
For example, the event could be created as follows:
var event = new CustomEvent('build', { 'detail': target.dataset.time });
Reference: Creating and Triggering Events