I have an Object and I want to bind a function to a button when Object was initialized.
var MyClass = {
Click: function() {
var a = MyClass; // <---------------------- look here
a.Start();
},
Bind: function() {
var a = document.getElementById('MyButton');
a.addEventListener('click', this.Click, false); //<---------- and here
},
Init: function() {
this.Bind();
}
}
So, I'm new at using it and I don't know if object can be declared like this (inside Click() function that should be done after clicking a button):
Is it a bad practise? Which could be the best way in this case when adding an event here?
Edit: fiddle
Firstly you have a syntax error. getElementsById() should be getElementById() - no s. Once you fix that, what you have will work, however note that it's not really a class but an object.
If you did want to create this as a class to maintain scope of the contained methods and variables, and also create new instances, you can do it like this:
var MyClass = function() {
var _this = this;
_this.click = function() {
_this.start();
};
_this.start = function() {
console.log('Start...');
}
_this.bind = function() {
var a = document.getElementById('MyButton');
a.addEventListener('click', this.click, false);
};
_this.init = function() {
_this.bind();
};
return _this;
}
new MyClass().init();
<button id="MyButton">Click me</button>
For event listeners it's easiest and best to use jQuery, for example if you want to have some .js code executed when user clicks on a button, you could use:
https://api.jquery.com/click/
I don't know how new you are to .js, but you should look up to codecademy tutorials on JavaScript and jQuery.
.click() demo:
https://www.w3schools.com/jquery/tryit.asp?filename=tryjquery_event_click
Related
I am moving some jquery functions into a javascript object to clean up some code. My problem is, when I put methods on my object's constructor, my event handlers don't seem to respond to events but respond fine if my handlers are helper methods and are outside of the object's constructor.
Here's my code that isn't working
function MyConstructor() {
this.init();
this.selectAllHandler();
}
MyConstructor.prototype = {
init: function() {
$(document).on('click', '#my_element', this.selectAllHandler);
},
selectAllHandler: function() {
// some code in here
}
}
When using this, my code does not error out and putting console.log's atop the function runs. But when I try to click on the thing to trigger the handler, it doesn't do anything.
But, if I build it as a constructor using a method outside of the object, it works fine. Like this
function MyConstructor() {
this.init();
}
MyConstructor.prototype = {
init: function() {
$(document).on('click', '#my_element', selectAllHandler);
}
}
function selectAllHandler() {
// code that works fine
}
what am I doing wrong that I cannot call the handlers inside the object's prototype?
edit
Here is my new code. The problem now, is $(this) seems to refer to the constructor and no longer refers to the element being clicked on.
function MyConstructor() {
this.init();
}
MyConstructor.prototype = {
init: function() {
$(document).on('click', '#my_element', this.selectAllHandler.bind(this));
},
selectAllHandler: function() {
var checkboxes = $('.prospect_select_box');
console.log($(this)); // => [MyConstructor]
if (!$(this).prop('checked')) {
console.log('here')
checkboxes.prop('checked', false);
$('#prospect-left-section').hide();
} else {
console.log('other here')
checkboxes.prop('checked', true);
$('#prospect-left-section').show();
}
}
}
You have two objects you are interested in: the constructed object, and the clicked element. The first you need to find the method selectAllHandler, the second to work with $(this) within that function. Obviously both of them cannot be this at the same time, so you'll need to reference one of them in a different way.
Here is how you could do that.
function MyConstructor() {
this.init();
}
MyConstructor.prototype = {
init: function() {
var that = this;
$(document).on('click', '#my_element', function () {
that.selectAllHandler.call(this);
});
},
selectAllHandler: function() {
$(this).text('clicked!');
}
}
new MyConstructor();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="my_element">click me</button>
Note how call is used to make sure the selectAllHandler will run with this set to what jQuery passed on as element.
If however, you need to also reference the constructed object with this inside setAllHandler, then do it the other way around, and use that as this, but reference the clicked element via the event object that is passed to the function:
function MyConstructor() {
this.init();
}
MyConstructor.prototype = {
init: function() {
var that = this;
$(document).on('click', '#my_element', this.selectAllHandler.bind(this));
},
selectAllHandler: function(e) {
var elem = e.target;
$(elem).text('clicked ' + this.other);
},
other: 'me!'
}
new MyConstructor();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="my_element">click me</button>
I have following JavaScript code.
var Foo = function () {
$('body').on('click', '.btn', this.update.bind(this));
};
Foo.prototype = (function () {
var update = function (e) {
console.log('update');
e.preventDefault();
};
return {
update: update
}
})();
new Foo();
new Foo();
new Foo();
I am creating 3 instances of Foo constructor. Inside constructor, I am attaching a click event to dom element. However, with this approach the click event is attached 3 times because I am creating 3 instance using new operator. How can I make this work so that after creating 3 instance it only attach one click event to that dom element?
JSFIDDLE
var Foo = function () {
$('body').off('click', '.btn');
$('body').on('click', '.btn', this.update.bind(this));
};
This removes the click event listener, then rebinds it. That way it is limited to one listener.
This is not a memory leak. This is how jQuery works if you bind events multiple times. If you do something like that:
$('body').on('click', '.btn', function(e){/*logic here*/});
$('body').on('click', '.btn', function(e){/*logic here*/});
$('body').on('click', '.btn', function(e){/*logic here*/});
It will attach the event 3 times. Why do you think that if you put this thing into a class and instantiate the class 3 times it will not attach the event.
A way to prevent this behaviour is to do something like that:
var Foo = function () {
if(!Foo.instantiatedOnce) {
$('body').on('click', '.btn', this.update.bind(this));
}
Foo.instantiatedOnce = true;
};
Foo.prototype = (function () {
var update = function (e) {
console.log('update');
e.preventDefault();
};
return {
update: update
}
})();
Foo.instantiatedOnce = false;
new Foo();
new Foo();
new Foo();
Doing something like that it is like simulating a static variable that is shared between instances.
This should work:
var binded = false;
var Foo = function () {
if (!binded) {
$('body').one('click', '.btn', this.update.bind(this));
binded = true;
}
};
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
}
I'm trying to improve my code overall, although the following code does work, I want to avoid using _this (to me a hacky way of doing it ), and start using either .call/.apply or .bind to set the context of this on the following example.
Here is the code and the link to jsfiddler link.
(function (window, $) {
'use strict';
var ButtonEffect, button;
Function Constructor
ButtonEffect = function (elem) {
this.button = $( elem );
};
//Prototype Chain
ButtonEffect.prototype = {
addEffect : function (ref) {
return $(this.button, ref).addClass('custom-effect-1');
},
btnObserver : function () {
//Don't want to use this approach
var _this = this;
this.button.on({
click : function () {
//Want to call addEffect without using _this/that #hack
_this.addEffect($(this));
}
});
}
};
button = new ButtonEffect('.my-button');
button.btnObserver();
(window, window.jQuery));
Here is another Solution i came up with link
Seems more appropriate to use the built in jQuery methods for passing data to the event handler, that way this still references the element inside the handler
(function (window, $) {
'use strict';
var ButtonEffect, button;
ButtonEffect = function (elem) {
this.button = $( elem );
};
ButtonEffect.prototype = {
addEffect : function (ref) {
return $(this.button, ref).addClass('custom-effect-1');
},
btnObserver : function () {
this.button.on( {
click : function (e) {
e.data.scope.addEffect($(this));
}
}, {scope: this});
}
};
button = new ButtonEffect('.my-button');
button.btnObserver();
}(window, window.jQuery));
FIDDLE
You can change your code like this:
btnObserver : function () {
this.button.on({
click : function (ev) {
this.addEffect($(ev.currentTarget));
}.bind(this)
});
}
ev.currentTarget is usually the same as what this would be if bind is not used. And bind makes it so that the value of this inside your event handler is the same as the scope in which bind executes. I have a fiddle.
I have the following javascript code found also in this fiddle: http://jsfiddle.net/periklis/k4u4c/
<button id = "element_id" class = "myclass">Click me</button>
<script>
$(document).ready(function() {
this.myfunc = function() {
console.log('Hello world');
}
this.myfunc();
$('#element_id').select('.myclass').bind('click', function() {
this.myfunc(); //Obviously this doesn't work
});
});
</script>
How can I call this.myfunc() when the element is clicked? I don't want to define the myfunc() in the global space.
Thanks as always
Create a local variable that references to the function, that way it is accessible from the anonymous function and you don't end up with myfunc in the global namespace.
<button id = "element_id" class = "myclass">Click me</button>
<script>
$(document).ready(function() {
var myfunc = function() {
console.log('Hello world');
}
myfunc();
$('#element_id').select('.myclass').bind('click', function() {
myfunc(); // works!
});
});
</script>
If you, on the other hand, assign var that = this;, then your method myfunc will be stored on the HTMLDocument object (from $(document)), which is perhaps not what you want. But if that's what you want, then you do this (as others have suggested also, I might add).
<button id = "element_id" class = "myclass">Click me</button>
<script>
$(document).ready(function() {
// storing reference to $(document) in local variable
var that = this;
// adding myfunc on to the document object
that.myfunc = function() {
console.log('Hello world');
}
that.myfunc();
$('#element_id').select('.myclass').bind('click', function() {
that.myfunc(); // works!
});
});
</script>
// Simon A
You could do
$(document).ready(function() {
var that = this;
that.myfunc = function() {
console.log('Hello world');
}
that.myfunc();
$('#element_id').select('.myclass').bind('click', function() {
that.myfunc();
});
});
In this way you cache the this variable with something that you can reuse in your event handlers where this points to the current element
You may be a little confused by what you are doing with the this.myfunc call.
In that context this is referring to document which means you are globally defining that function and it can be referenced at any time by document.myfunc();
If you are just wanting to put a function in a variable temporarily then the following code should help:
$(document).ready(function() {
this.myfunc = function() {
alert('Hello world');
};
var otherfunc = function() {
alert('Hi world');
};
$('.cv1').click(document.myfunc);
$('.cv2').click(otherfunc);
});
JSFiddle: http://jsfiddle.net/LKmuX/
This demonstrates both what you are doing in terms of attaching a function to document and also just putting it in a variable.
An alternative to caching the context, if you need to use the external context inside the binded function, is to use the proxy() method (docs here) to change the scope of the internal function like this :
$('#element_id').select('.myclass').bind('click', $.proxy(function() {
this.myfunc();
}, this));
In this way, I force the actual this (the context when I'm using the bind method) to be the same inside the binded function (that normally has his own context)
http://jsfiddle.net/k4u4c/2/
FYI - the same method can be found in Dojo library (in Dojo it's
largely used), and it's called hitch
Most common way to do this is to cache this in other variable and later on in handler refer to that variable as you would do with this. http://jsfiddle.net/k4u4c/3/
<button id = "element_id" class = "myclass">Click me</button>
<script>
$(document).ready(function() {
this.myfunc = function() {
console.log('Hello world');
}
this.myfunc();
var that = this;
$('#element_id').select('.myclass').bind('click', function() {
that.myfunc(); //Obviously this DOES work :)
});
});
</script>