object oriented jquery - event handlers not working - javascript

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>

Related

JavaScript: using THIS inside object function and binding event

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

"this" changes value on click

If I have the following code:
function Something() {
this.randomElement = $("#element");
}
Something.prototype = {
functionOne: function() {
console.log("hello");
},
functionTwo: function() {
this.randomElement.click(function(e) {
this.functionOne(); //this becomes pointed at randomElement
});
}
}
How can I write this in a clean way where I wouldn't have to use Something.prototype.functionOne() to replace this.functionOne() inside of functionTwo? Since the click event changes the value of this?
Because this is bound to the item that is clicked. You need to use bind
this.randomElement.click( this.functionOne.bind(this) );
or jQuery's proxy.
this.randomElement.click( $.proxy(this.functionOne, this) );

Avoid Using _this inside of nested function jQuery

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.

Javascript: How to pass an argument to a method being called without parentheses

Sorry for how stupid this is going to sound. My JS vocabulary is terrible and I had absolutely no idea what to search for.
I'm using jQuery.
So I've got this code:
var example = {
open: function(element){
alert(element.text());
},
init: function(){
$("a").click(example.open);
}
};
$(document).ready(function(){example.init();)
So here's the problem: I want to pass an argument to example.open() when I click the "a" element. It doesn't seem like I can, though. In order for the example.open method to just…exist on page-load and not just run, it can't have parentheses. I think. So there's no way to pass it an argument.
So I guess my question is…how do you pass an argument to a function that can't have parentheses?
Thanks so much.
Insert another anonymous function:
var example = {
open: function(element){
alert(element.text());
},
init: function(){
$("a").click(function()
{
example.open($(this));
});
}
};
You can also try this version because jQuery set the function's context (this) to the DOM element:
var example = {
open: function(){
alert($(this).text());
},
init: function(){
$("button").click(example.open);
}
};
Since jQuery binds the HTML element that raised the event into the this variable, you just have to pass it as a regular parameter:
var example = {
open: function(element){
alert(element.text());
},
init: function(){
$("a").click(function() {
// jQuery binds "this" to the element that initiated the event
example.open(this);
});
}
}
$(document).ready(function(){example.init();)
You can pass the anchor through its own handler:
var example = {
open: function( element ){
alert(element.text());
},
init: function(){
$("a").on("click", function() {
example.open( $(this) );
});
}
};
$(document).ready(function() {
example.init();
});
I don't understand what you actually want to do;
however, I can give a try:
var example = {
open: function(event){
event.preventDefault();
alert($(event.target).text()+' : '+event.data.x);
},
init: function(){
$("a").bind('click',{x:10},example.open);
}
};
$(example.init);
demo:
http://jsfiddle.net/rahen/EM2g9/2/
Sorry, I misunderstood the question.
There are several ways to handle this:
Wrap the call in a function:
$('a').click( function(){ example.open( $(this) ) } );
Where $(this) can be replaced by your argument list
Call a different event creator function, which takes the arguments as a parameter:
$('a').bind( 'click', {yourvariable:yourvalue}, example.open );
Where open takes a parameter called event and you can access your variable through the event.data (in the above it'd be event.data.yourvariable)
Errors and Other Info
However your element.text() won't just work unless element is a jQuery object. So you can jQueryify the object before passing it to the function, or after it's received by the function:
jQuery the passed object:
function(){ example.open(this) } /* to */ function(){ example.open($(this)) }
jQuery the received object:
alert(element.text()); /* to */ alert($(element).text());
That said, when calling an object without parameters this will refer to the object in scope (that generated the event). So, really, if you don't need to pass extra parameters you can get away with something like:
var example = {
open: function(){ // no argument needed
alert($(this).text()); // this points to element being clicked
},
init: function(){
$("a").click(example.open);
}
};
$(document).ready(function(){
example.init();
}); // your ready function was missing closing brace '}'

jQuery: UI widget definition

I have a widget defined like so:
$.widget("ui.mywidget", {
_init: function() {
this.element.bind("keyup", function(event) {
alert(this.options);
alert(this.options.timeout);
});
}
});
And trying to call it like so:
$("input.mywidget").mywidget({timeout: 5});
I also redefined the bind method using the this.element.keyup(function(event) { ... }) style: no difference.
But, in the keyup bind, this.options (and referencing it just as options) both yield undefined. I thought the UI widget framework allowed this type of abstraction; am I doing something wrong?
When inside bind(), this changes to refer to the object that the event is raised on. Try:
$.widget("ui.mywidget", {
_init: function(options) {
var opts = this.options;
this.element.bind("keyup", function(event) {
alert(opts);
alert(opts.timeout);
});
}
});
What #Dave said is right. You can also set "this" to a variable rather than using options as an argument to the init function. Here is how I see it implemented often:
$.widget("ui.mywidget", {
options: {
timeout: 100
},
_init: function() {
var self = this;
self.element.bind("keyup", function(event) {
alert(self.options);
alert(self.options.timeout);
});
}
});
Why stop there? Check out $.proxy and write better code
$.widget("ui.mywidget", {
_create: function() {
//Under this syntax, _omgAlertsRule must be a method of 'this'
this.element.bind("keyup", $.proxy( this, '_omgAlertsRule' ) );
},
_omgAlertsRule: function( event ){
alert(this.options);
alert(this.options.timeout);
}
});

Categories

Resources