Scope of this in Click Handler - javascript

I have something similar to the following but I get the error: "this._toggle is not a function"
function handler(){
this._toggle();
}
SelectFX.prototype._toggle = function(){
...
this.removeEventListener('click', handler);
}
this.addEventListener('click', handler);
which I'm guessing is to do with the scope that addEventListener creates,
I would of thought then that adding this to a variable would fix but this code:
var self = this;
function handler(){
self._toggle();
}
SelectFX.prototype._toggle = function(){
...
this.removeEventListener('click', handler);
}
this.addEventListener('click', handler);
But the above gives the error "Cannot read property '_toggle ' of undefined"
If I use an anonymous function like below for the click handler it works fine but I need to Remove the Click Event later on, please help
SelectFX.prototype._toggle = function(){
...
}
this.addEventListener('click', function(){
this._toggle(); //Works fine but I need to remove this addEventListener later on
});
I've create a Gist here with the Full plugin https://gist.github.com/grassed/ce76d9b2a5fa6ab9e5be which centers around this.selPlaceholder.addEventListener( 'click', clickHandler);

You can use the native bind function to pass the object that should represent this in the function called. You can use that also for event listeners. In the example below, I pass in someObject to be this when someListener is being called.
var el = document.getElementById("clickable-span");
var someObject = {
clickCount: 0,
someListener: function(){
this.clickCount++;
this.showCount();
el.innerText = 'Click me again';
},
showCount: function(){
document.getElementById('target-span').innerText = 'You clicked ' + this.clickCount + ' time(s)';
}
}
// use bind to pass in what `this` should refer to in the someListener method
// we want `this` to point to someObject so we can use the clickcount and such
el.addEventListener(
"click",
someObject.someListener.bind(someObject)
);
<button id="clickable-span">Click me</button>
<hr />
<span id="target-span"></span>

Related

Pass parameter to bound function

I am running into an issue where I want to pass an 'event' parameter to a function which is being called from a JQuery eventListener.
$('#todoRemove').on('click', this.removeTask(event));
This immediately calls the function when the page is loaded, then does not work when pressing the button which would kick off the event. What can I change to make it so it calls the method in the prototype but passes the event parameter?
TaskCtrlr.prototype = {
init: function () {
this.setupEventHandlers();
},
setupEventHandlers: function () {
$('#addTask').on('click', this.addTask.bind(this));
$('#todoRemove').on('click', this.removeTask.bind(this));
/* $('#todoComplete').on('click', this.completeTask.bind(this));
$('#doneRemove').on('click', this.removeTask.bind(this));*/
},
addTask: function () {
let taskInput = this.view.getTaskInput();
let newTask;
if (this.model.tasks.todo.length == 0) {
newTask = new Task(0, taskInput.title, taskInput.desc, false);
} else {
let id = this.model.tasks.todo[this.model.tasks.todo.length - 1].id + 1;
newTask = new Task(id, taskInput.title, taskInput.desc, false);
}
this.model.addTask(newTask);
this.view.addTodoTask(newTask);
},
completeTask: function (event) {
console.log('wwwwww');
console.log(event.target.id);
},
removeTask: function (event) {
console.log('eeeeee');
console.log(event.target.id);
}
};
EDIT: CURRENT SOLUTION
$('#todoRemove').on('click', event, removeTask);
ERROR:
jQuery.Deferred exception: removeTask is not defined ReferenceError:
removeTask is not defined
Why do you want to pass event? What does it even refer to?
The event object is passed by the caller of the event handler, which is jQuery. You should do exactly the same as you do for the other handlers:
$('#todoRemove').on('click', this.removeTask.bind(this));
jQuery will pass the event object to the function without you having to do anything.
This immediately calls the function when the page is loaded
$('#todoRemove').on('click', this.removeTask(event));
Yes, it will call it because during registering a call back, you are not really registering a callback but calling your function using this code:
this.removeTask(event)
Instead you need to do this. I am not sure what event is but the 2nd argument you can use to pass something to the callback:
$('#todoRemove').on('click', event, removeTask);
And you can define removeTask like this:
function removeTask( event ) {
//...
}
Here is an example you can play with to get comfortable:
function greet( event ) {
alert( "Hello " + event.data.name );
}
$( "button" ).on( "click", {
name: "Karl"
}, greet );
If you do not pass anything, jQuery will still pass the a parameter to you which contains the event info as shown below:
function greet2( event ) {
alert( "Hello " + event.target.id );
}
$( "button" ).on( "click", greet2 );
There is more info here.
<== Fiddle Me ==>

