I have a hidden input in my HTML code and i want to know when the input value has changed.
<input type="hidden" id="myInputHidden" />
I can make something like this:
$('#myInputHidden').on('change', function() {
alert('triggered');
});
In the first place, this doesn't work and in many posts i have read that i must trigger manually the event.
The problem is that i don't know when (and where) the input value is changed so i cannot trigger that event.
The only way to implement a change event to a hidden field is by dirty-checking, e.g:
(function() {
var myHidden = document.getElementById('myInputHidden'),
currentValue = myHidden.value;
setTimeout(function myHiddenOnChange() {
if (myHidden.value !== currentValue) {
currentValue = myHidden.value;
myHiddenChanged.call(myHidden);
}
setTimeout(myHiddenOnChange, 30);
}, 30);
function myHiddenChanged() {
// that's your hidden field's 'change' event
}
})();
I don't recommend it, but another approach is to override the HTMLInputElement.prototype descriptor:
(function() {
var _htmlInputElementValue = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value'),
_htmlInputElementValueSet = _htmlInputElementValue.set,
_ev = document.createEvent('Event');
_ev.initEvent('change', true, true);
_htmlInputElementValue.set = function() {
_htmlInputElementValueSet.apply(this, [].slice.call(arguments));
if (this.type === 'hidden') {
this.dispatchEvent(_ev);
}
}
Object.defineProperty(HTMLInputElement.prototype, 'value', _htmlInputElementValue);
})();
Doing that, anytime someone changes the value property of a hidden field, it triggers the change event, so, if you're listening to that event, your code will start working.
In the following code I have "input" event handler that changes the value of the input as the user types. I also have "change" event handler to keep track of any changes made to this field. For some reason "change" event doesn't fire in Chrome and IE when the user leaves the field. Why is that and how to make it work in all major browsers?
Also note that it's not acceptable to trigger "change" event manually every time "input" event fires.
EDIT: "change" event seems to fire in Chrome only if transform function does not change the resulting string in any way. So if I type lower case letters and focus out after every character, "change" fires only for indexes 0, 2, 4 ...
Link to fiddle: http://jsfiddle.net/3hqxx2pr/
function transform(s) {
var r = "";
for (var i = 0; i < s.length; i++) {
r += i & 1 ? s[i].toUpperCase() : s[i];
}
return r;
}
$(document).ready(function(){
$("#in").on("change", function() {
console.log("changed"); // works only in ff
});
$("#in").on("input propertychange", function() {
$("#in").val(transform($("#in").val()));
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<input id="in"/>
Trigger change yourself
$("#in").val(transform($("#in").val())).change();
or
$("#in").val(transform($("#in").val())).trigger("change");
Based on a edit and restriction you made:
$("#in").on("input propertychange", function() {
var inp = $(this);
var orgVal = inp.val();
var transVal = transform(orgVal);
if (orgVal !== transVal) {
inp.val(transVal).trigger("change");
}
});
I'd like to know if an input element has changed, I learned that I can listen to onpropertychange in IE and oninput in other browsers.
Here is my code:
var _addChangedProperty = function(input){
input.changed = false;
var oninput = function(){
this.changed = !!this.value;
if(this.changed){
this.style.color = "black";
}
};
input.onpropertychange = input.oninput = oninput;
};
Now I'd like to change input.onpropertychange = input.oninput = oninput; to addEventListerner and attachEvent, I need to check if onpropertychange event is supported, how could I do this (without browser detect)?
You can check using the in operator:
"onpropertychange" in input
This kind of feature test doesn't work in older versions of Firefox, which report false event for event handler properties corresponding to events that do exist, but that isn't a problem here because Firefox doesn't currently support the propertychange event and is unlikely to in the future.
Here's some background: http://perfectionkills.com/detecting-event-support-without-browser-sniffing/
One other point: you need separate functions to handle the propertychange and input events, because in the propertychange handler you need to check whether it is the value property that has changed. Otherwise, you'll end up handling changes to any property of the input.
input.onpropertychange = function(evt) {
evt = evt || window.event;
if (evt.propertyName == "value") {
// Do stuff here
}
};
First of all, here is a list of event types that are defined by the W3C standards. (This list is based on the onevent attributes defined in the HTML5 standard. I assume that there are dozens of other event types, but this list is long enough as it is.)
abort
afterprint
beforeprint
beforeunload
blur
canplay
canplaythrough
change
click
contextmenu
copy
cuechange
cut
dblclick
DOMContentLoaded
drag
dragend
dragenter
dragleave
dragover
dragstart
drop
durationchange
emptied
ended
error
focus
focusin
focusout
formchange
forminput
hashchange
input
invalid
keydown
keypress
keyup
load
loadeddata
loadedmetadata
loadstart
message
mousedown
mouseenter
mouseleave
mousemove
mouseout
mouseover
mouseup
mousewheel
offline
online
pagehide
pageshow
paste
pause
play
playing
popstate
progress
ratechange
readystatechange
redo
reset
resize
scroll
seeked
seeking
select
show
stalled
storage
submit
suspend
timeupdate
undo
unload
volumechange
waiting
Now, is it possible to define a global event handler that is called when any event originally occurs on any element on the page? (In this case, I don't want to count those events that occurred on elements because they bubbled up from a descendant element - that's why I wrote "originally occurs".)
If that is not possible, is it at least possible to define an event handler that is called when any event bubbles up to the root of the DOM tree (which is either the document object or the window object - both should work)? (I know that it's possible to stop bubbling programmatically, but I would use this event handler on a page that has no other handlers defined on any other elements.) (Also, I believe some events don't bubble up, but let's ignore these cases for the sake of this argument.)
I know that I can do this (using jQuery):
$(document).bind('abort afterprint beforeprint beforeunload etc.', function() {
// handle event
});
but that would be a rather undesirable solution for me.
btw I don't need a cross-browser solution. If it works in just one browser, I'm fine.
Also, Firebug is able to log events, but I would like to be able to catch the event programmatically (via JavaScript) rather then having them simply logged in the console.
/*
function getAllEventTypes(){
if(location.href !='https://developer.mozilla.org/en-US/docs/Web/Events') return;
var types = {};
$('.standard-table:eq(0) tr').find('td:eq(1)').map(function(){
var type = $.trim(this.innerText) || 'OtherEvent';
types[type] = types[type] || [];
var event = $.trim(this.previousElementSibling.innerText);
if(event) types[type].push(event);
});
for(var t in types) types[t] = types[t].join(' ');
return "var DOMEvents = "+JSON.stringify(types, null, 4).replace(/"(\w+)\":/ig, '$1:');
}
*/
var DOMEvents = {
UIEvent: "abort DOMActivate error load resize scroll select unload",
ProgressEvent: "abort error load loadend loadstart progress progress timeout",
Event: "abort afterprint beforeprint cached canplay canplaythrough change chargingchange chargingtimechange checking close dischargingtimechange DOMContentLoaded downloading durationchange emptied ended ended error error error error fullscreenchange fullscreenerror input invalid languagechange levelchange loadeddata loadedmetadata noupdate obsolete offline online open open orientationchange pause pointerlockchange pointerlockerror play playing ratechange readystatechange reset seeked seeking stalled submit success suspend timeupdate updateready visibilitychange volumechange waiting",
AnimationEvent: "animationend animationiteration animationstart",
AudioProcessingEvent: "audioprocess",
BeforeUnloadEvent: "beforeunload",
TimeEvent: "beginEvent endEvent repeatEvent",
OtherEvent: "blocked complete upgradeneeded versionchange",
FocusEvent: "blur DOMFocusIn Unimplemented DOMFocusOut Unimplemented focus focusin focusout",
MouseEvent: "click contextmenu dblclick mousedown mouseenter mouseleave mousemove mouseout mouseover mouseup show",
SensorEvent: "compassneedscalibration Unimplemented userproximity",
OfflineAudioCompletionEvent: "complete",
CompositionEvent: "compositionend compositionstart compositionupdate",
ClipboardEvent: "copy cut paste",
DeviceLightEvent: "devicelight",
DeviceMotionEvent: "devicemotion",
DeviceOrientationEvent: "deviceorientation",
DeviceProximityEvent: "deviceproximity",
MutationNameEvent: "DOMAttributeNameChanged DOMElementNameChanged",
MutationEvent: "DOMAttrModified DOMCharacterDataModified DOMNodeInserted DOMNodeInsertedIntoDocument DOMNodeRemoved DOMNodeRemovedFromDocument DOMSubtreeModified",
DragEvent: "drag dragend dragenter dragleave dragover dragstart drop",
GamepadEvent: "gamepadconnected gamepaddisconnected",
HashChangeEvent: "hashchange",
KeyboardEvent: "keydown keypress keyup",
MessageEvent: "message message message message",
PageTransitionEvent: "pagehide pageshow",
PopStateEvent: "popstate",
StorageEvent: "storage",
SVGEvent: "SVGAbort SVGError SVGLoad SVGResize SVGScroll SVGUnload",
SVGZoomEvent: "SVGZoom",
TouchEvent: "touchcancel touchend touchenter touchleave touchmove touchstart",
TransitionEvent: "transitionend",
WheelEvent: "wheel"
}
var RecentlyLoggedDOMEventTypes = {};
for (var DOMEvent in DOMEvents) {
var DOMEventTypes = DOMEvents[DOMEvent].split(' ');
DOMEventTypes.filter(function(DOMEventType) {
var DOMEventCategory = DOMEvent + ' ' + DOMEventType;
document.addEventListener(DOMEventType, function(e){
if(RecentlyLoggedDOMEventTypes[DOMEventCategory]) { return; }
RecentlyLoggedDOMEventTypes[DOMEventCategory] = true;
setTimeout(function(){ RecentlyLoggedDOMEventTypes[DOMEventCategory] = false }, 5000);
var isActive = e.target == document.activeElement;
if(isActive) {
console.info(DOMEventCategory,
' target=', e.target,
' active=', document.activeElement,
' isActive=', true );
} else {
console.log(DOMEventCategory,
' target=', e.target,
' active=', document.activeElement,
' isActive=', false );
}
}, true);
});
}
You can iterate through all properties of dom element and select ones that match /on(.*)/ pattern (for example onclick or onmousemove):
var events = [];
for (var property in element) {
var match = property.match(/^on(.*)/)
if (match) {
events.push(match[1]);
}
}
console.log(events.join(' '))
I highly doubt there's a way to do this in Firefox. Looking at Firebug's source code (particularly the attachAllListeners method), turns out that iterating through a list of event names is obviously the way to go, but this doesn't solve the bubbling issues.
There doesn't seem to be any 'easy-way' to do that.
My idea:
You know which are all the events, so you can handle all events for every DOM element:
var events =
[
"onabort",
"onafterprint",
"onbeforeprint",
"onbeforeunload",
...
];
var root = document.body;
var elms = root.childNodes;
for(var i = 0; i < elms.length; i++)
{
for(var j = 0; j < events.length; j++)
{
elms[i][events[j]] = globalHandler;
}
}
function globalHandler()
{
alert("Global handler called");
}
That's the 'intuitive idea' but doesn't seem to be very efficient. However, it should work.
Good luck.
How to listen for all events on a specific target Element 👾
For all native events, we can retrieve a list of supported events by iterating over the target.onevent properties and installing our listener for all of them.
for (const key in target) {
if(/^on/.test(key)) {
const eventType = key.substr(2);
target.addEventListener(eventType, listener);
}
}
The only other way that events are emitted which I know of is via EventTarget.dispatchEvent, which every Node and thefore every Element inherits.
To listen for all these manually triggered events, we can proxy the dispatchEvent method globally and install our listener just-in-time for the event whose name we just saw ✨ ^^
const dispatchEvent_original = EventTarget.prototype.dispatchEvent;
EventTarget.prototype.dispatchEvent = function (event) {
if (!alreadyListenedEventTypes.has(event.type)) {
target.addEventListener(event.type, listener, ...otherArguments);
alreadyListenedEventTypes.add(event.type);
}
dispatchEvent_original.apply(this, arguments);
};
🔥 function snippet 🔥
function addEventListenerAll(target, listener, ...otherArguments) {
// install listeners for all natively triggered events
for (const key in target) {
if (/^on/.test(key)) {
const eventType = key.substr(2);
target.addEventListener(eventType, listener, ...otherArguments);
}
}
// dynamically install listeners for all manually triggered events, just-in-time before they're dispatched ;D
const dispatchEvent_original = EventTarget.prototype.dispatchEvent;
function dispatchEvent(event) {
target.addEventListener(event.type, listener, ...otherArguments); // multiple identical listeners are automatically discarded
dispatchEvent_original.apply(this, arguments);
}
EventTarget.prototype.dispatchEvent = dispatchEvent;
if (EventTarget.prototype.dispatchEvent !== dispatchEvent) throw new Error(`Browser is smarter than you think!`);
}
// usage example
addEventListenerAll(window, (evt) => {
console.log(evt.type);
});
document.body.click();
document.body.dispatchEvent(new Event('omg!', { bubbles: true }));
// usage example with `useCapture`
// (also receives `bubbles: false` events, but in reverse order)
addEventListenerAll(
window,
(evt) => { console.log(evt.type); },
true
);
document.body.dispatchEvent(new Event('omfggg!', { bubbles: false }));
A bit late to the party but I did create something that might be useful for others here.
https://codepen.io/phreaknation/pen/QmJjEa
This is an ES6 Class that captures all events from an element that is known to that element. This demo allows you to change the element time in the page, as well as read out the events with clickable links to their MDN page as well as interact with the element and see how the events are triggered with time stamps.
I hope this helps
Class code
class EventSystem {
constructor(element) {
this._ = {
element: null
}
return this;
}
getAllEventTypes({blacklist = [], whitelist = []} = {}) {
const events = [];
for (let property in this._.element) {
const match = property.match(/^on(.*)/);
if (match) {
if ((whitelist.length > 0 ? whitelist.indexOf(match) !== -1 : true) &&
(blacklist.length > 0 ? blacklist.indexOf(match) === -1 : true)) {
events.push(match[1]);
}
}
}
return events;
}
getElementType() {
return this._.element.tagName.toLowerCase();
}
setElement(element) {
this._.element = element;
return this;
}
applyEvents(events, callback) {
events.forEach((event) => {
this._.element.addEventListener(event, (ev) => {
if (typeof callback === 'function') {
callback(event, ev);
}
})
})
}
}
My solution to this problem. I loop through all datatypes on the global context (window, in this case), check if the type extends EventTarget, and then extracts them via checking for the "on" prefix.
const getEventNames = (root) => {
let events = [ ];
const objectHasSubPrototype = (object, comp) => {
let proto = Object.getPrototypeOf(object);
while(proto !== null && proto !== EventTarget) {
proto = Object.getPrototypeOf(proto);
}
return (proto !== null);
};
const addEventNames = (propNames) => {
propNames.filter(x => x.match(/^on\w+$/)).forEach((propName) => {
propName = propName.substr(2);
if(events.indexOf(propName) === -1) {
events.push(propName);
}
});
};
Object.getOwnPropertyNames(root).forEach((name) => {
let value = root[name];
if(value) {
if(objectHasSubPrototype(value, EventTarget)) {
let propNames = Object.getOwnPropertyNames(Object.getPrototypeOf(value).prototype);
addEventNames(propNames);
propNames = Object.getOwnPropertyNames(window);
addEventNames(propNames);
}
}
});
return events;
};
// Attach all events to the window
getEventNames(window).forEach((eventName) => {
window.addEventListener(eventName, (event) => console.log(eventName, event));
});
For the last version of the MDN website:
(function getAllEventTypes(){
if(location.href !='https://developer.mozilla.org/en-US/docs/Web/Events') return;
var types = {};
$('.standard-table').map(function(){
if($(this).find('caption').length > 0){
var type = $(this).find('caption')[0].innerHTML || 'OtherEvent';
types[type] = types[type] || [];
$(this).find('tbody tr td code a').each(function(el){
if(this.innerText) types[type].push(this.innerText);
});
}
});
for(var t in types) types[t] = types[t].join(' ');
return "var DOMEvents = "+JSON.stringify(types, null, 4).replace(/"(\w+)\":/ig, '$1:');
})();
What is the best way to simulate a user entering text in a text input box in JS and/or jQuery?
I don't want to actually put text in the input box, I just want to trigger all the event handlers that would normally get triggered by a user typing info into a input box. This means focus, keydown, keypress, keyup, and blur. I think.
So how would one accomplish this?
You can trigger any of the events with a direct call to them, like this:
$(function() {
$('item').keydown();
$('item').keypress();
$('item').keyup();
$('item').blur();
});
Does that do what you're trying to do?
You should probably also trigger .focus() and potentially .change()
If you want to trigger the key-events with specific keys, you can do so like this:
$(function() {
var e = $.Event('keypress');
e.which = 65; // Character 'A'
$('item').trigger(e);
});
There is some interesting discussion of the keypress events here: jQuery Event Keypress: Which key was pressed?, specifically regarding cross-browser compatability with the .which property.
You could dispatching events like
el.dispatchEvent(new Event('focus'));
el.dispatchEvent(new KeyboardEvent('keypress',{'key':'a'}));
To trigger an enter keypress, I had to modify #ebynum response, specifically, using the keyCode property.
e = $.Event('keyup');
e.keyCode= 13; // enter
$('input').trigger(e);
Here's a vanilla js example to trigger any event:
function triggerEvent(el, type){
if ('createEvent' in document) {
// modern browsers, IE9+
var e = document.createEvent('HTMLEvents');
e.initEvent(type, false, true);
el.dispatchEvent(e);
} else {
// IE 8
var e = document.createEventObject();
e.eventType = type;
el.fireEvent('on'+e.eventType, e);
}
}
You can achieve this with: EventTarget.dispatchEvent(event) and by passing in a new KeyboardEvent as the event.
For example: element.dispatchEvent(new KeyboardEvent('keypress', {'key': 'a'}))
Working example:
// get the element in question
const input = document.getElementsByTagName("input")[0];
// focus on the input element
input.focus();
// add event listeners to the input element
input.addEventListener('keypress', (event) => {
console.log("You have pressed key: ", event.key);
});
input.addEventListener('keydown', (event) => {
console.log(`key: ${event.key} has been pressed down`);
});
input.addEventListener('keyup', (event) => {
console.log(`key: ${event.key} has been released`);
});
// dispatch keyboard events
input.dispatchEvent(new KeyboardEvent('keypress', {'key':'h'}));
input.dispatchEvent(new KeyboardEvent('keydown', {'key':'e'}));
input.dispatchEvent(new KeyboardEvent('keyup', {'key':'y'}));
<input type="text" placeholder="foo" />
MDN dispatchEvent
MDN KeyboardEvent
You're now able to do:
var e = $.Event("keydown", {keyCode: 64});
First of all, I need to say that sample from Sionnach733 worked flawlessly. Some users complain about absent of actual examples. Here is my two cents. I've been working on mouse click simulation when using this site: https://www.youtube.com/tv. You can open any video and try run this code. It performs switch to next video.
function triggerEvent(el, type, keyCode) {
if ('createEvent' in document) {
// modern browsers, IE9+
var e = document.createEvent('HTMLEvents');
e.keyCode = keyCode;
e.initEvent(type, false, true);
el.dispatchEvent(e);
} else {
// IE 8
var e = document.createEventObject();
e.keyCode = keyCode;
e.eventType = type;
el.fireEvent('on'+e.eventType, e);
}
}
var nextButton = document.getElementsByClassName('icon-player-next')[0];
triggerEvent(nextButton, 'keyup', 13); // simulate mouse/enter key press
For typescript cast to KeyboardEventInit and provide the correct keyCode integer
const event = new KeyboardEvent("keydown", {
keyCode: 38,
} as KeyboardEventInit);
I thought I would draw your attention that in the specific context where a listener was defined within a jQuery plugin, then the only thing that successfully simulated the keypress event for me, eventually caught by that listener, was to use setTimeout().
e.g.
setTimeout(function() { $("#txtName").keypress() } , 1000);
Any use of $("#txtName").keypress() was ignored, although placed at the end of the .ready() function. No particular DOM supplement was being created asynchronously anyway.