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
Related
I'm trying set easyautocomplete on my input. Dataset i am geting from ajax json and there is some delay. If user is too fast and writes for example "Adam" and pushes tab, cursor skips to next input, but after easyautocomplete shows dialog on previous input and doesn´t hide it. Is there any way how to show easyautocomplete dialog only when i have cursor in input?
var options = {
minCharNumber: 3,
url: function(phrase) {
return "data?q=" + phrase;
},
getValue: function(element) {
return element.surname + " " + element.name;
},
template: {
type: "description",
fields: {
description: "phone"
}
},
ajaxSettings: {
dataType: "json",
method: "POST",
data: {
dataType: "json"
}
},
list: {
onClickEvent: function() {
/*Some action*/
},
hideAnimation: {
type: "slide", //normal|slide|fade
time: 400,
callback: function() {}
}
},
requestDelay: 400
};
$(".autoComplete").easyAutocomplete(options);
Minimum, Complete, Verifiable, Example
In order to easily see this result, you'll have to open up your dev tools and throttle your network traffic so the ajax connection takes a little while
Here's a Demo of the issue in jsFiddle
Handling Library Events (Doesn't Work)
My initial thought was you could handle this during some of the EAC lifecycle events that fired, like the onLoadEvent or onShowListEvent:
var options = {
url: "https://raw.githubusercontent.com/KyleMit/libraries/gh-pages/libraries/people.json",
getValue: "name",
list: {
match: {
enabled: true
},
onLoadEvent: function() {
console.log('LoadEvent', this)
},
onShowListEvent: function() {
console.log('ShowListEvent', this)
}
},
};
However, these methods don't seem to provide an option to alter the control flow and prevent future events
Updating Source Code (Works)
Peeking into the library's source code, Easy AutoComplete does the following ...
Handles keyup events
Which then calls loadData
Which fires an AJAX request with the provided URL
depending on the network and server speed, any amount of time can pass before step 4, and the input could lose focus
Once the ajax promise is returned, will call showContainer()
Which triggers the "show.eac" event on the container
Which then opens the list with the selected animation
During step 6, we could add one last check to confirm the selected input still has focus before actually opening, which would look like this:
$elements_container.on("show.eac", function() {
// if input has lost focus, don't show container
if (!$field.is(":focus")) {return}
// ... rest of method ...
Here's a working demo in Plunker which modifies the library's source code in a new file
Unfortunately, that's not a great practice as it leaves you fragile to future changes and transfers ownership of the lib maintenance to your codebase. But it does work.
I created Pull Request #388 with the proposed changes, so hopefully a long term fix within the library itself will be available at some point in the future.
Wrapper (Recommended for now)
If you don't want to muck with third party code, there are some weird workarounds to mess with the internal execution. We can't modify the showContainer method since it's inside a closure.
We can add another listener on the show.eac event so we can be a part of the event pipeline, however there are some challenges here too. Ideally, we'd like to fire before the library handler is executed and conditionally stop propagation. However, initializing EAC will both a) create the container we have to listen to and also b) attach an event listener.
Note: Event handlers in jQuery are fired in the order they are attached!
So, we have to wait until after the lib loads to attach our handler, but that means we'll only fire after the list is already displayed.
From the question How to order events bound with jQuery, we can poke into the jQuery internals and re-order attached events so we can fire our handler before the library's handler is called. That'll look like this:
$._data(element, 'events')["show"].reverse()
Note: Both e.stopPropagation() and e.preventDefault() won't work since they prevent future events; instead we need to take more immediate action with e.stopImmediatePropagation()
So the wrapper would look like this:
$("#countries").easyAutocomplete(options);
$(".easy-autocomplete-container").on("show.eac", function(e) {
var inputId = this.id.replace('eac-container-','')
var isFocused = $("#"+inputId).is(":focus")
if (!isFocused ) {
e.stopImmediatePropagation()
}
});
$(".easy-autocomplete-container").each(function() {
$._data(this, 'events')["show"].reverse()
})
Here's a working demo in CodePen
All of this is happening for IE8.
Due to script import orders, I'm having a bit of code being executed before JQuery is loaded where I need to fire a custom event.
This event will be picked up later in another bit of code when I'm sure JQuery will have been loaded. So I'd like to use JQuery to pick up this event.
I saw this previously asked question: How to trigger a custom javascript event in IE8? and applied the answer, which worked, but when I'm trying to pick up the Event via JQuery, then nothing happens.
Here's what I've tried:
function Event() {}
Event.listen = function(eventName, callback) {
if (document.addEventListener) {
document.addEventListener(eventName, callback, false);
} else {
document.documentElement.attachEvent('onpropertychange', function(e) {
if (e.propertyName == eventName) {
callback();
}
});
}
};
Event.trigger = function(eventName) {
if (document.createEvent) {
var event = document.createEvent('Event');
event.initEvent(eventName, true, true);
document.dispatchEvent(event);
} else {
document.documentElement[eventName] ++;
}
};
Event.listen('myevent', function() {
document.getElementById('mydiv-jquery').innerText = "myevent jquery";
});
$(document).on('myevent', function() {
document.getElementById('mydiv-vanilla').innerText = "myevent vanilla";
});
Event.trigger('myevent');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="mydiv-jquery">Nothing</div>
<div id="mydiv-vanilla">Nothing</div>
PS: The snippet doesn't seem to work properly in IE. Here's a jsfiddle that should be working.
There are a few problems with this code.
You shadow the built-in window.Event without checking if it exists; this could cause problems for other scripts.
You don't preserve the this binding when calling the callback from your onpropertychange listener. You should apply the callback to the document rather than calling it directly so the behavior will be as close as possible to addEventListener.
You attempt to increment document.documentElement[eventName] while it is undefined. The first call will change the value to NaN, so onpropertychange should pick it up, but on subsequent calls it will remain NaN.
You make no attempt to have .on() recognize your Event.listen function, so naturally the code in Event.listen will never be executed from a listener attached with .on().
Have you tried using Andrea Giammarchi's CustomEvent shim?
Is there a way to allow other bound events to the same object(ex. textbox) to fire/trigger first?
Say 2 events got bound to the same textbox. Both keyup events. In my case there is a plugin binding its own events, but the way the code is written, mine get bound first. I do not want mine to fire first.
$("#firstname").keyup(function() {
// ...is there anyway to allow the other keyup event to fire first, from here?
// do my work here...
}
$("#firstname").keyup(function() {
// the plugin work.
}
I need to use keyup, there is already key down events.
You should really rewrite your code to only have one keyup binding to that event but if that isn't feasible, you could do it dirty with semaphores and separate your functionality from the binding so it can be called from either bind...
var semaphore = 0; // on init
$("#firstname").keyup(function () { // this one should run first
semaphore++;
if (semaphore === 0) {
first_action();
}
}
$("#firstname").keyup(function () { // this one should run second
if (semaphore > 1) { // you know the first event fired
second_action();
}
else if (semaphore < 1) {
first_action();
second_action();
semaphore++;
}
}
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
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.