Remove click event problems - javascript

I'm doing something obviously wrong but can't explain to myself.
The goal is to listen on a click event (START, first). If user clicks Start the second click listener should get ready but only fire if clicked on the particular element. If second click happens outside, the second click event listener should again be removed.
As simple as it sounds, here are my problems:
When clicking on "start" why is the document.body.addEventListener('click' firing?
How can I accomplish what I've explained?
var Blubb = function(element){
this.element = element;
document.addEventListener('make-ready', this.makeBlubbReady.bind(this), false);
};
Blubb.prototype.makeBlubbReady = function(){
var options = {one: true, two: false };
this.element.classList.remove('disabled');
this.element.addEventListener('click', (function(){this.go(options)}).bind(this), false);
document.body.addEventListener(
'click',
(function(event){
console.log('This shouldn\'t be ready before clicking "start"');
if(event.target == this.element) {
return;
}
this.element.removeEventListener('click', this.go)
}).bind(this),
false
);
};
Blubb.prototype.go = function(options){
console.log('Blubb go, options.one: ', options.one);
};
document.querySelector('.first').addEventListener('click', function(){
new Blubb(document.querySelector('.second'));
document.dispatchEvent(new CustomEvent('make-ready', {}));
}, false)
.second.disabled {
display: none;
}
<div class="first">START</div>
<br />
<div class="second disabled">BLUBB</div>
Next is pretty much the same as above. Just to explain the 1. problem more. Why does the console logs 3 when only setting up the event listener on the body? I expected to behave like setting up the event listener on this.element which also just waits to get a click event..
var Blubb = function(element){
this.element = element;
document.addEventListener('make-ready', this.makeBlubbReady.bind(this), false);
};
Blubb.prototype.makeBlubbReady = function(){
var options = {one: true, two: false };
this.element.classList.remove('disabled');
console.log('1');
this.element.addEventListener('click', (function(){
console.log('2');
this.go(options)
}).bind(this), false);
document.body.addEventListener('click', (function(){
console.log('3');
}).bind(this),
false
);
};
Blubb.prototype.go = function(options){
console.log('Blubb go, options.one: ', options.one);
};
document.querySelector('.first').addEventListener('click', function(){
new Blubb(document.querySelector('.second'));
document.dispatchEvent(new CustomEvent('make-ready', {}));
}, false)
.second.disabled {
display: none;
}
<div class="first">START</div>
<br />
<div class="second disabled">BLUBB</div>

The problem with your code is that you're using bind to bind a reference of the class to the listener, but bind would create a new reference of the listener each time it's used. Therefore you won't be able to remove the listener later on. To fix this you have to save the listener reference into a variable for later use. Also your binding the listener to the document therefore it gets executed instantly, to prevent this you could wrap the binding into a setTimeout or set the useCapture option to true.
Also you should consider making your class a singleton or destroy the previous instance because multiple instances would influence each other because of the listeners on the document.
Here's an example of a class using the techniques descripted above:
var Blubb = (function(doc) {
var defaults = {
one: true,
two: false
},
disabledClass = 'disabled',
instance = null;
function Blubb(element, options) {
if (instance) {
instance.destroy();
};
this.element = element;
this.options = Object.assign({}, defaults, options);
this.element.classList.remove(disabledClass);
this.removeListener = setupListener.call(this);
instance = this;
}
function setupListener() {
var listener = onClick.bind(this);
doc.addEventListener('click', listener, true);
return function() {
doc.removeEventListener('click', listener, true);
}
}
function onClick(event) {
if (event.target === this.element) {
this.go(this.options);
} else {
this.destroy();
}
}
Blubb.prototype.go = function(options) {
console.log('Blubb go, options: ', options);
}
Blubb.prototype.destroy = function() {
this.element.classList.add(disabledClass);
this.element = null;
instance = null;
this.removeListener();
}
return Blubb;
})(document);
// setup
document.querySelector('.first').addEventListener('click', function() {
new Blubb(document.querySelector('.second'));
}, false)
.second.disabled {
display: none;
}
.first,
.second {
background: #eee;
cursor: pointer;
}
<div class="first">START</div>
<br />
<div class="second disabled">BLUBB</div>
EDIT: You can also use bind the listener with useCapture instead of wrapping it into a setTimeout. I edited the snippet above.

Related

What is the JavaScript equivalent of JQuery's .change(); [duplicate]

I have attached an event to a text box using addEventListener. It works fine. My problem arose when I wanted to trigger the event programmatically from another function.
How can I do it?
Note: the initEvent method is now deprecated. Other answers feature up-to-date and recommended practice.
You can use fireEvent on IE 8 or lower, and W3C's dispatchEvent on most other browsers. To create the event you want to fire, you can use either createEvent or createEventObject depending on the browser.
Here is a self-explanatory piece of code (from prototype) that fires an event dataavailable on an element:
var event; // The custom event that will be created
if(document.createEvent){
event = document.createEvent("HTMLEvents");
event.initEvent("dataavailable", true, true);
event.eventName = "dataavailable";
element.dispatchEvent(event);
} else {
event = document.createEventObject();
event.eventName = "dataavailable";
event.eventType = "dataavailable";
element.fireEvent("on" + event.eventType, event);
}
A working example:
// Add an event listener
document.addEventListener("name-of-event", function(e) {
console.log(e.detail); // Prints "Example of an event"
});
// Create the event
var event = new CustomEvent("name-of-event", { "detail": "Example of an event" });
// Dispatch/Trigger/Fire the event
document.dispatchEvent(event);
For older browsers polyfill and more complex examples, see MDN docs.
See support tables for EventTarget.dispatchEvent and CustomEvent.
If you don't want to use jQuery and aren't especially concerned about backwards compatibility, just use:
let element = document.getElementById(id);
element.dispatchEvent(new Event("change")); // or whatever the event type might be
See the documentation here and here.
EDIT: Depending on your setup you might want to add bubbles: true:
let element = document.getElementById(id);
element.dispatchEvent(new Event('change', { 'bubbles': true }));
if you use jQuery, you can simple do
$('#yourElement').trigger('customEventName', [arg0, arg1, ..., argN]);
and handle it with
$('#yourElement').on('customEventName',
function (objectEvent, [arg0, arg1, ..., argN]){
alert ("customEventName");
});
where "[arg0, arg1, ..., argN]" means that these args are optional.
Note: the initCustomEvent method is now deprecated. Other answers feature up-to-date and recommended practice.
If you are supporting IE9+ the you can use the following. The same concept is incorporated in You Might Not Need jQuery.
function addEventListener(el, eventName, handler) {
if (el.addEventListener) {
el.addEventListener(eventName, handler);
} else {
el.attachEvent('on' + eventName, function() {
handler.call(el);
});
}
}
function triggerEvent(el, eventName, options) {
var event;
if (window.CustomEvent) {
event = new CustomEvent(eventName, options);
} else {
event = document.createEvent('CustomEvent');
event.initCustomEvent(eventName, true, true, options);
}
el.dispatchEvent(event);
}
// Add an event listener.
addEventListener(document, 'customChangeEvent', function(e) {
document.body.innerHTML = e.detail;
});
// Trigger the event.
triggerEvent(document, 'customChangeEvent', {
detail: 'Display on trigger...'
});
If you are already using jQuery, here is the jQuery version of the code above.
$(function() {
// Add an event listener.
$(document).on('customChangeEvent', function(e, opts) {
$('body').html(opts.detail);
});
// Trigger the event.
$(document).trigger('customChangeEvent', {
detail: 'Display on trigger...'
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
I searched for firing click, mousedown and mouseup event on mouseover using JavaScript. I found an answer provided by Juan Mendes. For the answer click here.
Click here is the live demo and below is the code:
function fireEvent(node, eventName) {
// Make sure we use the ownerDocument from the provided node to avoid cross-window problems
var doc;
if (node.ownerDocument) {
doc = node.ownerDocument;
} else if (node.nodeType == 9) {
// the node may be the document itself, nodeType 9 = DOCUMENT_NODE
doc = node;
} else {
throw new Error("Invalid node passed to fireEvent: " + node.id);
}
if (node.dispatchEvent) {
// Gecko-style approach (now the standard) takes more work
var eventClass = "";
// Different events have different event classes.
// If this switch statement can't map an eventName to an eventClass,
// the event firing is going to fail.
switch (eventName) {
case "click": // Dispatching of 'click' appears to not work correctly in Safari. Use 'mousedown' or 'mouseup' instead.
case "mousedown":
case "mouseup":
eventClass = "MouseEvents";
break;
case "focus":
case "change":
case "blur":
case "select":
eventClass = "HTMLEvents";
break;
default:
throw "fireEvent: Couldn't find an event class for event '" + eventName + "'.";
break;
}
var event = doc.createEvent(eventClass);
var bubbles = eventName == "change" ? false : true;
event.initEvent(eventName, bubbles, true); // All events created as bubbling and cancelable.
event.synthetic = true; // allow detection of synthetic events
// The second parameter says go ahead with the default action
node.dispatchEvent(event, true);
} else if (node.fireEvent) {
// IE-old school style
var event = doc.createEventObject();
event.synthetic = true; // allow detection of synthetic events
node.fireEvent("on" + eventName, event);
}
};
The accepted answer didn’t work for me, none of the createEvent ones did.
What worked for me in the end was:
targetElement.dispatchEvent(
new MouseEvent('click', {
bubbles: true,
cancelable: true,
view: window,
}));
Here’s a snippet:
const clickBtn = document.querySelector('.clickme');
const viaBtn = document.querySelector('.viame');
viaBtn.addEventListener('click', function(event) {
clickBtn.dispatchEvent(
new MouseEvent('click', {
bubbles: true,
cancelable: true,
view: window,
}));
});
clickBtn.addEventListener('click', function(event) {
console.warn(`I was accessed via the other button! A ${event.type} occurred!`);
});
<button class="clickme">Click me</button>
<button class="viame">Via me</button>
From reading:
https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
Modified #Dorian's answer to work with IE:
document.addEventListener("my_event", function(e) {
console.log(e.detail);
});
var detail = 'Event fired';
try {
// For modern browsers except IE:
var event = new CustomEvent('my_event', {detail:detail});
} catch(err) {
// If IE 11 (or 10 or 9...?) do it this way:
// Create the event.
var event = document.createEvent('Event');
// Define that the event name is 'build'.
event.initEvent('my_event', true, true);
event.detail = detail;
}
// Dispatch/Trigger/Fire the event
document.dispatchEvent(event);
FIDDLE: https://jsfiddle.net/z6zom9d0/1/
SEE ALSO:
https://caniuse.com/#feat=customevent
Just to suggest an alternative that does not involve the need to manually invoke a listener event:
Whatever your event listener does, move it into a function and call that function from the event listener.
Then, you can also call that function anywhere else that you need to accomplish the same thing that the event does when it fires.
I find this less "code intensive" and easier to read.
I just used the following (seems to be much simpler):
element.blur();
element.focus();
In this case the event is triggered only if value was really changed just as you would trigger it by normal focus locus lost performed by user.
function fireMouseEvent(obj, evtName) {
if (obj.dispatchEvent) {
//var event = new Event(evtName);
var event = document.createEvent("MouseEvents");
event.initMouseEvent(evtName, true, true, window,
0, 0, 0, 0, 0, false, false, false, false, 0, null);
obj.dispatchEvent(event);
} else if (obj.fireEvent) {
event = document.createEventObject();
event.button = 1;
obj.fireEvent("on" + evtName, event);
obj.fireEvent(evtName);
} else {
obj[evtName]();
}
}
var obj = document.getElementById("......");
fireMouseEvent(obj, "click");
You could use this function i compiled together.
if (!Element.prototype.trigger)
{
Element.prototype.trigger = function(event)
{
var ev;
try
{
if (this.dispatchEvent && CustomEvent)
{
ev = new CustomEvent(event, {detail : event + ' fired!'});
this.dispatchEvent(ev);
}
else
{
throw "CustomEvent Not supported";
}
}
catch(e)
{
if (document.createEvent)
{
ev = document.createEvent('HTMLEvents');
ev.initEvent(event, true, true);
this.dispatchEvent(event);
}
else
{
ev = document.createEventObject();
ev.eventType = event;
this.fireEvent('on'+event.eventType, event);
}
}
}
}
Trigger an event below:
var dest = document.querySelector('#mapbox-directions-destination-input');
dest.trigger('focus');
Watch Event:
dest.addEventListener('focus', function(e){
console.log(e);
});
Hope this helps!
You can use below code to fire event using Element method:
if (!Element.prototype.triggerEvent) {
Element.prototype.triggerEvent = function (eventName) {
var event;
if (document.createEvent) {
event = document.createEvent("HTMLEvents");
event.initEvent(eventName, true, true);
} else {
event = document.createEventObject();
event.eventType = eventName;
}
event.eventName = eventName;
if (document.createEvent) {
this.dispatchEvent(event);
} else {
this.fireEvent("on" + event.eventType, event);
}
};
}
if (!Element.prototype.triggerEvent) {
Element.prototype.triggerEvent = function (eventName) {
var event;
if (document.createEvent) {
event = document.createEvent("HTMLEvents");
event.initEvent(eventName, true, true);
} else {
event = document.createEventObject();
event.eventType = eventName;
}
event.eventName = eventName;
if (document.createEvent) {
this.dispatchEvent(event);
} else {
this.fireEvent("on" + event.eventType, event);
}
};
}
var input = document.getElementById("my_input");
var button = document.getElementById("my_button");
input.addEventListener('change', function (e) {
alert('change event fired');
});
button.addEventListener('click', function (e) {
input.value = "Bye World";
input.triggerEvent("change");
});
<input id="my_input" type="input" value="Hellow World">
<button id="my_button">Change Input</button>
What's worth noticing, is the fact that we can create, any kind of pre-defined events, and listen to it from anywhere.
We are not limited to classical built-in events.
In this base example, a custom event interfacebuiltsuccessuserdefinedevent is dispatched every 3 seconds, on the self.document
self.document.addEventListener('interfacebuiltsuccessuserdefinedevent', () => console.log("WOW"), false)
setInterval(() => { // Test
self.document.dispatchEvent(new Event('interfacebuiltsuccessuserdefinedevent'))
}, 3000 ) // Test
Interesting fact: elements can listen for events that haven't been created yet.
The most efficient way is to call the very same function that has been registered with addEventListener directly.
You can also trigger a fake event with CustomEvent and co.
Finally some elements such as <input type="file"> support a .click() method.
var btn = document.getElementById('btn-test');
var event = new Event(null);
event.initEvent('beforeinstallprompt', true, true);
btn.addEventListener('beforeinstallprompt', null, false);
btn.dispatchEvent(event);
this will imediattely trigger an event 'beforeinstallprompt'
HTML
myLink
<button onclick="fireLink(event)"> Call My Link </button>
JS
// click event listener of the link element --------------
document.getElementById('myLink').addEventListener("click", callLink);
function callLink(e) {
// code to fire
}
// function invoked by the button element ----------------
function fireLink(event) {
document.getElementById('myLink').click(); // script calls the "click" event of the link element
}
Use jquery event call.
Write the below line where you want to trigger onChange of any element.
$("#element_id").change();
element_id is the ID of the element whose onChange you want to trigger.
Avoid the use of
element.fireEvent("onchange");
Because it has very less support. Refer this document for its support.
What you want is something like this:
document.getElementByClassName("example").click();
Using jQuery, it would be something like this:
$(".example").trigger("click");

Apply multi-event do same function on same element [duplicate]

So my dilemma is that I don't want to write the same code twice. Once for the click event and another for the touchstart event.
Here is the original code:
document.getElementById('first').addEventListener('touchstart', function(event) {
do_something();
});
document.getElementById('first').addEventListener('click', function(event) {
do_something();
});
How can I compact this? There HAS to be a simpler way!
I thought some might find this approach useful; it could be applied to any similarly repetitive code:
ES6
['click','ontouchstart'].forEach( evt =>
element.addEventListener(evt, dosomething, false)
);
ES5
['click','ontouchstart'].forEach( function(evt) {
element.addEventListener(evt, dosomething, false);
});
You can just define a function and pass it. Anonymous functions are not special in any way, all functions can be passed around as values.
var elem = document.getElementById('first');
elem.addEventListener('touchstart', handler, false);
elem.addEventListener('click', handler, false);
function handler(event) {
do_something();
}
Maybe you can use a helper function like this:
// events and args should be of type Array
function addMultipleListeners(element,events,handler,useCapture,args){
if (!(events instanceof Array)){
throw 'addMultipleListeners: '+
'please supply an array of eventstrings '+
'(like ["click","mouseover"])';
}
//create a wrapper to be able to use additional arguments
var handlerFn = function(e){
handler.apply(this, args && args instanceof Array ? args : []);
}
for (var i=0;i<events.length;i+=1){
element.addEventListener(events[i],handlerFn,useCapture);
}
}
function handler(e) {
// do things
};
// usage
addMultipleListeners(
document.getElementById('first'),
['touchstart','click'],
handler,
false);
[Edit nov. 2020] This answer is pretty old. The way I solve this nowadays is by using an actions object where handlers are specified per event type, a data-attribute for an element to indicate which action should be executed on it and one generic document wide handler method (so event delegation).
const firstElemHandler = (elem, evt) =>
elem.textContent = `You ${evt.type === "click" ? "clicked" : "touched"}!`;
const actions = {
click: {
firstElemHandler,
},
touchstart: {
firstElemHandler,
},
mouseover: {
firstElemHandler: elem => elem.textContent = "Now ... click me!",
outerHandling: elem => {
console.clear();
console.log(`Hi from outerHandling, handle time ${
new Date().toLocaleTimeString()}`);
},
}
};
Object.keys(actions).forEach(key => document.addEventListener(key, handle));
function handle(evt) {
const origin = evt.target.closest("[data-action]");
return origin &&
actions[evt.type] &&
actions[evt.type][origin.dataset.action] &&
actions[evt.type][origin.dataset.action](origin, evt) ||
true;
}
[data-action]:hover {
cursor: pointer;
}
<div data-action="outerHandling">
<div id="first" data-action="firstElemHandler">
<b>Hover, click or tap</b>
</div>
this is handled too (on mouse over)
</div>
For large numbers of events this might help:
var element = document.getElementById("myId");
var myEvents = "click touchstart touchend".split(" ");
var handler = function (e) {
do something
};
for (var i=0, len = myEvents.length; i < len; i++) {
element.addEventListener(myEvents[i], handler, false);
}
Update 06/2017:
Now that new language features are more widely available you could simplify adding a limited list of events that share one listener.
const element = document.querySelector("#myId");
function handleEvent(e) {
// do something
}
// I prefer string.split because it makes editing the event list slightly easier
"click touchstart touchend touchmove".split(" ")
.map(name => element.addEventListener(name, handleEvent, false));
If you want to handle lots of events and have different requirements per listener you can also pass an object which most people tend to forget.
const el = document.querySelector("#myId");
const eventHandler = {
// called for each event on this element
handleEvent(evt) {
switch (evt.type) {
case "click":
case "touchstart":
// click and touchstart share click handler
this.handleClick(e);
break;
case "touchend":
this.handleTouchend(e);
break;
default:
this.handleDefault(e);
}
},
handleClick(e) {
// do something
},
handleTouchend(e) {
// do something different
},
handleDefault(e) {
console.log("unhandled event: %s", e.type);
}
}
el.addEventListener(eventHandler);
Update 05/2019:
const el = document.querySelector("#myId");
const eventHandler = {
handlers: {
click(e) {
// do something
},
touchend(e) {
// do something different
},
default(e) {
console.log("unhandled event: %s", e.type);
}
},
// called for each event on this element
handleEvent(evt) {
switch (evt.type) {
case "click":
case "touchstart":
// click and touchstart share click handler
this.handlers.click(e);
break;
case "touchend":
this.handlers.touchend(e);
break;
default:
this.handlers.default(e);
}
}
}
Object.keys(eventHandler.handlers)
.map(eventName => el.addEventListener(eventName, eventHandler))
Unless your do_something function actually does something with any given arguments, you can just pass it as the event handler.
var first = document.getElementById('first');
first.addEventListener('touchstart', do_something, false);
first.addEventListener('click', do_something, false);
Simplest solution for me was passing the code into a separate function and then calling that function in an event listener, works like a charm.
function somefunction() { ..code goes here ..}
variable.addEventListener('keyup', function() {
somefunction(); // calling function on keyup event
})
variable.addEventListener('keydown', function() {
somefunction(); //calling function on keydown event
})
I have a small solution that attaches to the prototype
EventTarget.prototype.addEventListeners = function(type, listener, options,extra) {
let arr = type;
if(typeof type == 'string'){
let sp = type.split(/[\s,;]+/);
arr = sp;
}
for(let a of arr){
this.addEventListener(a,listener,options,extra);
}
};
Allows you to give it a string or Array. The string can be separated with a space(' '), a comma(',') OR a Semicolon(';')
I just made this function (intentionally minified):
((i,e,f)=>e.forEach(o=>i.addEventListener(o,f)))(element, events, handler)
Usage:
((i,e,f)=>e.forEach(o=>i.addEventListener(o,f)))(element, ['click', 'touchstart'], (event) => {
// function body
});
The difference compared to other approaches is that the handling function is defined only once and then passed to every addEventListener.
EDIT:
Adding a non-minified version to make it more comprehensible. The minified version was meant just to be copy-pasted and used.
((element, event_names, handler) => {
event_names.forEach( (event_name) => {
element.addEventListener(event_name, handler)
})
})(element, ['click', 'touchstart'], (event) => {
// function body
});
I'm new at JavaScript coding, so forgive me if I'm wrong.
I think you can create an object and the event handlers like this:
const myEvents = {
click: clickOnce,
dblclick: clickTwice,
};
function clickOnce() {
console.log("Once");
}
function clickTwice() {
console.log("Twice");
}
Object.keys(myEvents).forEach((key) => {
const myButton = document.querySelector(".myButton")
myButton.addEventListener(key, myEvents[key]);
});
<h1 class="myButton">Button</h1>
And then click on the element.
document.getElementById('first').addEventListener('touchstart',myFunction);
document.getElementById('first').addEventListener('click',myFunction);
function myFunction(e){
e.preventDefault();e.stopPropagation()
do_something();
}
You should be using e.stopPropagation() because if not, your function will fired twice on mobile
This is my solution in which I deal with multiple events in my workflow.
let h2 = document.querySelector("h2");
function addMultipleEvents(eventsArray, targetElem, handler) {
eventsArray.map(function(event) {
targetElem.addEventListener(event, handler, false);
}
);
}
let counter = 0;
function countP() {
counter++;
h2.innerHTML = counter;
}
// magic starts over here...
addMultipleEvents(['click', 'mouseleave', 'mouseenter'], h2, countP);
<h1>MULTI EVENTS DEMO - If you click, move away or enter the mouse on the number, it counts...</h1>
<h2 style="text-align:center; font: bold 3em comic; cursor: pointer">0</h2>
What about something like this:
['focusout','keydown'].forEach( function(evt) {
self.slave.addEventListener(evt, function(event) {
// Here `this` is for the slave, i.e. `self.slave`
if ((event.type === 'keydown' && event.which === 27) || event.type === 'focusout') {
this.style.display = 'none';
this.parentNode.querySelector('.master').style.display = '';
this.parentNode.querySelector('.master').value = this.value;
console.log('out');
}
}, false);
});
// The above is replacement of:
/* self.slave.addEventListener("focusout", function(event) { })
self.slave.addEventListener("keydown", function(event) {
if (event.which === 27) { // Esc
}
})
*/
You can simply do it iterating an Object. This can work with a single or multiple elements. This is an example:
const ELEMENTS = {'click': element1, ...};
for (const [key, value] of Object.entries(ELEMENTS)) {
value.addEventListener(key, () => {
do_something();
});
}
When key is the type of event and value is the element when you are adding the event, so you can edit ELEMENTS adding your elements and the type of event.
Semi-related, but this is for initializing one unique event listener specific per element.
You can use the slider to show the values in realtime, or check the console.
On the <input> element I have a attr tag called data-whatever, so you can customize that data if you want to.
sliders = document.querySelectorAll("input");
sliders.forEach(item=> {
item.addEventListener('input', (e) => {
console.log(`${item.getAttribute("data-whatever")} is this value: ${e.target.value}`);
item.nextElementSibling.textContent = e.target.value;
});
})
.wrapper {
display: flex;
}
span {
padding-right: 30px;
margin-left: 5px;
}
* {
font-size: 12px
}
<div class="wrapper">
<input type="range" min="1" data-whatever="size" max="800" value="50" id="sliderSize">
<em>50</em>
<span>Size</span>
<br>
<input type="range" min="1" data-whatever="OriginY" max="800" value="50" id="sliderOriginY">
<em>50</em>
<span>OriginY</span>
<br>
<input type="range" min="1" data-whatever="OriginX" max="800" value="50" id="sliderOriginX">
<em>50</em>
<span>OriginX</span>
</div>
//catch volume update
var volEvents = "change,input";
var volEventsArr = volEvents.split(",");
for(var i = 0;i<volknob.length;i++) {
for(var k=0;k<volEventsArr.length;k++) {
volknob[i].addEventListener(volEventsArr[k], function() {
var cfa = document.getElementsByClassName('watch_televised');
for (var j = 0; j<cfa.length; j++) {
cfa[j].volume = this.value / 100;
}
});
}
}
'onclick' in the html works for both touch and click event. Here's the example.
This mini javascript libary (1.3 KB) can do all these things
https://github.com/Norair1997/norjs/
nor.event(["#first"], ["touchstart", "click"], [doSomething, doSomething]);

How to trigger JavaScript custom events correctly

I am struggling to understand how a custom event type is linked to a specific user action/trigger. All documentation seems to dispatch the event without any user interaction.
In the following example I want the event to be dispatched once a user has been hovering on the element for 3 seconds.
var img = document.createElement('img');img.src = 'http://placehold.it/100x100';
document.body.appendChild(img)
var event = new CustomEvent("hoveredforthreeseconds");
img.addEventListener('hoveredforthreeseconds', function(e) { console.log(e.type)}, true);
var thetrigger = function (element, event) {
var timeout = null;
element.addEventListener('mouseover',function() {
timeout = setTimeout(element.dispatchEvent(event), 3000);
},true);
element.addEventListener('mouseout', function() {
clearTimeout(timeout);
},true);
};
I have a trigger but no logical way of connecting it to the event.
I was thinking about creating an object called CustomEventTrigger which is essentially CustomEvent but has a third parameter for the trigger and also creating a method called addCustomEventListener, which works the same as addEventListener but when initialised it then passes the target Element to the custom event trigger which then dispatches the event when it's instructed to.
Custom events have to be triggered programatically through dispatchEvent, they are not fired by the DOM. You will always need to explictly call them in your code, such as in response to a user-generated event such as onmouseover, or a change of state such as onload.
You're very close to a working implementation, however you're immediately invoking dispatchEvent in your setTimeout. If you save it into a closure (as below) you can invoke dispatchEvent while passing your element after setTimeout has finished the timeout.
It's also good practice to declare your variables at the top of a file, to avoid possible scope issues.
var img = document.createElement('img'), timeout, event, thetrigger;
img.src = 'http://placehold.it/100x100';
document.body.appendChild(img);
img.addEventListener("hoveredForThreeSeconds", afterHover, false);
thetrigger = function (element, event) {
timeout = null;
element.addEventListener('mouseover',function() {
timeout = setTimeout(function(){ element.dispatchEvent(event) }, 3000);
},true);
element.addEventListener('mouseout', function() {
clearTimeout(timeout);
},true);
};
function afterHover(e) {
console.log("Event is called: " + e.type);
}
event = new CustomEvent("hoveredForThreeSeconds");
thetrigger(img, event);
I have created a method called addCustomEventListener, which works the same as addEventListener but when initialised passes the target Element to the custom event trigger which dispatches the event when it says, so in this case it only dispatches if the timeout reaches 3 seconds.
var img = document.getElementById('img');
window.mouseover3000 = new CustomEvent('mouseover3000', {
detail: {
trigger: function(element, type) {
timeout = null;
element.addEventListener('mouseover', function() {
timeout = setTimeout(function() {
element.dispatchEvent(window[type])
}, 3000);
}, false);
element.addEventListener('mouseout', function() {
clearTimeout(timeout);
}, false)
}
}
});
window.tripleclick = new CustomEvent('tripleclick', {
detail: {
trigger: function(element, type) {
element.addEventListener('click', function(e) {
if(e.detail ===3){
element.dispatchEvent(window[type])
}
}, false);
}
}
});
EventTarget.prototype.addCustomEventListener = function(type, listener, useCapture, wantsUntrusted) {
this.addEventListener(type, listener, useCapture, wantsUntrusted);
window[type].detail.trigger(this, type);
}
var eventTypeImage = function(e) {
this.src = "http://placehold.it/200x200?text=" + e.type;
}
img.addEventListener('mouseout', eventTypeImage, false);
img.addEventListener('mouseover', eventTypeImage, false);
img.addCustomEventListener('mouseover3000', eventTypeImage, false);
img.addCustomEventListener('tripleclick', eventTypeImage, false);
<img id="img" src="http://placehold.it/200x200?text=No+hover" ;/>
I think this could be useful to others so please feel free to improve on this.

How to assing new mouse event on node without overriding those set earlier?

Example:
domNode.onmouseover = function() {
this.innerHTML = "The mighty mouse is over me!"
}
domNode.onmouseover = function() {
this.style.backgroundColor = "yellow";
}
In this example the text won't change, but the thing is also that I don't always know what was assigned before, so is there a way to tell to js: Run everything that was eventually assigned without knowing what was that and then run my function?
It's possible to do this by passing the current event handler to the new handler:
domNode.onmouseover = function()
{
console.log('first handler');
}
domNode.onmouseover = (function (current)
{
return function()
{
current();//call handler that was set when this handler was created
console.log('new handler');
};
})(domNode.onmouseover);//pass reference to current handler
See this fiddle, to see it in actionYou can keep on doing this as much as you want/need:
domNode.onmouseover = function()
{
console.log('first handler');
}
domNode.onmouseover = (function (current)
{
return function()
{
current();
console.log('second handler');
};
})(domNode.onmouseover);
domNode.onmouseover = (function (current)
{
return function()
{
current();
console.log('third handler');
};
})(domNode.onmouseover);
This will log:
first handler
second handler
third handler
That's all there is to it!
First of all, place it in a document.ready. (not sure if you done that)
If you want 2 actions for one action place it in once function.
You can also create 2 functions and call them in your mouseover.
$(document).ready(function(){
domNode.onmouseover = function() {
this.innerHTML = "The mighty mouse is over me!"
this.style.backgroundColor = "yellow";
}
});

adding multiple event listeners to one element

So my dilemma is that I don't want to write the same code twice. Once for the click event and another for the touchstart event.
Here is the original code:
document.getElementById('first').addEventListener('touchstart', function(event) {
do_something();
});
document.getElementById('first').addEventListener('click', function(event) {
do_something();
});
How can I compact this? There HAS to be a simpler way!
I thought some might find this approach useful; it could be applied to any similarly repetitive code:
ES6
['click','ontouchstart'].forEach( evt =>
element.addEventListener(evt, dosomething, false)
);
ES5
['click','ontouchstart'].forEach( function(evt) {
element.addEventListener(evt, dosomething, false);
});
You can just define a function and pass it. Anonymous functions are not special in any way, all functions can be passed around as values.
var elem = document.getElementById('first');
elem.addEventListener('touchstart', handler, false);
elem.addEventListener('click', handler, false);
function handler(event) {
do_something();
}
Maybe you can use a helper function like this:
// events and args should be of type Array
function addMultipleListeners(element,events,handler,useCapture,args){
if (!(events instanceof Array)){
throw 'addMultipleListeners: '+
'please supply an array of eventstrings '+
'(like ["click","mouseover"])';
}
//create a wrapper to be able to use additional arguments
var handlerFn = function(e){
handler.apply(this, args && args instanceof Array ? args : []);
}
for (var i=0;i<events.length;i+=1){
element.addEventListener(events[i],handlerFn,useCapture);
}
}
function handler(e) {
// do things
};
// usage
addMultipleListeners(
document.getElementById('first'),
['touchstart','click'],
handler,
false);
[Edit nov. 2020] This answer is pretty old. The way I solve this nowadays is by using an actions object where handlers are specified per event type, a data-attribute for an element to indicate which action should be executed on it and one generic document wide handler method (so event delegation).
const firstElemHandler = (elem, evt) =>
elem.textContent = `You ${evt.type === "click" ? "clicked" : "touched"}!`;
const actions = {
click: {
firstElemHandler,
},
touchstart: {
firstElemHandler,
},
mouseover: {
firstElemHandler: elem => elem.textContent = "Now ... click me!",
outerHandling: elem => {
console.clear();
console.log(`Hi from outerHandling, handle time ${
new Date().toLocaleTimeString()}`);
},
}
};
Object.keys(actions).forEach(key => document.addEventListener(key, handle));
function handle(evt) {
const origin = evt.target.closest("[data-action]");
return origin &&
actions[evt.type] &&
actions[evt.type][origin.dataset.action] &&
actions[evt.type][origin.dataset.action](origin, evt) ||
true;
}
[data-action]:hover {
cursor: pointer;
}
<div data-action="outerHandling">
<div id="first" data-action="firstElemHandler">
<b>Hover, click or tap</b>
</div>
this is handled too (on mouse over)
</div>
For large numbers of events this might help:
var element = document.getElementById("myId");
var myEvents = "click touchstart touchend".split(" ");
var handler = function (e) {
do something
};
for (var i=0, len = myEvents.length; i < len; i++) {
element.addEventListener(myEvents[i], handler, false);
}
Update 06/2017:
Now that new language features are more widely available you could simplify adding a limited list of events that share one listener.
const element = document.querySelector("#myId");
function handleEvent(e) {
// do something
}
// I prefer string.split because it makes editing the event list slightly easier
"click touchstart touchend touchmove".split(" ")
.map(name => element.addEventListener(name, handleEvent, false));
If you want to handle lots of events and have different requirements per listener you can also pass an object which most people tend to forget.
const el = document.querySelector("#myId");
const eventHandler = {
// called for each event on this element
handleEvent(evt) {
switch (evt.type) {
case "click":
case "touchstart":
// click and touchstart share click handler
this.handleClick(e);
break;
case "touchend":
this.handleTouchend(e);
break;
default:
this.handleDefault(e);
}
},
handleClick(e) {
// do something
},
handleTouchend(e) {
// do something different
},
handleDefault(e) {
console.log("unhandled event: %s", e.type);
}
}
el.addEventListener(eventHandler);
Update 05/2019:
const el = document.querySelector("#myId");
const eventHandler = {
handlers: {
click(e) {
// do something
},
touchend(e) {
// do something different
},
default(e) {
console.log("unhandled event: %s", e.type);
}
},
// called for each event on this element
handleEvent(evt) {
switch (evt.type) {
case "click":
case "touchstart":
// click and touchstart share click handler
this.handlers.click(e);
break;
case "touchend":
this.handlers.touchend(e);
break;
default:
this.handlers.default(e);
}
}
}
Object.keys(eventHandler.handlers)
.map(eventName => el.addEventListener(eventName, eventHandler))
Unless your do_something function actually does something with any given arguments, you can just pass it as the event handler.
var first = document.getElementById('first');
first.addEventListener('touchstart', do_something, false);
first.addEventListener('click', do_something, false);
Simplest solution for me was passing the code into a separate function and then calling that function in an event listener, works like a charm.
function somefunction() { ..code goes here ..}
variable.addEventListener('keyup', function() {
somefunction(); // calling function on keyup event
})
variable.addEventListener('keydown', function() {
somefunction(); //calling function on keydown event
})
I have a small solution that attaches to the prototype
EventTarget.prototype.addEventListeners = function(type, listener, options,extra) {
let arr = type;
if(typeof type == 'string'){
let sp = type.split(/[\s,;]+/);
arr = sp;
}
for(let a of arr){
this.addEventListener(a,listener,options,extra);
}
};
Allows you to give it a string or Array. The string can be separated with a space(' '), a comma(',') OR a Semicolon(';')
I just made this function (intentionally minified):
((i,e,f)=>e.forEach(o=>i.addEventListener(o,f)))(element, events, handler)
Usage:
((i,e,f)=>e.forEach(o=>i.addEventListener(o,f)))(element, ['click', 'touchstart'], (event) => {
// function body
});
The difference compared to other approaches is that the handling function is defined only once and then passed to every addEventListener.
EDIT:
Adding a non-minified version to make it more comprehensible. The minified version was meant just to be copy-pasted and used.
((element, event_names, handler) => {
event_names.forEach( (event_name) => {
element.addEventListener(event_name, handler)
})
})(element, ['click', 'touchstart'], (event) => {
// function body
});
I'm new at JavaScript coding, so forgive me if I'm wrong.
I think you can create an object and the event handlers like this:
const myEvents = {
click: clickOnce,
dblclick: clickTwice,
};
function clickOnce() {
console.log("Once");
}
function clickTwice() {
console.log("Twice");
}
Object.keys(myEvents).forEach((key) => {
const myButton = document.querySelector(".myButton")
myButton.addEventListener(key, myEvents[key]);
});
<h1 class="myButton">Button</h1>
And then click on the element.
document.getElementById('first').addEventListener('touchstart',myFunction);
document.getElementById('first').addEventListener('click',myFunction);
function myFunction(e){
e.preventDefault();e.stopPropagation()
do_something();
}
You should be using e.stopPropagation() because if not, your function will fired twice on mobile
This is my solution in which I deal with multiple events in my workflow.
let h2 = document.querySelector("h2");
function addMultipleEvents(eventsArray, targetElem, handler) {
eventsArray.map(function(event) {
targetElem.addEventListener(event, handler, false);
}
);
}
let counter = 0;
function countP() {
counter++;
h2.innerHTML = counter;
}
// magic starts over here...
addMultipleEvents(['click', 'mouseleave', 'mouseenter'], h2, countP);
<h1>MULTI EVENTS DEMO - If you click, move away or enter the mouse on the number, it counts...</h1>
<h2 style="text-align:center; font: bold 3em comic; cursor: pointer">0</h2>
What about something like this:
['focusout','keydown'].forEach( function(evt) {
self.slave.addEventListener(evt, function(event) {
// Here `this` is for the slave, i.e. `self.slave`
if ((event.type === 'keydown' && event.which === 27) || event.type === 'focusout') {
this.style.display = 'none';
this.parentNode.querySelector('.master').style.display = '';
this.parentNode.querySelector('.master').value = this.value;
console.log('out');
}
}, false);
});
// The above is replacement of:
/* self.slave.addEventListener("focusout", function(event) { })
self.slave.addEventListener("keydown", function(event) {
if (event.which === 27) { // Esc
}
})
*/
You can simply do it iterating an Object. This can work with a single or multiple elements. This is an example:
const ELEMENTS = {'click': element1, ...};
for (const [key, value] of Object.entries(ELEMENTS)) {
value.addEventListener(key, () => {
do_something();
});
}
When key is the type of event and value is the element when you are adding the event, so you can edit ELEMENTS adding your elements and the type of event.
Semi-related, but this is for initializing one unique event listener specific per element.
You can use the slider to show the values in realtime, or check the console.
On the <input> element I have a attr tag called data-whatever, so you can customize that data if you want to.
sliders = document.querySelectorAll("input");
sliders.forEach(item=> {
item.addEventListener('input', (e) => {
console.log(`${item.getAttribute("data-whatever")} is this value: ${e.target.value}`);
item.nextElementSibling.textContent = e.target.value;
});
})
.wrapper {
display: flex;
}
span {
padding-right: 30px;
margin-left: 5px;
}
* {
font-size: 12px
}
<div class="wrapper">
<input type="range" min="1" data-whatever="size" max="800" value="50" id="sliderSize">
<em>50</em>
<span>Size</span>
<br>
<input type="range" min="1" data-whatever="OriginY" max="800" value="50" id="sliderOriginY">
<em>50</em>
<span>OriginY</span>
<br>
<input type="range" min="1" data-whatever="OriginX" max="800" value="50" id="sliderOriginX">
<em>50</em>
<span>OriginX</span>
</div>
//catch volume update
var volEvents = "change,input";
var volEventsArr = volEvents.split(",");
for(var i = 0;i<volknob.length;i++) {
for(var k=0;k<volEventsArr.length;k++) {
volknob[i].addEventListener(volEventsArr[k], function() {
var cfa = document.getElementsByClassName('watch_televised');
for (var j = 0; j<cfa.length; j++) {
cfa[j].volume = this.value / 100;
}
});
}
}
'onclick' in the html works for both touch and click event. Here's the example.
This mini javascript libary (1.3 KB) can do all these things
https://github.com/Norair1997/norjs/
nor.event(["#first"], ["touchstart", "click"], [doSomething, doSomething]);

Categories

Resources