Assign multiple event handlers to single event - javascript

I want to assign a JavaScript function to an event that already has a handler function. The new function should not change or remove (unassign) the existing function.
As an example:
I have a function called exFunction() that is already assigned to document.onmousemove. I have another function newFun() to assign to the same event. What I want to happen is when the document.onmousemove event occurs both functions are triggered.
newFun() is not a static function. It is going to be changing according to the webpage. (otherwise I can just write another function that calls both functions).
pure JavaScript only

By using addEventListener, you can apply multiple functions.
document.addEventListener('mousemove', exFunction)
document.addEventListener('mousemove', newFun)

You can also do like this
document.addEventListener("mousemove", function() {
myFunction();
someother();
});

As mentioned in other answers, you can easily assign several eventListeners to the event.
Note that the name of the function is the address of the code to execute and this address is immutable. The snippet shows what works and what doesn't.
var myDiv = document.getElementById('my-div');
function foo1(e) {
console.log('foo1');
if (e.altKey)
foo1 = foo2; //this doesn't work
};
function foo2(e) {
console.log('foo2');
if (e.shiftKey) {
myDiv.removeEventListener('click', foo2);
myDiv.addEventListener('click', foo3);
}
};
function foo3(e) {
console.log('foo3');
if (e.shiftKey) {
myDiv.removeEventListener('click', foo3);
myDiv.addEventListener('click', foo2);
}
};
//assign **addresses** of functions to eventListener
myDiv.addEventListener('click', foo1);
myDiv.addEventListener('click', foo2);
<div id="my-div" style="width:100px;height:100px;border:solid 1px"></div>

addEventListener is the right way to do this however time to time you may need to do this over the DOM element property event listeners by monkey patching.
Forgive me for partially snatching #AlexKudryashev's snippet.
var myDiv = document.getElementById('my-div'),
myBut = document.getElementById('my-button'),
f1 = e => console.log(`clicked "${e.currentTarget.id}" and f1 invoked`);
f2 = e => console.log(`clicked "${e.currentTarget.id}" and f2 invoked`);
monkeyPatch = (f1,f2) => e => (f1(e),f2(e));
myDiv.onclick = f1;
myBut.onclick = function(e){
console.log(`f2 monkey patched to f1. Click My Div to see the effect`);
myDiv.onclick = monkeyPatch(myDiv.onclick, f2);
}
<div id="my-div" style="width:100px;height:100px;border:solid 1px">My Div</div>
<button id="my-button">Monkey Patch f2</button>

Related

eventlistener can't access variable outside function

the read element was appended to another div container element by the submit eventlistener. but I can't access the read element when trying to attach it to another event listener outside the function. Any idea how to fix this?
function add() {
read = document.createElement('div');
read.textContent = 'Read';
read.classList.add('read');
card.appendChild(read);
}
submit.addEventListener('click', add);
read.addEventListener("click", () => {
read.classList.toggle('unread');
});
You code is not really correct.
I would suggest:
let read = null;
function add() {
if (read) return;
read = document.createElement('div');
read.textContent = 'Read';
read.classList.add('read');
card.appendChild(read);
read.addEventListener("click", () => {
read.classList.toggle('unread');
});
}
submit.addEventListener('click', add);
Although without a context on what you are trying to achieve it's going to be difficult.
BTW: no, variables that are declared inside a function are scoped, you cant access them from outside
The read element's eventListener isn't activated, because first if you click on the submit element the read element is an element.
Solution:
function add() {
read = document.createElement('div');
read.textContent = 'Read';
read.classList.add('read');
card.appendChild(read);
read.addEventListener("click", () => {
read.classList.toggle('unread');
});
}
submit.addEventListener('click', add);

React callback click event `this` undefined

