I'm an intermediate front-end JS developer and I'm trying the Module Pattern outlined by Chris Coyyer here.
But when I store a jQuery selector in the settings, I'm unable to use it to trigger a click event. See the below code with my comments... Any help is greatly appreciated!
var s,
TestWidget = {
settings: {
testButton: $("#testing")
},
init: function() {
s = this.settings;
this.bindUIActions();
},
bindUIActions: function() {
console.log(s.testButton); // This works: [context: document, selector: "#testing", constructor: function, init: function, selector: ""…]
//This doesn't work - why?????
s.testButton.click(function() {
//Why isn't this triggered?
alert('testButton clicked');
});
/*This works, obviously:
$('#testing').click(function() {
alert('testButton clicked');
});
*/
}
};
$(document).ready(function() {
TestWidget.init();
});
The problem is that you initialize $("#testing") before the DOM is ready, so this jQuery object is empty.
A simple solution is to put all your code in the ready callback.
Another one would be to replace
settings: {
testButton: $("#testing")
},
init: function() {
s = this.settings;
this.bindUIActions();
},
with
settings: {
},
init: function() {
s = this.settings;
s.testButton = $("#testing");
this.bindUIActions();
},
But it's hard to get why you use so much code for such a simple thing. You might be overusing the pattern here and it's not really clean as you have two global variables s and TestWidget when one would already be a lot.
Here's a slight variation of your code which would be, in my opinion, cleaner, while still using modules (IIFE variant) :
TestWidget = (function(){
var settings = {};
return {
init: function() {
settings.testButton = $("#testing");
this.bindUIActions();
},
bindUIActions: function() {
console.log(settings.testButton);
settings.testButton.click(function() {
alert('testButton clicked');
});
}
}
})();
$(document).ready(function() {
TestWidget.init();
});
settings is kept in the closure and doesn't leak in the global namespace. Note that even this version doesn't make sense if you don't do more with the module.
Related
I'm learning how to use objects to help organize my code and give it some structure but I've run into a problem. I don't understand how to set the $(this) from inside of one function to the $(this) of another function.
I'm researching call and apply but I can't seem to grasp how it works in this scenario.
cloneCard and clickCard is where I'm having the problem. I want to pass the $(this) that is referenced when I click the card to the cloneCard function.
Here is my code so far (updated to reflect the answer):
var Modal = {
init: function(config) {
this.config = config;
this.clickCard();
this.removeModal();
this.clickOutside();
this.createClose();
},
clickCard: function() {
$this = this;
this.config.boardOutput.on('click', '.card', function(event) {
$this.showOverlay();
$this.cloneCard.call($(this));
$this.createClose();
});
},
cloneCard: function() {
this.clone()
.replaceWith($('<div/>').html(this.html()))
.removeClass('card')
.addClass('modal')
.css("margin-top", $(window).scrollTop())
.prependTo('body');
},
showOverlay: function() {
this.config.overlay.show();
},
removeModal: function() {
$('.modal').remove();
$('.overlay').hide();
},
clickOutside: function() {
this.config.overlay.on('click', this.removeModal);
},
createClose: function() {
$('<span class="close">X</span>')
.prependTo('.modal')
.on('click', this.removeModal);
}
};
Modal.init({
boardOutput: $('#board-output'),
overlay: $('.overlay')
});
For what you need, calling self.cloneCard.call($(this)); instead of self.cloneCard($(this));
should work. What you're doing is, calling cloneCard passing it the element in which the the clickCard event occured.
If this doesn't work, i think we'll need more information to sovle your problem.
I am trying to produce a blinking effect using dojo fadeIn/Out.
The following snippet of code is defined inside the declaration of a widget class:
_startHighlightEffect : function() {
var blinkInterval = 5000; //Scope here is that of the parent widget
window.setInterval ( function() {
dojo.fadeOut(
{
node: this._headerDiv.domNode,
onEnd: function() {
dojo.fadeIn({node: this._headerDiv.domNode},3000).play();
}
},3000).play();
}, blinkInterval);
},
_highlightEffect : function() {
this.func = dojo.hitch(this,this._startHighlightEffect);
this.func();
}
The problem I am facing is that it says,"this._headerDiv is undefined". On checking with firebug, the scope of this._headerDiv is Window instead of the parent widget.
Please help me understand what am I missing here.
What #jbabey describes will work, but in terms of dojo.hitch, you used it on the wrong function. You need to hitch the function that is passed into setInterval.
_startHighlightEffect : function() {
var blinkInterval = 5000; //Scope here is that of the parent widget
// hitch the function that will be executed by the setInterval call *********
window.setInterval (dojo.hitch(this, function() {
dojo.fadeOut(
{
node: this._headerDiv.domNode,
onEnd: dojo.hitch(this, function() {
dojo.fadeIn(
{node: this._headerDiv.domNode},3000).play();
})
},3000).play();
}, blinkInterval));
},
_highlightEffect : function() {
this._startHighlightEffect();
}
you can save the context when it is the context you want, and use it later:
_startHighlightEffect : function() {
var blinkInterval = 5000; //Scope here is that of the parent widget
var that = this; // save the scope
window.setInterval ( function() {
dojo.fadeOut(
{
node: that._headerDiv.domNode, // use the saved scope
onEnd: function() {
dojo.fadeIn({node: that._headerDiv.domNode},3000).play();
}
},3000).play();
}, blinkInterval);
}
I'm trying to create multiple timers. Any tutorials which can help me. The most difficult part is the html part and how to link my files a sample project would do me good.
Html5.Views.Timer = Backbone.View.extend({
el: 'div#timer',
initialize: function() {
timer = this.model;
this.render();
},
events: {
"clicked div#add_time": "update_timer"
}
render: function() {
$(this.el).append(HandlebarsTemplates['timer'](timer);
this.start_timer();
},
start_timer: function() {
clearTimeout(this.main_timer);
this.main_timer = setTimeout(function() {
if (this.countDownInstance) {
this.countDownInstance.clearRewriteCounter();
}
this.countDownInstance = new countDown(timed.length, 'main_timer');
}, timed_length*1000);
},
update_timer: function() {
timed.length = timed.length+30
this.start_timer();
}
});
I think there is a typo or you use timed_length or you use timed.length.. Also I think timed.length should returns an error at least there is a variable called timed in a global scope.
Also the line timer = this.model; is declaring a variable timer in a global scope.
Also the calls to timed_length maybe you want to change them to this.timed_length.
Summarizing.. I think there are a bunch of typos here.
im having some trouble with the following code:
Ext.define('...controller...', {
extend: 'Ext.app.Controller',
init: function() {
...
},
crearLoginWindow:function(){
var loginWindow = Ext.create('Proto1.view.adminbd.LoginWindowBDView', {
itemId: 'loginwindow',
autoShow: true,
modal: true
});
Ext.ComponentQuery.query('#loginwindow > button[text="Cancelar"]')[0].on('click', function(){loginWindow.close()});
//Cant call 'enviarLogin' function if its inside 'crearLoginWindow'
Ext.ComponentQuery.query('#loginwindow > button[text="Conectar"]')[0].on('click', this.enviarLogin, this);
var enviarLogin = function(){
console.log('login')
}
}
});
I want to be able to call 'enviarLogin' inside 'crearLoginWindow' function but it throws a reference error. If the function is placed outside 'crearLoginWindow' it will work.
Being these two lines the source of trouble:
Ext.ComponentQuery.query('#loginwindow > button[text="Conectar"]')[0].on('click', this.enviarLogin, this);
var enviarLogin = function(){
console.log('login')
}
I've tried different scope variants such as;
Ext.ComponentQuery.query('#loginwindow > button[text="Conectar"]')[0].on('click', this.crearLoginWindow.enviarLogin);
this.enviarLogin = function(){
console.log('login')
}
Which makes sense with what i think i understand about scope and the need to specify the place where the function resides to execute it.
Id appreciate a solution because this problem makes my code very messy!
Simply defining a function within another function doesn't attach it to any object or instance, so this.enviarLogin won't work. There are two basic options here:
Just define the inner function as you're doing (moving it up above your handler assignment), and reference it directly by name:
var enviarLogin = function(){
console.log('login')
};
Ext.ComponentQuery.query('#loginwindow > button[text="Conectar"]')[0]
.on('click', enviarLogin);
Define enviarLogin as a method, the same way you are defining crearLoginWindow:
Ext.define('...controller...', {
extend: 'Ext.app.Controller',
init: function() { ... },
crearLoginWindow: function(){
// ...
Ext.ComponentQuery.query('#loginwindow > button[text="Conectar"]')[0]
.on('click', this.enviarLogin, this);
},
enviarLogin: function(){
console.log('login')
}
});
The first version may be better if you don't need to reference enviarLogin outside of the crearLoginWindow method. The second is better if you do need to reference enviarLogin elsewhere, and it makes using this in enviarLogin clearer.
Using Mootools 1.3.2
Code is as follows:
var DNReportAbuse = new Class({
Extends: DNUserDialog,
comment_id: null,
container: null,
initialize: function(classname)
{
var bindclass = $(document.body).getElements(classname);
bindclass.each(function(el) {
el.addEvents({
click: function() {
this.reportComment();
}.bind(this)
});
});
},
reportComment: function() {
this.preventDefault();
alert('hello');
return false;
}
});
The event does bind, and when "this.reportComment();" is replaced with "alert('hello world');" it works entirely ...
... but when "this.reportComment()" is used, I instead receive an error, which Firebug explains as "function this.reportComment() is not a function".
I imagine that my issue has something to do with referring to a class function outside of its proper scope, though I'm a bit confused as to why ... or how to solve the issue. The end goal is to achieve an on-click binding of the reportComment() function to all members of a css class (up to 20 per page). The difficulty is that referencing the reportComment() function with "this.reportComment()" results in an error claiming that the function does not exist, when it clearly does exist.
Looking through similar questions on Stack Overflow did not seem to answer this issue ... so asking in hopes that someone can point me in the right direction.
You have some problems with bind and events:
initialize: function(classname)
{
var bindclass = $(document.body).getElements(classname);
var _self = this; //var to store the 'real' this you will use
bindclass.each(function(el) {
el.addEvents({
click: function(event) { //pass the event
this.reportComment(event);
}.bind(_self) //'this' is referring to the inner each function callback
});
});
},
reportComment: function(event) {
event.preventDefault(); //preventDefault on the event, not on 'this'
alert('hello');
return false;
}