I'm trying to create a custom function that unbinds and then binds an event. It looks like this:
App.bindEvent = function(selector, eventType, eventHandler) {
$(selector).unbind(eventType);
$(selector).bind(eventType, function(event) {
eventHandler(event);
});
};
However, the problem I am facing is that I cannot use the this keyword to reference the DOM element that was clicked. For example, I cannot do this:
App.bindEvent("#my-element", "click", function() {
var myId = $(this).attr("data-my-id");
});
How would I go about getting the this keyword to point to the clicked DOM element like it does in jQuery.bind()?
Thanks for any help.
Change:
eventHandler(event);
To:
eventHandler.call(this, event);
That'll change the "scope" of your function to be the same as the scope of the original "bind" call.
How about this instead:
App.bindEvent = function(selector, eventType, eventHandler) {
var element = this;
$(selector).unbind(eventType);
$(selector).bind(eventType, function(event) {
eventHandler.call(element, event);
});
};
You need to call the handler in the context of the object:
eventHandler.call(this, event);
I think you're trying to refer to
event.target
For example:
App.bindEvent("#my-element", "click", function(event) {
var myId = $(event.target).attr("data-my-id");
});
check out jquery's event documentation
Related
I am trying to use .one() to bind to an event with the name similar to something.else.thing, I am not able to change the event name since it comes from an external library.
The problem is because of the periods, jQuery creates namespaces, else and thing for the event something instead of creating an event named something.else.thing.
Is there any way around this?
Thanks
Edit:
Some example code:
$(document).on('appfeel.cordova.admob.onAdLoaded', function() {
console.log('Does nothing');
});
document.addEventListener('appfeel.cordova.admob.onAdLoaded', function() {
console.log('Works');
});
I don't think you can disable jQuery event namespacing so if you want to use one on an event with dots in it you can just do this in pure JS:
Fiddle: http://jsfiddle.net/AtheistP3ace/8z6ewwnv/1/
HTML:
<div id="test"></div>
<button id="mybutton">Run event again</button>
JS:
var test = document.getElementById('test');
var button = document.getElementById('mybutton');
var event = new Event('something.else.blah');
function onWeirdEvent () {
test.removeEventListener('something.else.blah', onWeirdEvent);
alert('did it');
}
test.addEventListener('something.else.blah', onWeirdEvent, false);
test.dispatchEvent(event);
button.addEventListener('click', function (e) {
test.dispatchEvent(event);
}, false);
Its essentially the same thing. If you really want everything to seem jQuery-ish create a custom plugin:
Fiddle: http://jsfiddle.net/AtheistP3ace/8z6ewwnv/2/
$.fn.customOne = function (eventString, fn) {
var self = this[0];
var origFn = fn;
fn = function (event) {
self.removeEventListener(eventString, fn);
return origFn.apply(self);
};
self.addEventListener(eventString, fn, false);
};
$.fn.customTrigger = function (eventString) {
var event = new Event(eventString);
var self = this[0];
self.dispatchEvent(event);
}
$('#test').customOne('something.else.blah', function () {
alert('did it');
});
$('#test').customTrigger('something.else.blah');
$('#test').customTrigger('something.else.blah');
Here is how I decided to go about solving this issue. I went about it this way because this way allows me to continue to use jQuery and all the functionality it provides while keeping my code consistent and only requires a few lines of extra code to go about.
$(document).one('somethingElseThing', function() {
console.log('Event!');
});
document.addEventListener('something.else.thing', function() {
$(document).trigger('somethingElseThing');
});
What I am doing is using straight JavaScript to create an event listener for the event with a period in the name and then I have it trigger a custom event that doesn't a have a period so that I can continue to use jQuery. This I believe is an easy straightforward approach.
How does one, through jQuery, get the ID of an element that is being clicked on and then pass it as a parameter into a function? Example jQuery code below.
jQuery(document).ready(function() {
var id = this_id;
jQuery(".lightbox a").click({param: id}, functionName);
});
May I note that the "param" parameter is integral to the structure of the function.
Apologies all, I am no Javascript master by any means.
I'm guessing the point is to pass event data to a function that expects that, as ,click() supports the .click( [eventData ], handler(eventObject) ) syntax, and if so, you have to iterate the collection yourself:
jQuery(document).ready(function($) {
$(".lightbox a").each(function() {
$(this).click({param: this.id}, functionName);
});
});
EDIT:
You could do this with on() as well:
jQuery(document).ready(function($) {
$(".lightbox a").each(function() {
$(this).on('click', {param: this.id}, functionName);
});
});
FIDDLE
Within the click handler, you can access the element ID with this.id or $(this).attr('id'):
jQuery(document).ready(function() {
jQuery(".lightbox a").click(function(){
functionName(this.id);
});
});
You can use this.id inside a click event, example:
jQuery(".lightbox a").click(function() {
var id = this.id;
//pass to a function
testFunction(id);
});
function testFunction(param) {
console.log(param);
}
It's easy just access to the this element to get the clicked element, then extract its id and save it into a variable like this:
jQuery(".lightbox a").click(function(){
var id = jQuery(this).attr("id");
callFunction(id);
});
http://jsfiddle.net/pArW6/
jQuery(document).ready(function() {
jQuery(".lightbox a").click(functionName);
});
function functionName()
{
alert(this.id);
}
You can you Use $(this).att("id").
$(".lightbox a").click(function() {
var ID=$(this).att("id");
//pass to a function
TestFunction(ID);
});
function TestFunction(P) {
console.log(P);
}
Live example
http://jsbin.com/enobop/1/edit
You can do this:
jQuery(document).ready(function () {
jQuery(".lightbox a").click(function (e) {
// Cancel the default action (navigation) of the click.
e.preventDefault();
// 'this' here refers to the link being clicked in the current scope
// you can check the console for the id for debug purpose
console.log(this.id);
// pass the id to the function
functionName(this.id);
});
});
Another way is to use the event parameter that gets passed to the callback function.
jQuery(".lightbox a").click(function(ev) {
console.log(ev.target.id);
}
Of course it's a mix of jQuery and pure JS.
Usually you have a function for an event declared with
function(event)
and the event has a target and the id of the target is, what you want. So
$("SomeElement").on("click", function(e){ callanotherFunction(e.target.id) })
does, what you wanted
You can use this.id or $(this).attr("id");, but you might want to get a reference to $(this) - wrapped or not - immediately and work from a variable if you do much of anything else in there.
I have a bunch of elements that get three different classes: neutral, markedV and markedX. When a user clicks one of these elements, the classes toggle once: neutral -> markedV -> markedX -> neutral. Every click will switch the class and execute a function.
$(document).ready(function(){
$(".neutral").click(function markV(event) {
alert("Good!");
$(this).addClass("markedV").removeClass("neutral");
$(this).unbind("click");
$(this).click(markX(event));
});
$(".markedV").click(function markX(event) {
alert("Bad!");
$(this).addClass("markedX").removeClass("markedV");
$(this).unbind("click");
$(this).click(neutral(event));
});
$(".markedX").click(function neutral(event) {
alert("Ok!");
$(this).addClass("neutral").removeClass("markedX");
$(this).unbind("click");
$(this).click(markV(event));
});
});
But obviously this doesn't work. I think I have three obstacles:
How to properly bind the changing element to the already defined function, sometimes before it's actually defined?
How to make sure to pass the event to the newly bound function [I guess it's NOT accomplished by sending 'event' to the function like in markX(event)]
The whole thing looks repetitive, the only thing that's changing is the alert action (Though each function will act differently, not necessarily alert). Is there a more elegant solution to this?
There's no need to constantly bind and unbind the event handler.
You should have one handler for all these options:
$(document).ready(function() {
var classes = ['neutral', 'markedV', 'markedX'],
methods = {
neutral: function (e) { alert('Good!') },
markedV: function (e) { alert('Bad!') },
markedX: function (e) { alert('Ok!') },
};
$( '.' + classes.join(',.') ).click(function (e) {
var $this = $(this);
$.each(classes, function (i, v) {
if ( $this.hasClass(v) ) {
methods[v].call(this, e);
$this.removeClass(v).addClass( classes[i + 1] || classes[0] );
return false;
}
});
});
});
Here's the fiddle: http://jsfiddle.net/m3CyX/
For such cases you need to attach the event to a higher parent and Delegate the event .
Remember that events are attached to the Elements and not to the classes.
Try this approach
$(document).ready(function () {
$(document).on('click', function (e) {
var $target = e.target;
if ($target.hasClass('markedV')) {
alert("Good!");
$target.addClass("markedV").removeClass("neutral");
} else if ($target.hasClass('markedV')) {
alert("Bad!");
$target.addClass("markedX").removeClass("markedV");
} else if ($target.hasClass('markedX')) {
alert("Ok!");
$target.addClass("neutral").removeClass("markedX");
}
});
});
OR as #Bergi Suggested
$(document).ready(function () {
$(document).on('click', 'markedV',function (e) {
alert("Good!");
$(this).addClass("markedV").removeClass("neutral");
});
$(document).on('click', 'markedX',function (e) {
alert("Bad!");
$(this).addClass("markedX").removeClass("markedV");
});
$(document).on('click', 'neutral',function (e) {
alert("Ok!");
$(this).addClass("neutral").removeClass("markedX");
});
});
Here document can be replaced with any static parent container..
How to properly bind the changing element to the already defined function, sometimes before it's actually defined?
You don't bind elements to functions, you bind handler functions to events on elements. You can't use a function before it is defined (yet you might use a function above the location in the code where it was declared - called "hoisting").
How to make sure to pass the event to the newly bound function [I guess it's NOT accomplished by sending 'event' to the function like in markX(event)]
That is what happens implicitly when the handler is called. You only need to pass the function - do not call it! Yet your problem is that you cannot access the named function expressions from outside.
The whole thing looks repetitive, the only thing that's changing is the alert action (Though each function will act differently, not necessarily alert). Is there a more elegant solution to this?
Yes. Use only one handler, and decide dynamically what to do in the current state. Do not steadily bind and unbind handlers. Or use event delegation.
I would like a cross modern browser way to take a mouse event from one html element and pass it on to another.
eg.
el1.addEventListener('mousemove', function(e) {
el2.trigger('mousemove', e);
});
el2.addEventListener('mousemove', function(e) {
//THIS SHOULD BE CALLED WHEN EITHER el1
});
a jquery solution is ok but I would prefer a non-jquery solution. Is this simple?
Here is the correct code
var el1 = document.getElementById('el1');
var el2 = document.getElementById('el2');
el1.onmousemove = function(e) {
alert('el1 event');
el2.onmousemove(e);
};
el2.onmousemove = function(e) {
alert('el2 event');
};
demo
This is good if you want the event argument e to pass over to el2's event. This updated demo shows mouse position being passed over.
native should work like that
var evt;
el1.onmousemove = function() {
evt = document.createEvent('MouseEvent');
evt.initEvent('mousemove', true, true);
el2.dispatchEvent(evt);
}
You can read up on element.dispatchEvent here:
https://developer.mozilla.org/en-US/docs/DOM/element.dispatchEvent
If you accept a jQuery answer then here's the code:
// el1 and el2 are the jQuery objects of the DOM elements
el1.mousemove(function (event) {
el2.mousemove(event);
});
In pure javascript:
el1.onmousemove = function(e) {
el2.onmousemove('mousemove', e);
};
el2.onmousemove = function(e) {
};
el1.addEventListener('mousemove', handler, false);
el2.addEventListener('mousemove', handler2, false);
function handler(e) {
// do some stuff
handler2.call(el2, e); // pass el2 as this reference, event as the argument.
};
not too sure if this is what you are looking for, just name the event handlers and trigger the one off the other.
if you do not need the this reference in the second handler, use handler2(e); instead.
further readings:
Function.prototype.call
Here is a half-baked demo passing the mouse event args. I'm unsure how well supported layerX/Y is, I just used it to show the demo.
I'm trying to figure out how I can wrap the click event. Eg this is the code I want.
$("#test").aclick(function() {
alert('hi');
});
The only thing that aclick does is use the e.preventDefault automatically.
Is it something like?
$.fn.aclick = function(e) {
e.preventDefault();
return $.fn.click.apply(this, arguments);
});
Thanks.
When binding a handler, the event object e doesn't exist yet. You need to create a default event handler that calls the supplied one:
$.fn.aclick = function (handler) {
return this.each(function () {
var el = this;
$(el).click(function (e) {
e.preventDefault();
handler.call(el, e);
});
});
};