I am using d3 and react to create charts. Clicking a button will update the chart. In the component for the chart is an onclick event listener, however the "this" keeps getting mixed up
One method with arrow functions returns Uncaught TypeError: node.getAttribute is not a function:
this.setupButtons= function() {
d3.select('#toolbar')
.selectAll('.buttons')
.on('click', ()=> {
d3.selectAll('.buttons').classed('active', false);
var button = d3.select(this);
button.classed('active', true);
var buttonId = button.attr('id');
this.toggleDisplay(buttonId)
});
}
*/
This is of course because the this refers to the component instance. So I researched how to refer to the click event and found that using e or event.target is supposed to solve the issue. In react, however, an error is returned: Uncaught TypeError: Cannot read property 'target' of undefined
this.setupButtons= function() {
d3.select('#toolbar')
.selectAll('.buttons')
.on('click', (e)=> {
d3.selectAll('.buttons').classed('active', false);
var button = d3.select(e.target);
button.classed('active', true);
var buttonId = button.attr('id');
this.toggleDisplay(buttonId)
});
}
Both this.setupButtons and this.toggleDisplay() are defined in the same method, which belongs to a component.
EDIT: This question does not appear to be a "duplicate" of the question provided.This was an issue of D3's handling of event, not the use of this, apparently. The solution to this question (add d3.event.target instead of event.target) is not provided as an answer in the question this supposedly duplicates.
You can store the reference of the outer this into another variable. I generally store it into a variable like var that = this and then you can reference the variable that wherever required.
this.setupButtons= function() {
var that = this;
d3.select('#toolbar')
.selectAll('.buttons')
.on('click', ()=> {
d3.selectAll('.buttons').classed('active', false);
var button = d3.select(that);
button.classed('active', true);
var buttonId = button.attr('id');
that.toggleDisplay(buttonId)
});
}
d3 goes about things a bit differently. The event is accessed from d3 itself (d3.event).
Source: https://octoperf.com/blog/2018/04/17/d3-js-mouse-events-and-transitions-tutorial/#how-to-use-d3-event-object
Assign var that = this in the outer function. Now you have a way to reference the outer this.
this.setupButtons = function () {
var that = this;
d3.select('#toolbar')
.selectAll('.buttons')
.on('click', function() {
d3.selectAll('.buttons').classed('active', false);
var button = d3.select(this);
button.classed('active', true);
var buttonId = button.attr('id');
that.toggleDisplay(buttonId)
});
}

convert javascript event handler to jquery

I would like to convert the event handler to a jquery style click event but it doesnt seem to like passing the event through, perhaps its because its not an anonymous function anymore?
// variables
var faqOne = document.getElementById("faqOne");
var $hiddenOne = $(".faqOneHidden");
// javascript event handler works!
faqOne.addEventListener("click", function(e){
showFaqOne.showClickedFaq(e);
}, false);
// javascript event handle - doesnt work!
$("#faqOne").click(function(){
showFaqOne.showClickedFaq(e);
});
// constructor
function DisplayQFaqs(link, faq){
this.link = link;
this.faq = faq;
}
// method prototype
DisplayQFaqs.prototype.showClickedFaq = function(e){
var el = e.currentTarget;
if(el === this.link) {
this.faq.toggle("slow", function(){
});
}
};
// new DisplayQFaqs Objects
var showFaqOne = new DisplayQFaqs(faqOne,$hiddenOne);
Your e is undefined inside
$("#faqOne").click(function(){
showFaqOne.showClickedFaq(e);
});
Change it to
$("#faqOne").click(function(e){//Now e is there
showFaqOne.showClickedFaq(e);
});

How to overwrite focus() in JavaScript for all elements?

