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);
}
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.
I Am looking for a way to proper call this inside function now i have quick hack var that = $(this); but i am sure that there is propper way of doing it. How i can avoid this hack?
This is input field which i use to get var and inspect Typing Interval
<input type="text" data-package="pink" class="js-p-input">
this i my code:
var cCalc = (function ($) {
var s;
return {
settings: {
typingTimer: "",
doneTypingInterval: 300,
$inputs: $(".js-p-input"),
},
init: function () {
s = this.settings;
this.bindUIActions();
},
bindUIActions: function () {
//on keyup, start the countdown
s.$inputs.on('keyup', function () {
var that = $(this);
clearTimeout(s.typingTimer);
s.typingTimer = setTimeout(function() {
cCalc.doneTyping(that, that.data("package"));
}, s.doneTypingInterval);
});
s.$inputs.on('keydown', function () {
clearTimeout(s.typingTimer);
});
},
doneTyping: function ($input, packageName) {
console.log('done!');
cCalc.getValues($input.val(), packageName);
},
};
})(jQuery);
cCalc.init();
There is nothing wrong with using that "hack", it is standard operating procedure. See also here What is the difference between call and apply? for even more "hacky" stuff with "this" that is standard.
As others have pointed out, there's not really anything wrong with using a closure but you could alternatively bind "this" into the timeout function scope like so:
s.$inputs.on('keyup', function () {
clearTimeout(s.typingTimer);
s.typingTimer = setTimeout(function() {
cCalc.doneTyping($(this), $(this).data("package"));
}.bind(this), s.doneTypingInterval);
});
+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>
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.
I run into a strange behavior on jquery ui (tested on jquery 1.8.3, jquery-ui 1.9.2).
I have a parent and child widget, on the _create method of the parent I give the parent's self reference to the newly created child.
When I try to access this reference from within the child all the function and properties declared after the building of the child are not available.
(function ($) {
$.widget("ui.parentWidget", $.ui.mouse, {
version: "#VERSION", widgetEventPrefix: "parentWidget",
_create: function () {
var self = this;
self.child = $("<div/>")
.childWidget({ parent: self })
.appendTo(self.element);
self.parentName = "parent";
},
parentFeature: function() {
return "feature";
},
getChild: function() {
return this.child;
},
updateChild: function () {
var self = this;
self.child.childWidget("update", self);
}
});}(jQuery));
The child widget
(function ($) {
$.widget("ui.childWidget", $.ui.mouse, {
version: "#VERSION", widgetEventPrefix: "childWidget",
options: {
},
_create: function () {
},
childParent: function () {
return this.options.parent;
},
updatedParent: function() {
return this.newParent;
},
update: function(parent) {
var self = this;
self.newParent = parent;
}
});
}(jQuery));
Now to test my output I just create a parent, retrieve its child, and read the child's parents from the child and display the results.
var parent = $("<div/>").parentWidget();
parent.parentWidget("updateChild");
var child = parent.parentWidget("getChild");
var childParent = child.childWidget("childParent");
var updatedParent = child.childWidget("updatedParent");
childParent.parentName or childParent.parentFeature() are undefined on ie7 and ie8 and works fine everywhere else updatedParent possesses all its content.
My question being, is this a bug ? or did I misunderstood how to properly use jquery ui plugin.