Unbinding a single event from within a prototype structure

I am trying to unbind an event handler that has been added to an object's prototype. The (cut-down) code in question is:
MyClass.prototype.bindEvents = function() {
var thisObj = this;
this.$tabs.on("click", function(e) {
return thisObj.handleTabClick($(this), e);
});
}
MyClass.prototype.unbindEvents = function() {
this.$tabs.off("click", this.handleTabClick);
}
MyClass.prototype.handleTabClick = function($tab, e) {
// do something
}
I know that I can (and did) complete clear the click event by doing
this.$tabs.off("click");
but there is another event handler on there which I wish to keep.
How do I unbind a single event within the prototype structure?
You can add a namespace to the event when you create it which you can then specifically reference when you remove the event handler. try this:
MyClass.prototype.bindEvents = function() {
var thisObj = this;
this.$tabs.on("click.foo", function(e) {
return thisObj.handleTabClick($(this), e);
});
}
MyClass.prototype.unbindEvents = function() {
this.$tabs.off("click.foo");
}
For more information see the 'Event names and namespaces' section of http://api.jquery.com/on/
Also note that your method of passing the click handler through an anonymous function to the handleTabClick function is redundant, you can simply do this:
this.$tabs.on("click.foo", thisObj.handleTabClick);
MyClass.prototype.handleTabClick = function(e) {
var $tab = $(this);
// do something
}

jQuery off() is not unbinding events when using bind

function bubble(content, triggerElm){
this.element = $('<div class="bubble" />').html(content);
this.element.css(.....) // here is positioned based on triggerElm
}
bubble.prototype.show = function(){
$(document).on('click', this._click.bind(this));
this.element.css(....)
};
bubble.prototype.hide = function(){
$(document).off('click', this._click.bind(this));
this.element.css(....)
};
bubble.prototype._click = function(event){
console.log('click', this);
if(this.element.is(event.target) || (this.element.has(event.target).length > 0))
return true;
this.hide();
};
var b = new bubble();
b.show();
b.hide();
I keep seeing click in the console, so the click does not unbind.
But if I remove the bind() call the click is unbinded. Does anyone know why? I need a way to be able to change "this" inside my test function, that's why I'm using bind().
The problem is that this._click.bind() creates a new function every time it's called. In order to detach a specific event handler, you need to pass in the original function that was used to create the event handler and that's not happening here, so the handler is not removed.
If there are only going to be a few bubbles in your app, you could and simply not use this. That will remove a lot of the confusion about what this is referring to and ensure that each bubble retains a reference to its own click function that can be used to remove the event as needed:
function bubble(content, triggerElm) {
var element = $('<div class="bubble" />').html(content);
element.css(.....); // here is positioned based on triggerElm
function click(event) {
console.log('click', element);
if (element.is(event.target) ||
element.has(event.target).length > 0) {
return true;
}
hide();
}
function show() {
$(document).on('click', click);
element.css(....);
}
function hide() {
$(document).off('click', click);
element.css(....);
}
return {
show: show,
hide: hide
};
}
var b1 = bubble(..., ...);
b1.show();
var b2 = bubble(..., ...);
b2.show();
See how this frees you from using contrivances like .bind() and underscore-prefixed methods.
One option would be to namespace the event:
$(document).on('click.name', test.bind(this));
$(document).off('click.name');
Example Here
try use jQuery's proxy to get a unique reference of your function.
In this way, when you call $.proxy(test, this), it will check if this function has already been referenced before. If yes, proxy will return you that reference, otherwise it will create one and return it to you. So that, you can always get your original function, rather than create it over and over again (like using bind).
Therefore, when you call off(), and pass it the reference of your test function, off() will remove your function from click event.
And also, your test function should be declared before use it.
var test = function(){
console.log('click');
};
$(document).on('click', $.proxy(test, this));
$(document).off('click', $.proxy(test, this));
http://jsfiddle.net/aw50yj7f/
Please read https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
bind creates a new function therefore doing $(document).on('click', test.bind(this)); is like $(document).on('click', function(){}); and each time you execute it you invoke a new anonymous function thus you dont have a reference to unbind.
If you would do something like:
var test = function(){
console.log('click');
};
var newFunct = test.bind(this);
$(document).on('click', newFunct );
$(document).off('click', newFunct );
It should work fine
e.g: http://jsfiddle.net/508dr0hv/
Also - using bind is not recommended, its slow and not supported in some browsers.
rather than binding this to the event, send this as a parameter:
$("#DOM").on("click",{
'_this':this
},myFun);
myFun(e){
console.info(e.data._this);
$("#DOM").off("click",myFun);
}