I want to overwrite the function focus() for all HTML elements in JavaScript.
How do I do that?
The question asks for just how to override the .focus() method for HTML elements, so that will be answered first. There are however, other ways to trigger "focus" on elements, and overriding those methods are discussed below.
Please also note, it is probably not a good idea to override global prototypes.
Overriding the .focus() method on HTML elements
You can override the focus function for all HTMLElement's by modifying its prototype.
HTMLElement.prototype.focus = function () {
// ... do your custom stuff here
}
If you still want to run the default focus behavior and run your custom override, you can do the following:
let oldHTMLFocus = HTMLElement.prototype.focus;
HTMLElement.prototype.focus = function () {
// your custom code
oldHTMLFocus.apply(this, arguments);
};
Overriding the focus event set via handlers on HTML elements
This is done in a similar fashion to overriding the .focus() method. However, in this case, we will target the EventTarget constructor and modify the addEventListener prototype:
let oldAddListener = HTMLElement.prototype.addEventListener;
EventTarget.prototype.addEventListener = function () {
let scope = this;
let func = arguments[1];
if (arguments[0] === 'focus') {
arguments[1] = function (e) {
yourCustomFunction(scope);
func(e);
};
}
oldAddListener.apply(this, arguments);
};
If you don't want original behavior at all, you can remove the func call (func(e)).
Overriding the focus event set via the onfocus attribute
Doing this is actually quite tricky, and will most likely have some unforseen limitations. That said, I could not find a way to override this by modifying prototypes, etc. The only way I could get this work was by using MutationObservers. It works by finding all elements that have the attribute onfocus and it sets the first function to run to your override function:
let observer = new MutationObserver(handleOnFocusElements);
let observerConfig = {
attributes: true,
childList: true,
subtree: true,
attributeFilter: ['onfocus']
};
let targetNode = document.body;
observer.observe(targetNode, observerConfig);
function handleOnFocusElements() {
Array
.from(document.querySelectorAll('[onfocus]:not([onfocus*="yourCustomFunction"])'))
.forEach(element => {
let currentCallbacks = element.getAttribute('onfocus');
element.setAttribute('onfocus', `yourCustomFunction(this); return; ${currentCallbacks}`);
});
}
If you want to stop the original onfocus from firing its events completely, you can just empty the onfocus attribute entirely on any mutation changes, and just set the value to your desired function.
An example with all of the code snippets together:
(function() {
window.yourCustomFunction = function(target) {
console.log('OVERRIDE on element', target);
};
let observer = new MutationObserver(handleOnFocusElements);
let observerConfig = {
attributes: true,
childList: true,
subtree: true,
attributeFilter: ['onfocus']
};
let targetNode = document.body;
// Start overriding methods
// The HTML `.focus()` method
let oldHTMLFocus = HTMLElement.prototype.focus;
HTMLElement.prototype.focus = function() {
yourCustomFunction(this);
oldHTMLFocus.apply(this, arguments);
};
// Event Target's .addEventListener prototype
let oldAddListener = HTMLElement.prototype.addEventListener;
EventTarget.prototype.addEventListener = function() {
let scope = this;
let func = arguments[1];
if (arguments[0] === 'focus') {
arguments[1] = function(e) {
yourCustomFunction(scope);
func(e);
};
}
oldAddListener.apply(this, arguments);
};
// Handle the onfocus attributes
function handleOnFocusElements() {
Array
.from(document.querySelectorAll('[onfocus]:not([onfocus*="yourCustomFunction"])'))
.forEach(element => {
let currentCallbacks = element.getAttribute('onfocus');
element.setAttribute('onfocus', `yourCustomFunction(this); return; ${currentCallbacks}`);
});
}
window.addEventListener('load', () => {
handleOnFocusElements();
observer.observe(targetNode, observerConfig);
});
})();
let input = document.querySelector('input');
input.addEventListener('focus', function() {
console.log('Add Event Listener Focus');
});
function attributeHandler() {
console.log('Called From the Attribute Handler');
}
<input type="text" onfocus="attributeHandler()">
In pure JS:
HTML:
<input id="test" type="textfield" />​
JS:
var myInput = document.getElementById("test");
myInput.addEventListener("focus",deFocus(myInput),true);
function deFocus(element) {
element.setAttribute('readonly','readonly');
return false;
}
Working fiddle
In JQuery:
HTML
​<input id="test" type="textfield" />
JQuery
​$('#test').focus(function() {
$(this).blur();
});​​​​​​​​​​​​​​​​​​​​
Working fiddle
You can add the EventListener to whatever you want, class, id, etc. and run it through the function or trigger something else to unlock them. JQuery can also handle this pretty smoothly, though if you're going that route I would suggest using a class as a selector and then you can add and remove the class dynamically to disable focus.

