"keyup" event and "keydown" being called by the same event handler - javascript

Hope your having a wonderful week :D
I have just started up with JavaScript and was wondering is it possible to handle the two events from one event-handler.
So for example
document.addEventListener("keydown",handleKeys);
document.addEventListener("keyup",handleKeys);
function handleKeys(e , a){
switch(e.keyCode) {
case '0': return isSomething = a; // a = false | true for keydown and keyup
}
}
Would something like this be possible

Yes, that's possible, though you'll need to set the 2nd param of handleKeys, or it will be undefined.
document.addEventListener("keydown", e => handleKeys(e, true));
document.addEventListener("keyup", e => handleKeys(e, false));
function handleKeys(e, down) {
console.log(down);
}

Yes you can do it, however - dont do it in this way - is better to use two separate handlers. If they have shared code, then move that code to separate third method which will be call with that handlers.

Related

Why wouldn't the event listener get removed from the tds in this html row? [duplicate]

I have the following code to add eventListener
area.addEventListener('click',function(event) {
app.addSpot(event.clientX,event.clientY);
app.addFlag = 1;
},true);
It is working correctly as expected..Later in another function i tried to remove the event listener using the following code
area.removeEventListener('click',function(event) {
app.addSpot(event.clientX,event.clientY);
app.addFlag = 1;
},true);
But the even listener is not removed..Why is it happening?Is there any problem with my removeEventListener()?
Note:Here area is something like document.getElementById('myId')
This is because that two anonymous functions are completely different functions. Your removeEventListener's argument is not a reference to the function object that was previously attached.
function foo(event) {
app.addSpot(event.clientX,event.clientY);
app.addFlag = 1;
}
area.addEventListener('click',foo,true);
area.removeEventListener('click',foo,true);
I find that for the windows object, the last param "true" is required.
The remove doesn't work if there is no capture flag.
In a React function component, make sure to define the callback with the useCallback(() => {}) hook. If you fail to do this, the callback will be a different one on every re-render and the removeEventListener method will not work.
const scrollCallback = useCallback(() => { // do sth. });
window.addEventListener("scroll", scrollCallback, true);
window.removeEventListener("scroll", scrollCallback, true);
It looks like no one's covered the part of the DOM specification (that both browsers and Node.js implement) that now gives you a mechanism to remove your event listener without using removeEventListener.
If we look at https://dom.spec.whatwg.org/#concept-event-listener we see that there are a number of properties that can be passed as options when setting up an event listener:
{
type (a string)
callback (an EventListener object, null by default)
capture (a boolean, false by default)
passive (a boolean, false by default)
once (a boolean, false by default)
signal (an AbortSignal object, null by default)
removed (a boolean for bookkeeping purposes, false by default)
}
Now, there's a lot of useful properties in that list, but for the purposes of removing an event listener it's the signal property that we want to make use of (which was added to the DOM level 3 in late 2020), because it lets us remove an event listener by using an AbortController instead of having to bother with keeping a reference to the exact handler function and listener options "because otherwise removeEventListener won't even work properly":
const areaListener = new AbortController();
area.addEventListener(
`click`,
({clientX: x, clientY: y}) => {
app.addSpot(x, y);
app.addFlag = 1;
},
{ signal: areaListener.signal }
);
And now, when it's time to remove that event listener, we simply run:
areaListener.abort()
And done: the JS engine will abort and clean up our event listener. No keeping a reference to the handling function, no making sure we call removeEventListener with the exact same funcation and properties as we called addEventListener: we just cancel the listener with a single, argumentless, abort call.
And of course, also note that if we want to do this "because we only want the handler to fire once", then we don't even need to do this, we can just create an event listener with { once: true } and JS will take care of the rest. No removal code required.
area.addEventListener(
`click`,
() => app.bootstrapSomething(),
{ once: true }
);
You are creating two different functions in both calls. So the second function does not relate in any way to the first one and the engine is able to remove the function. Use a common identifier for the function instead.
var handler = function(event) {
app.addSpot(event.clientX,event.clientY);
app.addFlag = 1;
};
area.addEventListener('click', handler,true);
later you can then remove the handler by calling
area.removeEventListener('click', handler,true);
To remove it, store the function in a variable or simply use a named function and pass that function to the removeEventListener call:
function areaClicked(event) {
app.addSpot(event.clientX, event.clientY);
app.addFlag = 1;
}
area.addEventListener('click', areaClicked, true);
// ...
area.removeEventListener('click', areaClicked, true);
If you want to pass local variables to the function called by the event listener, you can define the function inside the function (to get the local variables) and pass the name of the function in the function itself. For example, let's start inside the function that adds the event listener with app as a local variable. You would write a function inside this function such as,
function yourFunction () {
var app;
function waitListen () {
waitExecute(app, waitListen);
}
area.addEventListener('click', waitListen, true);
}
Then you have what you need to remove it when waitExecute is called.
function waitExecute (app, waitListen) {
... // other code
area.removeEventListener('click', waitListen, true);
}
define your Event Handler first,
and then
area.addEventListener('click',handler);
area.removeEventListener('click',handler);
This is what I ended up doing but it's in a route class but should not make much difference, I wanted for the event listener not to accumulate each time afterModel() is called but also needed arguments and scope so that the model is changed each time.
export default class iFrameRoute extends Route {
afterModel(model) {
this.initFrame = function(event) {
alert("I am being called");
window.removeEventListener("message", this.route.test);
}.bind({route: this, data: model});
window.addEventListener("message", this.initFrame );
}
}
I went through this same problem recently. A reasonble solution that I found was remove attribute "onclick" on element from HTMLElement class.
Let's imagine that you already got your component from DOM - using document.getElementById or document.querySelector - you can try that code:
js
const element = document.getElementById("myelement");
element.attributes.removeNamedItem('onclick');
html example
<div onClick="memoryGame.flipCard(this)">
.... // children elements
</div>
I know which this solution it ins't the best, but it works!
I hope I was able to help you.
Cheers!
PS: please, give me a "useful answer"... thanks :D
while adding function store in array and removing pass by map work for me
const [functionObjects, setfunctionObjects] = useState([]);
const addListener = (beforeUnloadListener) =>{
setfunctionObjects([...nano, beforeUnloadListener]);
addEventListener("beforeunload", beforeUnloadListener, {capture: true});
};
const removeListener = (beforeUnloadListener) => {
functionObjects.map((item) => {
removeEventListener("beforeunload", item, {capture: true});});
};
In case of React we can use useRef() to store our listener function in current property. So that in case of re-render and in case of remove listener it will maintain the same reference to the function.
const handleWindowClick = useRef(() => {
console.log("window clicked");
});
// for attaching event listener
window.addEventListener("click", handleWindowClick.current);
// for detaching event listener
window.removeEventListener("click", handleWindowClick.current);
Update 2023
I was using Angular and after numerous tries using AbortController() nothing solved my problem.
Finally renderer2 to the rescue. Here's what I did
mouseMoveListener :any;
mouseUpListener :any;
this.mouseMoveListener = this._renderer.listen("document", "mousemove", (event) => {
this.onMouseMove(event);
});
this.mouseUpListener = this._renderer.listen("document", "mouseup", (event) => {
this.onMouseUp(event);
});
ngOnDestroy(){
this.mouseMoveListener();
this.mouseUpListener();
}

removeEventListener on document in functional component react is not working [duplicate]

I have the following code to add eventListener
area.addEventListener('click',function(event) {
app.addSpot(event.clientX,event.clientY);
app.addFlag = 1;
},true);
It is working correctly as expected..Later in another function i tried to remove the event listener using the following code
area.removeEventListener('click',function(event) {
app.addSpot(event.clientX,event.clientY);
app.addFlag = 1;
},true);
But the even listener is not removed..Why is it happening?Is there any problem with my removeEventListener()?
Note:Here area is something like document.getElementById('myId')
This is because that two anonymous functions are completely different functions. Your removeEventListener's argument is not a reference to the function object that was previously attached.
function foo(event) {
app.addSpot(event.clientX,event.clientY);
app.addFlag = 1;
}
area.addEventListener('click',foo,true);
area.removeEventListener('click',foo,true);
I find that for the windows object, the last param "true" is required.
The remove doesn't work if there is no capture flag.
In a React function component, make sure to define the callback with the useCallback(() => {}) hook. If you fail to do this, the callback will be a different one on every re-render and the removeEventListener method will not work.
const scrollCallback = useCallback(() => { // do sth. });
window.addEventListener("scroll", scrollCallback, true);
window.removeEventListener("scroll", scrollCallback, true);
It looks like no one's covered the part of the DOM specification (that both browsers and Node.js implement) that now gives you a mechanism to remove your event listener without using removeEventListener.
If we look at https://dom.spec.whatwg.org/#concept-event-listener we see that there are a number of properties that can be passed as options when setting up an event listener:
{
type (a string)
callback (an EventListener object, null by default)
capture (a boolean, false by default)
passive (a boolean, false by default)
once (a boolean, false by default)
signal (an AbortSignal object, null by default)
removed (a boolean for bookkeeping purposes, false by default)
}
Now, there's a lot of useful properties in that list, but for the purposes of removing an event listener it's the signal property that we want to make use of (which was added to the DOM level 3 in late 2020), because it lets us remove an event listener by using an AbortController instead of having to bother with keeping a reference to the exact handler function and listener options "because otherwise removeEventListener won't even work properly":
const areaListener = new AbortController();
area.addEventListener(
`click`,
({clientX: x, clientY: y}) => {
app.addSpot(x, y);
app.addFlag = 1;
},
{ signal: areaListener.signal }
);
And now, when it's time to remove that event listener, we simply run:
areaListener.abort()
And done: the JS engine will abort and clean up our event listener. No keeping a reference to the handling function, no making sure we call removeEventListener with the exact same funcation and properties as we called addEventListener: we just cancel the listener with a single, argumentless, abort call.
And of course, also note that if we want to do this "because we only want the handler to fire once", then we don't even need to do this, we can just create an event listener with { once: true } and JS will take care of the rest. No removal code required.
area.addEventListener(
`click`,
() => app.bootstrapSomething(),
{ once: true }
);
You are creating two different functions in both calls. So the second function does not relate in any way to the first one and the engine is able to remove the function. Use a common identifier for the function instead.
var handler = function(event) {
app.addSpot(event.clientX,event.clientY);
app.addFlag = 1;
};
area.addEventListener('click', handler,true);
later you can then remove the handler by calling
area.removeEventListener('click', handler,true);
To remove it, store the function in a variable or simply use a named function and pass that function to the removeEventListener call:
function areaClicked(event) {
app.addSpot(event.clientX, event.clientY);
app.addFlag = 1;
}
area.addEventListener('click', areaClicked, true);
// ...
area.removeEventListener('click', areaClicked, true);
If you want to pass local variables to the function called by the event listener, you can define the function inside the function (to get the local variables) and pass the name of the function in the function itself. For example, let's start inside the function that adds the event listener with app as a local variable. You would write a function inside this function such as,
function yourFunction () {
var app;
function waitListen () {
waitExecute(app, waitListen);
}
area.addEventListener('click', waitListen, true);
}
Then you have what you need to remove it when waitExecute is called.
function waitExecute (app, waitListen) {
... // other code
area.removeEventListener('click', waitListen, true);
}
define your Event Handler first,
and then
area.addEventListener('click',handler);
area.removeEventListener('click',handler);
This is what I ended up doing but it's in a route class but should not make much difference, I wanted for the event listener not to accumulate each time afterModel() is called but also needed arguments and scope so that the model is changed each time.
export default class iFrameRoute extends Route {
afterModel(model) {
this.initFrame = function(event) {
alert("I am being called");
window.removeEventListener("message", this.route.test);
}.bind({route: this, data: model});
window.addEventListener("message", this.initFrame );
}
}
I went through this same problem recently. A reasonble solution that I found was remove attribute "onclick" on element from HTMLElement class.
Let's imagine that you already got your component from DOM - using document.getElementById or document.querySelector - you can try that code:
js
const element = document.getElementById("myelement");
element.attributes.removeNamedItem('onclick');
html example
<div onClick="memoryGame.flipCard(this)">
.... // children elements
</div>
I know which this solution it ins't the best, but it works!
I hope I was able to help you.
Cheers!
PS: please, give me a "useful answer"... thanks :D
while adding function store in array and removing pass by map work for me
const [functionObjects, setfunctionObjects] = useState([]);
const addListener = (beforeUnloadListener) =>{
setfunctionObjects([...nano, beforeUnloadListener]);
addEventListener("beforeunload", beforeUnloadListener, {capture: true});
};
const removeListener = (beforeUnloadListener) => {
functionObjects.map((item) => {
removeEventListener("beforeunload", item, {capture: true});});
};
In case of React we can use useRef() to store our listener function in current property. So that in case of re-render and in case of remove listener it will maintain the same reference to the function.
const handleWindowClick = useRef(() => {
console.log("window clicked");
});
// for attaching event listener
window.addEventListener("click", handleWindowClick.current);
// for detaching event listener
window.removeEventListener("click", handleWindowClick.current);
Update 2023
I was using Angular and after numerous tries using AbortController() nothing solved my problem.
Finally renderer2 to the rescue. Here's what I did
mouseMoveListener :any;
mouseUpListener :any;
this.mouseMoveListener = this._renderer.listen("document", "mousemove", (event) => {
this.onMouseMove(event);
});
this.mouseUpListener = this._renderer.listen("document", "mouseup", (event) => {
this.onMouseUp(event);
});
ngOnDestroy(){
this.mouseMoveListener();
this.mouseUpListener();
}

jQuery function in two events

I have this code:
$('#email').keyup(function() {
if(true || false)) {
} else {
}
});
I need include this function also in blur event.
I've tried to create a jquery function but I could not. Somebody give me a light.
You can do this -
$('#email').on('keyup blur',function() {
http://api.jquery.com/on/
Use the on method to attach multiple events, which are specified in the first argument passed to the function.
$('#email').on('keyup blur', function() {
if(true || false) { //there was an extra ) here
} else {
}
});
Working Example http://jsfiddle.net/nv39M/
One thing to be aware of, the keyup event is going to fire prior to the blur event firing.
Make a separate function as follows
function funcName(){
//Your code
}
Now,use jQuery on
$("#email").on("keyup",funcName);
$("#email").on("blur",funcName);
For reference,check
http://api.jquery.com/on/
There are (at least) two ways you could achieve this.
Specify multiple, space separated events as the first argument:
$('#email').on('keyup blur',function() {
// your logic
});
Use a named function:
function yourFunction() {
// your logic
}
$('#email').on('keyup', yourFunction);
$('#email').on('blur', yourFunction);
Option 1 is probably the best choice assuming you don't want to use the function anywhere else, and that you want to bind the event handlers at the same time. If, however, you wanted to bind the blur event at a later point (perhaps in response to another event), or to a different element, then the named function method would be the best choice.

Remove EventListener in JavaScript after event occurred?

There are 24 div-objects waiting/listening for a mouse-click. After click on one div-object, I want to remove the EventListener from all 24 div-objects.
for (var i=1;i<=24;i++){
document.getElementById('div'+i).addEventListener('click',function(event){
for (var z=1;z<=24;z++){
document.getElementById('div'+z).removeEventListener()//Problem lies here
}
//Some other code to be run after mouseclick
},false);
}
The problem is that the removeEventListener is nested in addEventListener and I need to define type, listener, caption as attributes to the removeEventListener method. And I think it is impossible to define the listener because of nesting.
I also tried to define a function name, but it didn't worked:
for (var i=1;i<=24;i++){
document.getElementById('div'+i).addEventListener('click',function helpme(event){
for (var z=1;z<=24;z++){
document.getElementById('div'+z).removeEventListener('click',helpme,false);
}
//Some other code to be run after mouseclick
},false);
}
You can tell the event listener to simply fire just once:
document.addEventListener("click", (e) => {
// function which to run on event
}, { once: true });
The documentation says:
once:
A boolean value indicating that the listener should be invoked at most once after being added. If true, the listener would be automatically removed when invoked.
It should work with a named function. If your second approach does not work properly, try storing the initial listener into a variable, like this:
var handler = function(event) {
for(...) {
removeEventListener('click', handler, false);
}
};
addEventListener('click', handler, false);
Ps. if you care about speed, you may wish to consider using just one event handler. You can put the handler into the parent element of the divs, and then delegate the event from there. With 24 handlers your current approach probably doesn't have a very big performance hit, but this is something you should keep in mind if it ever feels slow.
For those who needs to remove after a certain condition (or even inside a loop too), one alternative is using AbortController and AbortSignal:
const abortController = new AbortController();
let handler = function(event) {
if(...) {
abortController.abort();
}
};
addEventListener('click', handler, {signal: abortController.signal});
The same answer:
element.addEventListener("click", (e) => {
// function which to run on event
}, { once: true });
You can read more here: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener

Javascript: simulate a click on a link

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.

Categories

Resources