Setting the 'this' context of an anonymous function within a JQuery Method

I want to pass a specific context the the following click bound method:
$(document).ready(function(){
var o = {
name : 'I\'m object O'
};
$('#my-button').click(function(){
alert(this.name);
}.call(o));
});
The problem is that this gets triggered as soon as the page loads. Is there a way I can bind a context to the anonymous function of the click handler and have it trigger only when the click event is fired? Also can you explain why its been triggered on the page load rather than the click event.
Use $.proxy
$("#my-button").click($.proxy(function () {
alert(this.name);
}, o));
It's not working because you are calling this anonymous function at the time of it's declaration. Try this instead:
$(document).ready(function(){
var o = {
name : 'I\'m object O'
};
$('#my-button').click(function(){
function(){
alert(this.name);
}.call(o)
});
/*
* Or shorter:
* $('#my-button').click(function(){
* alert(o.name);
* });
*/
});
$(document).mousedown(function(e){
//Do stuff
}

Disabling click event handlers

I am trying to basically disable the click event on a <div> temporarily.
I have tried the following (preview):
$('hello').observe('click', function (e) {
e.stop();
});
$('hello').observe('click', function (e) {
alert('click not stopped!');
});
However, when #hello is clicked, the alert box still appears. I do not want the second attached handler to be called, and I do not want to change the second handler.
I will also accept a solution such as:
$('hello').observe('click', function (e) {
alert('click not stopped!');
});
$('hello').disableEvent('click');
// Now, handler won't be called
$('hello').observe('click', function (e) {
alert('click not stopped (2)!');
});
// New handler won't be called, either
$('hello').enableEvent('click');
// Now, handler will be called
I am using the Prototype.js framework. This doesn't seem to be a browser-specific issue.
When you assign handlers to events; you are basically just storing a set of functions to be executed when an event fires.
When an event fires, the handlers you've added are executed in the order they we're added. So if you we're to add three handlers to a div's click-event:
$("div").observe("click", function ()
{
alert("one");
});
$("div").observe("click", function ()
{
alert("two");
});
$("div").observe("click", function ()
{
alert("three");
});
.. you would get three alerts ("one", "two" and "three") when the click event of the div element fires. Those three alerts will still get shown, if you put in:
$("div").observe("click", function (e)
{
e.stop();
})
.. because you are only canceling the event for one particular handler. Not all associated handlers.
So what you will need to do is use a reference variable, which keeps track of wether the click event is allowed to fire:
var cancelClickEvent = true;
$("div").observe("click", function ()
{
// if cancelClickEvent is true, return the function early, to
// stop the code underneath from getting executed
if (cancelClickEvent) return;
// your code goes here
});
You will then need to implement the above if-clause in all your handlers.
Can't you just set the object's disabled property to true?
As I said in comments to roosteronacid's answer, I wrote an extension to Event.observe. Works in most browsers, but not IE.
// XXX HACK XXX
(function () {
var handlerCache = $A([ ]);
function findHandler(either) {
var pair = handlerCache.find(function (pair) {
return $A(pair).member(either);
});
return pair && pair[0];
}
function addHandler(handler) {
function newHandler(e) {
if (!e.halted) {
handler.apply(this, arguments);
}
}
handlerCache.push([ handler, newHandler ]);
return newHandler;
}
Event.observe = Event.observe.extended(function ($super, element, eventName, handler) {
handler = findHandler(handler) || addHandler(handler);
$super(element, eventName, handler);
});
Event.stopObserving = Event.stopObserving.extended(function ($super, element, eventName, handler) {
handler = findHandler(handler) || handler;
$super(element, eventName, handler);
});
Element.addMethods({
observe: Event.observe
});
Event.prototype.halt = function () {
this.halted = true;
};
}());
Note: Function.prototype.extended is a custom function which puts the original Event.observe in as $super.

Categories

Resources