I'm trying to do a specific action on the clicked element if an argument is passed in the on click method in jQuery. When I try to access this it's referencing the entire window instead of the clicked on element. How would I access the clicked on element in the handler?
Here's the code I'm using:
var myfunction = function(action) {
var content;
var $this = $(this);
if(action === "one") {
$(".output").text("clicked on one");
$this.addClass("one");
}
if(action === "two") {
$(".output").text("clicked on two");
$this.addClass("two");
}
};
$("#button").on("click", function(event) {
myfunction("one");
});
$("#button2").on("click", function(event) {
myfunction("two");
});
I set up an example on jsbin here. Any help would be appreciated.
There are several ways to do this.
JQUERY WAY:
Within your jquery click event handlers you have the event object. It has a property called target which is what you're looking for.
Change this: $this.addClass("one");
To this: $(event.target).addClass("one");
You can also do this: event.target.className = "one"
And do for "two" as well obviously...
VANILLA WAY:
You can just pass in an extra argument representing your clicked element.
var myfunction = function(action, element) {
var content;
if(action === "one") {
$(".output").text("clicked on one");
$(element).addClass("one");
// or event.target.className = "one"
}
if(action === "two") {
$(".output").text("clicked on two");
$(element).addClass("two");
// or event.target.className = "two"
}
};
$("#button").on("click", function(event) {
myfunction("one", this);
});
$("#button2").on("click", function(event) {
myfunction("two", this);
});
You can use Function.prototype.call:
$("#button2").on("click", function(event) {
myfunction.call(this, "two");
});
or store the action as an attribute on your element, bind your handler directly and query the attribute.
var myfunction = function() {
var content;
var $this = $(this);
var action = $this.attr('data-action');
if (action === "one") {
$(".output").text("clicked on one");
$this.addClass("one");
} else if (action === "two") {
$(".output").text("clicked on two");
$this.addClass("two");
}
};
$("#button2").on("click", myfunction);
this refers to the object the function belongs to, in your case the function belongs to the window object or global object, the 'this' keyword behaves differently depending on how you use your function, if you use it as a constructor function for example (with the new keyword) 'this' will be bound to new object being constructed, and when the function is used as an event handler this will be set to the event the element the event fired from.
see :https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this for more information.
you need to change your code and do something like this:
$(".button").on("click",function(){
var $this = $(this) //refers to the event it was fired from (button object)
$(".output").text("You clicked on "+$this.text());
});
i used classes instead of ids to target any button that's clicked
an example in jsfiddle: http://jsfiddle.net/fvacbd9u/
Related
I'm trying to remove an event listener inside of a listener definition:
canvas.addEventListener('click', function(event) {
click++;
if(click == 50) {
// remove this event listener here!
}
// More code here ...
How could I do that? this = event...
You need to use named functions.
Also, the click variable needs to be outside the handler to increment.
var click_count = 0;
function myClick(event) {
click_count++;
if(click_count == 50) {
// to remove
canvas.removeEventListener('click', myClick);
}
}
// to add
canvas.addEventListener('click', myClick);
EDIT: You could close around the click_counter variable like this:
var myClick = (function( click_count ) {
var handler = function(event) {
click_count++;
if(click_count == 50) {
// to remove
canvas.removeEventListener('click', handler);
}
};
return handler;
})( 0 );
// to add
canvas.addEventListener('click', myClick);
This way you can increment the counter across several elements.
If you don't want that, and want each one to have its own counter, then do this:
var myClick = function( click_count ) {
var handler = function(event) {
click_count++;
if(click_count == 50) {
// to remove
canvas.removeEventListener('click', handler);
}
};
return handler;
};
// to add
canvas.addEventListener('click', myClick( 0 ));
EDIT: I had forgotten to name the handler being returned in the last two versions. Fixed.
canvas.addEventListener('click', function(event) {
click++;
if(click == 50) {
this.removeEventListener('click',arguments.callee,false);
}
Should do it.
You could use a named function expression (in this case the function is named abc), like so:
let click = 0;
canvas.addEventListener('click', function abc(event) {
click++;
if (click >= 50) {
// remove event listener function `abc`
canvas.removeEventListener('click', abc);
}
// More code here ...
}
Quick and dirty working example: http://jsfiddle.net/8qvdmLz5/2/.
More information about named function expressions: http://kangax.github.io/nfe/.
If #Cybernate's solution doesn't work, try breaking the trigger off in to it's own function so you can reference it.
clickHandler = function(event){
if (click++ == 49)
canvas.removeEventListener('click',clickHandler);
}
canvas.addEventListener('click',clickHandler);
element.querySelector('.addDoor').onEvent('click', function (e) { });
element.querySelector('.addDoor').removeListeners();
HTMLElement.prototype.onEvent = function (eventType, callBack, useCapture) {
this.addEventListener(eventType, callBack, useCapture);
if (!this.myListeners) {
this.myListeners = [];
};
this.myListeners.push({ eType: eventType, callBack: callBack });
return this;
};
HTMLElement.prototype.removeListeners = function () {
if (this.myListeners) {
for (var i = 0; i < this.myListeners.length; i++) {
this.removeEventListener(this.myListeners[i].eType, this.myListeners[i].callBack);
};
delete this.myListeners;
};
};
It looks like no one's covered the part of the current JavaScript DOM specification that 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 to control event listening:
{
type (a string)
callback (null or an EventListener object)
capture (a boolean, initially false)
passive (a boolean, initially false)
once (a boolean, initially false)
signal (null or an AbortSignal object)
removed (a boolean for bookkeeping purposes, initially false)
}
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 tell the JS engine to remove an event listener by just calling abort() instead of having to bother with removeEventListener:
const canvasListener = (new AbortController()).signal;
canvas.addEventListener('click', () => {
click++;
if (click === 50) {
canvasListener.abort();
} else {
doSomethingWith(click);
}
}, {
signal: canvasListener
});
(Note that this does not use the useCapture flag, because the useCapture flag is essentially completely useless)
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 properties as we called addEventListener: we just cancel the listener.
I think you may need to define the handler function ahead of time, like so:
var myHandler = function(event) {
click++;
if(click == 50) {
this.removeEventListener('click', myHandler);
}
}
canvas.addEventListener('click', myHandler);
This will allow you to remove the handler by name from within itself.
If someone uses jquery, he can do it like this :
var click_count = 0;
$( "canvas" ).bind( "click", function( event ) {
//do whatever you want
click_count++;
if ( click_count == 50 ) {
//remove the event
$( this ).unbind( event );
}
});
Hope that it can help someone.
Note that the answer given by #user113716 work nicely :)
A way to achieve that is use jquery, so you can use:
canvas.click(yourfunction);
then you can detach all event listener with:
canvas.off();
Try this, it worked for me.
<button id="btn">Click</button>
<script>
console.log(btn)
let f;
btn.addEventListener('click', f=function(event) {
console.log('Click')
console.log(f)
this.removeEventListener('click',f)
console.log('Event removed')
})
</script>
I'm trying to remove an event listener inside of a listener definition:
canvas.addEventListener('click', function(event) {
click++;
if(click == 50) {
// remove this event listener here!
}
// More code here ...
How could I do that? this = event...
You need to use named functions.
Also, the click variable needs to be outside the handler to increment.
var click_count = 0;
function myClick(event) {
click_count++;
if(click_count == 50) {
// to remove
canvas.removeEventListener('click', myClick);
}
}
// to add
canvas.addEventListener('click', myClick);
EDIT: You could close around the click_counter variable like this:
var myClick = (function( click_count ) {
var handler = function(event) {
click_count++;
if(click_count == 50) {
// to remove
canvas.removeEventListener('click', handler);
}
};
return handler;
})( 0 );
// to add
canvas.addEventListener('click', myClick);
This way you can increment the counter across several elements.
If you don't want that, and want each one to have its own counter, then do this:
var myClick = function( click_count ) {
var handler = function(event) {
click_count++;
if(click_count == 50) {
// to remove
canvas.removeEventListener('click', handler);
}
};
return handler;
};
// to add
canvas.addEventListener('click', myClick( 0 ));
EDIT: I had forgotten to name the handler being returned in the last two versions. Fixed.
canvas.addEventListener('click', function(event) {
click++;
if(click == 50) {
this.removeEventListener('click',arguments.callee,false);
}
Should do it.
You could use a named function expression (in this case the function is named abc), like so:
let click = 0;
canvas.addEventListener('click', function abc(event) {
click++;
if (click >= 50) {
// remove event listener function `abc`
canvas.removeEventListener('click', abc);
}
// More code here ...
}
Quick and dirty working example: http://jsfiddle.net/8qvdmLz5/2/.
More information about named function expressions: http://kangax.github.io/nfe/.
If #Cybernate's solution doesn't work, try breaking the trigger off in to it's own function so you can reference it.
clickHandler = function(event){
if (click++ == 49)
canvas.removeEventListener('click',clickHandler);
}
canvas.addEventListener('click',clickHandler);
element.querySelector('.addDoor').onEvent('click', function (e) { });
element.querySelector('.addDoor').removeListeners();
HTMLElement.prototype.onEvent = function (eventType, callBack, useCapture) {
this.addEventListener(eventType, callBack, useCapture);
if (!this.myListeners) {
this.myListeners = [];
};
this.myListeners.push({ eType: eventType, callBack: callBack });
return this;
};
HTMLElement.prototype.removeListeners = function () {
if (this.myListeners) {
for (var i = 0; i < this.myListeners.length; i++) {
this.removeEventListener(this.myListeners[i].eType, this.myListeners[i].callBack);
};
delete this.myListeners;
};
};
It looks like no one's covered the part of the current JavaScript DOM specification that 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 to control event listening:
{
type (a string)
callback (null or an EventListener object)
capture (a boolean, initially false)
passive (a boolean, initially false)
once (a boolean, initially false)
signal (null or an AbortSignal object)
removed (a boolean for bookkeeping purposes, initially false)
}
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 tell the JS engine to remove an event listener by just calling abort() instead of having to bother with removeEventListener:
const canvasListener = (new AbortController()).signal;
canvas.addEventListener('click', () => {
click++;
if (click === 50) {
canvasListener.abort();
} else {
doSomethingWith(click);
}
}, {
signal: canvasListener
});
(Note that this does not use the useCapture flag, because the useCapture flag is essentially completely useless)
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 properties as we called addEventListener: we just cancel the listener.
I think you may need to define the handler function ahead of time, like so:
var myHandler = function(event) {
click++;
if(click == 50) {
this.removeEventListener('click', myHandler);
}
}
canvas.addEventListener('click', myHandler);
This will allow you to remove the handler by name from within itself.
If someone uses jquery, he can do it like this :
var click_count = 0;
$( "canvas" ).bind( "click", function( event ) {
//do whatever you want
click_count++;
if ( click_count == 50 ) {
//remove the event
$( this ).unbind( event );
}
});
Hope that it can help someone.
Note that the answer given by #user113716 work nicely :)
A way to achieve that is use jquery, so you can use:
canvas.click(yourfunction);
then you can detach all event listener with:
canvas.off();
Try this, it worked for me.
<button id="btn">Click</button>
<script>
console.log(btn)
let f;
btn.addEventListener('click', f=function(event) {
console.log('Click')
console.log(f)
this.removeEventListener('click',f)
console.log('Event removed')
})
</script>
I'm building a decision tree in JavaScript. I do not have jQuery available to me for this project.
I would like to be able to have buttons, placed anywhere in the decision tree (Hidden or displayed anywhere on the page), with the same class name. The listener on the JS side would then run a function.
Here is what I am using for and ID based listener. It works well but I need to be able to have multiple buttons with the same class or name available. Although I have seen examples of this, I cannot get it to function properly.
function q1a1() {
var q1a1button = document.getElementById("q1answer1");
if(q1a1button.addEventListener){
q1a1button.addEventListener("click", function() { q1answer1();}, false);
} else if(q1a1button.attachEvent){
q1a1button.attachEvent("onclick", function() { q1answer1();});
}
};
if(window.addEventListener){
window.addEventListener("load", q1a1, false);
} else if(window.attachEvent){
window.attachEvent("onload", q1a1);
} else{
document.addEventListener("load", q1a1, false);
}
function q1answer1() {
//DO SOME STUFF
}
This also needs to work in as many versions of IE as possible. For single class handling I'm using querySelectorAll.
What you are really looking for is JavaScript Event Delegation. In your case, you have BUTTON elements, which I'm going to assume are <button> tags. Now you want to know when one of those buttons was clicked and then run a function:
if (document.addEventListener) {
document.addEventListener("click", handleClick, false);
}
else if (document.attachEvent) {
document.attachEvent("onclick", handleClick);
}
function handleClick(event) {
event = event || window.event;
event.target = event.target || event.srcElement;
var element = event.target;
// Climb up the document tree from the target of the event
while (element) {
if (element.nodeName === "BUTTON" && /foo/.test(element.className)) {
// The user clicked on a <button> or clicked on an element inside a <button>
// with a class name called "foo"
doSomething(element);
break;
}
element = element.parentNode;
}
}
function doSomething(button) {
// do something with button
}
Anywhere on the page that a <button class="foo">...</button> element appears, clicking it, or any HTML tag inside of it, will run the doSomething function.
Update: Since Event Delegation is used, only a single click handler is registered on the document object. If more <button>s are created as a result of an AJAX call, you don't have to register click handlers on those new <button>s since we take advantage of the click event bubbling up from the element the user clicked on to the document object itself.
If you don't have jquery:
if (document.body.addEventListener){
document.body.addEventListener('click',yourHandler,false);
}
else{
document.body.attachEvent('onclick',yourHandler);//for IE
}
function yourHandler(e){
e = e || window.event;
var target = e.target || e.srcElement;
if (target.className.match(/keyword/))
{
//an element with the keyword Class was clicked
}
}
If you use a cross browser library like jquery:
HTML:
<div class="myClass">sample</div>
<div class="myClass">sample 2</div>
JS:
function theFuncToCall(event){
//func code
}
$(document).on('click', '.myClass', theFuncToCall);
var buttons = document.querySelectorAll(".MyClassName");
var i = 0, length = buttons.length;
for (i; i < length; i++) {
if (document.addEventListener) {
buttons[i].addEventListener("click", function() {
// use keyword this to target clicked button
});
} else {
buttons[i].attachEvent("onclick", function() {
// use buttons[i] to target clicked button
});
};
};
This answer is a bit overkill, but it should show you ways you could structure your code in a "modern" way even if you're still targeting old browsers
Write code to add event listeners so there is minimal difference between new and old browsers
var listen = (function () { // will return the handler for use in unlisten
if (window.addEventHandler) {
return function (node, type, handler) {
node.addEventListener(type, handler);
return handler;
};
} else if (window.attachEvent) {
return function (node, type, handler) {
var fn = function (e) {
if (!e) {
e = window.event;
}
if (!e.target && e.srcElement) {
e.target = e.srcElement;
}
return handler.call(this, e);
};
node.attachEvent('on' + type, fn);
return fn;
};
} else {
throw new Error('Events not supported in this environment');
// or
// return function ... node['on' + type] = function () { ... };
}
}());
and if you'd like the reverse, too
var unlisten = (function () { // use handler given by listen
if (window.removeEventListener) {
return function (node, type, handler) {
node.removeEventListener(type, handler);
};
} else if (window.detachEvent) {
return function (node, type, handler) {
node.detachEvent('on' + type, handler);
};
} else {
throw new Error('Events not supported in this environment');
// or
// return function ... node['on' + type] = null;
}
}());
Write your click handler
function clickHandler(e) {
// do stuff
}
Wrap your click handler in a function to choose only clicks on buttons with the right class
function wrappedClickHandler(e) {
var tokens, i;
if (e.target.tagName !== 'INPUT' && e.target.tagName !== 'BUTTON') {
return;
}
tokens = (e.target.className || '').split(' ');
for (i = 0; i < tokens.length; ++i) {
if (tokens[i] === 'theClassTokenWeWant') {
return clickHandler.call(this, e);
// or
// return clickHandler.call(e.target, e);
}
}
}
Add this as a listener to a common ancestor node
var h = listen(document, 'click', wrappedClickHandler);
// .. later, if desired
unlisten(document, 'click', h);
Would the simpler way of writing the event delegation function be to add it to the container of the buttons? For example,
// Select Container Element
const questionContainer = document.querySelector(".container");
// Listen For Clicks Within Container
questionContainer.onclick = function (event) {
// Prevent default behavior of button
event.preventDefault();
// Store Target Element In Variable
const element = event.target;
// If Target Element Is a Button
if (element.nodeName === 'BUTTON') {
// Event Code
}
}
I have a list of elements with its event listeners like this :
var h1 = document.getElementById("h1"),
h2 = document.getElementById("h2"),
h3 = document.getElementById("h3");
var elemArray = [h1,h2,h3];
for(var i=0;i<elemArray.length;i++){
elemArray[i].addEventListener("click",mouseListener,false);
}
function mouseListener(e){
// code goes here
// whenever it's called i want to know from
// which element it's been called
}
Now what i want is whenever i click on one of the elements,
I want to know which one has been clicked without using different functions for each element.
Just use this variable inside your callback, it will point to the target element:
function mouseListener(e) {
console.log( this.id );
}
function mouseListener(e){
var elementEventWasSet = this;
var clickedEl = e.target; // in case of bubbling, this is the one that was clicked
// code goes here
// whenever it's called i want to know from
// which element it's been called
}
You can do something like this
function mouseListener(e){
if(this === h1){
console.log("First was pressed");
}else if(this === h2){
console.log("Second was pressed");
}else if(this === h3){
console.log("Third was pressed");
}
}
This will work only if h1,h2 and h3 are global variable.
You can see this approach in action on this site:
http://jsfiddle.net/94xMu/2/
Is there in jQuery any function what can return true or false if mouse entered into their child element?
I look to something what I can use like this:
if ( $(this).mouseIsOnChild() ) {
// mouse is on a child element
} else {
// mouse leaved branch of HTML tree
}
Of courese .mouseIsOnChild() function is not exists. Yet.
//track hover state
$('*').hover(function() {
$(this).data('hover', true);
}, function() {
$(this).data('hover', false);
});
//plugin to check whether mouse is on children
$.fn.mouseIsOnChild = function() {
var ret = false;
$(this).children().each(function() {
ret = $(this).data('hover');
return ret ? false : true;
});
return ret;
}
Inside the event handler - you can check if e.target (e is the argument passed in the event handler function) is the current element that triggered the event using .is()
if($(this).children('div').is(e.target)){
// replacing $(this).children('div') with the children element selector
}else{
}
FIDDLE
If you are looking not for a specific children but any children you can do something like this
if($('>*',this).is(e.target)){
// do something if it's children
}else{
}
FIDDLE
Yes. The mouseenter() and mouseleave() events exist.
Using this we can construct a data structure that checks for if a mouse is in a child's area:
$(function() {
var isIn = false;
$(target).children().mouseenter(function() {isIn = true;});
$(target).children().mouseleave(function() {isIn = false;});
var isMouseIn = function() { return isIn; };
});
now, your function isMouseIn() returns true if the mouse is inside the child, false otherwise.
If you wanted to get fancy, you could attach custom data to the target node instead of a global variable. I will leave that as an exercise for the motivated.