how can MSIE alert 'unknow'?

for some purpose i want to cache the Window.Event object in a variable and use it later,but MSIE keep telling me that this is 'unknow'.
just run the code below in IE,you will see what i mean
i just want to ask how can this happen?
did i miss something?
html:
<button id='btn'>Click!!!</button>
JS:
var eventObj = null;
document.getElementById('btn').onclick = function() {
eventObj = window.event;
setTimeout(function() {
alert(typeof eventObj.srcElement);
}, 1000)
}​
EIDT 1:
i have search some test done by other ,see the below:
HTML :
<button id='btn1'>Click 1 !!</button>
<button id="btn2">Click 2 !!</button>
JS
var btn1EventObj = null;
document.getElementById('btn1').onclick = function() {
btn1EventObj = window.event;
alert(btn1EventObj.srcElement.id);
}
document.getElementById('btn2').onclick = function() {
alert(btn1EventObj === window.event); // output:false;
alert(btn1EventObj.srcElement === window.event.srcElement); // output: true ;
alert(btn1EventObj.srcElement.id); // output: btn2 ;
}
when the btn1 has been click i assume i cache the 'event object' in the btn1EventObj,and then click the btn2:
test:
btn1EventObj === window.event -> false; // there is not only one event object in MSIE
btn1EventObj.srcElement === window.event.srcElement -> true // i can not understand this one the the below.
btn1EventObj.srcElement.id ->btn2
see the fiddle
so all the above tell me that maybe all the event raised in MSIE are all share attributes ,and when the btn2 is clicked,all the previous attribute are overwrite by the later one?
am i kind of right ?
You can't copy window.event out of context of the actual event in IE. In other words: there is no existing event when you assign the handler in the script. If you want to refer to the window.event, the handler has to be assigned inline.
MSDN:
The event object is available only during an event—that is,
you can use it in event handlers but not in other code.
HTML:
<button id='btn' onclick="clicker(event);">Click!!!</button>
and JS:
function clicker(e){
setTimeout(function (){
alert(e.srcElement);
},1000);
return;
}
event in MSDN.
the `window.event' is a global object and may be changed on each event that raised. many events raised on browser within miliseconds.
NOTE:
if you set var y = someObject the object was not copied into y variable, the y variable just contains the address of the object in memory(you can google about: object reference and pointers ).so you need to copy object into y , or try alternative solution below:
so you need to use function(e){} style event handlers:
var eventObj = null;
document.getElementById('btn').onclick = function(e) {
eventObj = e;
setTimeout(function() {
alert(typeof eventObj.srcElement);
}, 1000)
}
see this on jsfiddle
EDIT 1:
in M$ IE you can use this code:
function copyObject(o){
return {srcElement: o.srcElement,
...
more attributes ...
};
}
var eventObj = null;
document.getElementById('btn').onclick = function(e) {
eventObj = copyObject(window.event);
setTimeout(function() {
alert(typeof eventObj.srcElement);
}, 1000)
}
see on jsfiddle(Edited)
var eventObj = null;
document.getElementById('btn').onclick = function() {
eventObj = window.event;
setTimeout((function(eventObj) {
alert(typeof eventObj.srcElement);
})(eventObj), 1000)
}
DEMO.
The event object is available only during an event-that is, you can use it in event handlers but not in other code and you are using setTimeout so it's not available after the event has been executed because to avoid conflict IE set it to null and after 1 second it's not available, so you can use a closure.

Categories