I'm inside a javascript object (vr roxx :) ), but every time I do an event bind with jQuery I have to include the main object instance's context through the data parameter in order to work with it. Isn't there an easy/neat way to do this in jQuery?
var oink =
{
pig: null,
options:
{
showPigMom: 0
},
init: function(pigObj)
{
//Show the pigmom
$(this.doc).bind('keyup click', {o: this}, function(e)
{
var o = e.data.o;
if (o.options.showpath)
o.doWhatever();
});
...
I use the $.proxy() function
init: function(pigObj)
{
//Show the pigmom
$(this.doc).bind('keyup click', $.proxy(function(e) {
if (this.options.showpath)
this.doWhatever();
$(e.currentTarget).text(); // use this to access the clicked element
}, this));
}
init: function() {
var self = this;
$(this.doc).bind('keyup click', function() {
if (self.options.showpath) self.doWhatever();
});
}
init: function() {
$(this.doc).bind('keyup click', function() {
if (this.options.showpath) this.doWhatever();
$(e.currentTarget).text(); // use this to access the clicked element
}.bind(this))
}
Related
I've had this issue before, essentially I'd like to keep everything my app separate i.e caching the dom, binding events, no html in javascript etc.
I have an issue where in my bindevents method I have a click on the delete button, however the delete button only exists once a to do has been added.
I'm there getting an error:
Uncaught TypeError: Cannot read property 'addEventListener' of null
Because I guess I'm searching the dom for an element that doesn't exist, how do I keep the structure as it is but only search the DOM for the delete button once an item has been added?
JS
(function() {
var toDo = {
data: [],
cacheDom: function() {
this.toDoApp = document.getElementById('to-do-app');
this.toDoTemplate = document.getElementById('to-do-template');
this.addToDo = document.getElementById('add-to-do');
this.addToDoValue = document.getElementById('add-to-do-value');
this.deleteToDo = document.querySelector('.to-do-delete');
},
load: function() {
this.toDoTemplate = Handlebars.compile(this.toDoTemplate.innerHTML);
},
render: function() {
this.toDoApp.innerHTML = this.toDoTemplate(this.data);
},
bindEvents: function() {
this.addToDo.addEventListener("click", this.add.bind(this));
this.deleteToDo.addEventListener("click", this.delete.bind(this));
},
add: function(e) {
var toDoValue = this.addToDoValue.value;
if(toDoValue) {
var toDoObj = {
value: toDoValue,
id: Date.now()
}
this.data.push(toDoObj);
}
this.render();
},
delete: function() {
console.log("delete!");
},
init: function() {
this.cacheDom();
this.bindEvents();
this.load();
this.render();
}
}
toDo.init();
})();
You should be bind()ing your event handler functions to their scope when they're defined, not when they're setup as listeners.
bindEvents: function() {
this.addToDo.addEventListener("click", this.add);
this.deleteToDo.addEventListener("click", this.delete);
},
add: function() {
// ...
}.bind(this),
delete: function() {
// ...
}.bind(this),
At first, you may just try to get Elements after onload:
window.addEventListener("DOMContentLoaded",toDo.init.bind(toDo));
And you could listen at window for all clicks, then filter them:
window.addEventListener("click",function(evt){
var el = evt.target;
do {
if(el.classList.contains("someclass")){
somefunc.call(el);
}
} while ( el = el.parentElement);
});
I would remove the deletetodo caching and event assignment from the init procedure (which is always going to be null anyway) and listen for the event when the button is added instead.
Short question, how can I more efficiently write below code so I don't repeatedly assign the parent variable a new value?
Is this bind function the same as using object literals?
function bindAuthorPopup() {
$(".insight-author").on({
mouseenter: function (event) {
var parent = $(this).parent('div').find('.popup-content');
parent.toggleClass('show');
},
mouseleave: function (event) {
var parent = $(this).parent('div').find('.popup-content');
parent.toggleClass('show');
},
});
}
You can do something like this:
function bindAuthorPopup() {
$(".insight-author").each(function() {
var elem = $(this);
var parent = elem.parent('div').find('.popup-content');
elem.on({
mouseenter: function (event) {
parent.toggleClass('show');
},
mouseleave: function (event) {
parent.toggleClass('show');
},
});
});
}
This works even if the callbacks are different. If they're always the same, then you can use what #sh1da9440 wrote.
You can pass space-separated event types to the "on" method.
function bindAuthorPopup() {
$(".insight-author").on('mouseenter mouseleave', function (event) {
var parent = $(this).parent('div').find('.popup-content');
parent.toggleClass('show');
});
}
Background:
I am trying to dry up my code using the object literal pattern.
The object:
Here is my object:
(function(){
var bookingForm = {
init: function(){
this.cacheDOM();
this.bindEvents();
},
cacheDOM: function(){
this.$nextStep = $('.btn-next-step');
},
bindEvents: function(){
this.$nextStep.on('click', this.nextStep.bind(this));
},
nextStep: function(value){
alert($(value).attr('data'));
},
prevStep: function(){
},
}
bookingForm.init();
})();
And my button which is supposed to trigger the nextStep function
<button class="btn btn-success btn-next-step" data="2">
Next Step
<i class="fa fa-arrow-right"></i>
</button>
So I am trying to access the data attribute of the button, so when I click it I should get an alert of '2'.
I have tried a number of ways... this current code just alerts 'undefined'.
Question
How do I pass my data attribute through to the bound function?
The argument to an event listener is the event, not the element. To get the element, use event.target
nextStep: function(event){
alert($(event.target).attr('data'));
jQuery normally binds this to the target element, but you've overridden that with this.nextStep.bind(this), so this contains the bookingForm object.
I think your problem is in bindEvents. You are binding the same object to function, but the function is hoping to receive an element.
I propose the next change
(function(){
var bookingForm = {
init: function(){
this.cacheDOM();
this.bindEvents();
},
cacheDOM: function(){
this.$nextStep = $('.btn-next-step');
},
bindEvents: function(){
var that = this;
this.$nextStep.on('click', function (e) {
that.nextStep(this)); // here "this" is the element
// or you can do that.nextStep(e.target);
});
},
nextStep: function(value){
alert($(value).attr('data'));
},
prevStep: function(){
},
}
bookingForm.init();
})();
+function ($) {
'use strict';
var popup = {
init: function(element) {
this._active = 'products__popup--active';
this._product = $('.products__popup');
this._element = $('[data-popup-to]');
this._TIME = 500;
popup.attachEvt();
},
attachEvt: function() {
var that = this;
that._element.bind('click', popup.handlerEvt.call(that));
},
handlerEvt: function() {
console.log(this);
console.log('test');
}
};
$(window).on('load', function() {
popup.init();
});
}(jQuery);
I have this script, and is not working yet, I cant show you a working example because it is not ready, I'm organizing the code first.
And there is a problem with the attachEvt function, inside it I want to call another function of my object, this function will bind a click in the that._element, but I want pass to the handlerEvt the scope of this (the clicked element) and the that (the object), but this is not working:
that._element.bind('click', popup.handlerEvt.call(that));
I'm just passing the that scope and when the script loads, the element will be clicked without click, I want avoid this.. this is possible?
UPDATE:
Resuming:
I want be able to use the scope of the object (that) and the scope of the clicked element (this) inside the handlerEvt function, but without make the event click when the script loads.. :B
Try utilizing .bind() , with this set to that._element , that passed as parameter to handlerEvent . Note order of parameters at handlerEvent: obj: that first , evt event object second
+function ($) {
'use strict';
var popup = {
init: function(element) {
this._active = 'products__popup--active';
this._product = $('.products__popup');
this._element = $('[data-popup-to]');
this._TIME = 500;
popup.attachEvt();
},
attachEvt: function() {
var that = this;
that._element.bind('click', popup.handlerEvt.bind(that._element, that));
},
handlerEvt: function(obj, evt) {
console.log(evt, obj, this);
console.log('test');
}
};
$(window).on('load', function() {
popup.init();
});
}(jQuery);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div data-popup-to="true">click</div>
Ok terrible title but I couldn't think of another description.
I have the following code:
jQuery( document ).ready( function( $ )
{
$.myNamespace = {
init: function()
{
$('.button').click(function() {
this.anotherFunction();
});
},
anotherFunction: function()
{
alert('insidefunction');
}
}
$.myNamespace.init();
});
As you can see I am trying to call anotherFunction from inside init and have there the two ways I tried but didn't work. So how am I able to call that function or is my concept wrong?
jQuery( document ).ready( function( $ )
{
$.myNamespace = {
init: function()
{
var a=this;
$('.button').click(function() {
a.anotherFunction();
});
},
anotherFunction: function()
{
alert('insidefunction');
}
}
$.myNamespace.init();
});
http://jsfiddle.net/ZpAtm/2/
Absolutely calling it within the click handler changes things, as this inside any jQuery event handler is set to the element that caused the event.
Instead, try using the following pattern:
jQuery(document).ready(function($) {
$.myNamespace = (function() {
function init() {
$('.button').click(function() {
anotherFunction();
});
}
function anotherFunction() {
alert('insidefunction');
}
// return an object with all the functions you want
// available publically as properties. Don't include
// any "private" functions.
return {
init: init,
anotherFunction: anotherFunction
};
})();
$.myNamespace.init();
});