scope collision while writing a jQuery plugin - javascript

I am trying to write a simple jQuery plugin just to see how its done. But i cant seem to run it twice simultaneously. Its basically a count down and all it does is get the text() value in a div and count it down until it reaches 1.
$('#box1').startCounter();
$('#box2').startCounter();
This call changes both this variables inside the function to point to #box2. Here is my jsfiddle
Its pretty confusing how this changes around in a jQuery plugin. thanks for any help :)

You defined $this in global scope, so when startCount is called on the second element, the value is overwritten. Use var to make it local:
var $this = this;
DEMO
Instead of invoking the function again the element, you could also do something like this:
$.fn.startCount = function(count, div) {
count = (count) ? count : parseInt($('span.no-display',this).text());
var $target = $('div.counter', this);
var run = function() {
if (count <= 1) {
this.fadeOut().mouseout();
}
else {
count--;
$target.text(count);
setTimeout(run, 1000);
}
};
run();
}
DEMO 2
And to make your plugin work in environments where $ does not refer to jQuery (jQuery.noConflict()), you should do:
(function($) {
$.fn.startCount = ...
}(jQuery));

Related

Array.pop() Isn't killing my setInterval

I've written some code that when you click a button it adds an instance of a function to an array,
var objects = [];
$(document).on("click", ".addButton", function(){
objects.push(new newObject(1));
});
function newObject(amount){
setInterval(function(){
addValue(amount);
}, 1000);
}
So then every second each new object created keeps running the addValue function every second adding the amount.
The problem is when I try and destroy that function with objects.pop() it deletes the object but the setInterval doesn't stop running.
How do I make it destroy everything in that function and stop it from running?
There is nothing quite like that in JS for setInterval. I would suggesting declaring a method to handle clean up.
// "Class" declaration
function newObject(amount) {
var id = setInterval(function() {
addValue(amount);
}, 1000);
this.kill = function() {
clearInterval(id);
}
}
// "Public" api for the data structure
var objects = [];
function addNewObject() {
objects.push(new newObject(1));
}
function destroyLastObject() {
objects.pop().kill();
}
// Event bindings
$(document).on("click", ".addButton", addNewObject);
$(document).on("click", ".removeButton", destroyLastObject);
Completely untested, but along these lines should work.
EDIT
This, imo, is a great resource for learning about different patterns within javascript - long but well well worth the read: https://addyosmani.com/resources/essentialjsdesignpatterns/book/
You got to find something to check against to clear the interval. I am clearing based on array length. It only executes once.
// you got to find something to check against to clear the interval
var objects = [];
document.addEventListener("click", function(){
console.log('click');
objects.push(new newObject(1));
});
function newObject(amount){
var interval= setInterval(function(){
if(objects.length !==0){
clearInterval(interval);
}
}, 1000);
}

Autoscroll with setInterval/clearInterval

Im relatively new to JS coding, and can't get this little number to work. Can anyone tell me what I'm doing wrong?
My JavaScript is:
incrementScroll = function() {
window.scrollBy(0, 3) ;
}
startScrollLoop = function() {
scrollLoopId = setInterval( "incrementScroll", 5) ;
}
stopScrollLoop = function() {
clearInterval( scrollLoopId ) ;
}
And my HTML is:
<button onclick="startScrollLoop()">AUTO SCROLL</button>
<button onclick="stopScrollLoop ()">STOP SCROLL</button>
Again, many thanks for help. New to all of this and need to make a project work by morning.
Cheers.
The first argument to setInterval() should be a function reference, or non-ideally, a string to be eval()'d, which would be a complete function call with (). So remove the quotes:
// Pass the reference to incrementScroll,
// not a quoted string containing its name.
scrollLoopId = setInterval(incrementScroll, 5);
And to clear the scroll, you will need to define scrollLoopId at a higher scope with var.
// Define outside the function so it is available
// in scope of the stopScrollLoop() function when needed...
var scrollLoopId;
var startScrollLoop = function() {
scrollLoopId = setInterval( incrementScroll, 5) ;
}
Jsfiddle demo
(uses a slower scroll speed to give you a chance to click the stop button in the middle of the text)
Note that it is good practice to use the var keyword with each of these. even though they would end up at window scope anyway (assuming they're not being defined inside another function).
// Define with var
var incrementScroll = function() {
window.scrollBy(0, 3);
}

jQuery anonymous function

I have some code (below) which I wrote for a small piece of text to fade in out through a cycle of around 4 paragraphs. It works, but whenever I bring up the Web Inspector, it just tells me that it's an 'Anonymous function'. This is really annoying. Does anyone know how to fix it?
Btw, the bit that it hightlights as an anonymous function is:
slides[current].fadeOut("slow");
slides[target].fadeIn("slow");
The whole extract of code is here:
$(document).ready(function() {
var About = {
init: function() {
var slide_images = $('#widget p')
slides = new Array(),
delay = 5,
current = 0;
slide_images.each(function(index) {
current = index;
slides.push($(this));
});
var interval = setInterval(function() {
target = (current < (slides.length - 1)) ? current + 1 : 0;
slides[current].fadeOut("slow");
slides[target].fadeIn("slow");
current = target;
}, delay * 750);
}
}
About.init();
});
I made a jsfiddle here.
Because it is an anonymous function, as opposed to a named function.
One potential solution could be to roll the code into a named function and reference that function by named for the init option.

How to pass timeout by reference? Or a better way to implement?

I had this code working previously, but I am not so sure now that I have separated my HTML controls from my jQueryUI Widget.
Currently, the timer starts correctly, but I lose my reference to _refreshTimeout after one tick. That is, after the first tick, unchecking my PlanViewRefreshCheckbox does not stop my timer from running.
I have two JavaScript files, PlanView.js and PlanViewCanvas.js.
PlanView.js looks something like this:
(function ($) {
var _minZoom = -2.0;
var _maxZoom = 2.0;
var _stepZoom = (_maxZoom - _minZoom) / 100;
var _refreshTimeout = null;
var _refreshInterval = 60000; //One minute
$(document).ready(function () {
//Initialize Refresh combo box.
$('#PlanViewRefreshCheckbox').click(function () {
if ($(this).is(':checked')) {
var planViewCanvas = $('#PlanViewCanvas');
//Binding forces the scope to stay as 'this' instead of the domWindow (which calls setTimeout).
_refreshTimeout = setTimeout(function(){planViewCanvas.PlanViewCanvas('refresh', _refreshInterval, _refreshTimeout)}.bind(planViewCanvas), _refreshInterval)
}
else {
clearTimeout(_refreshTimeout);
}
});
}
})(jQuery);
and PlanViewCanvas.js houses a jQueryUI Widget:
(function ($) {
$.widget("ui.PlanViewCanvas", {
//other properties and methods not-relevant to problem declared here.
refresh: function (refreshInterval, refreshTimeout) {
var self = this;
_stage.removeChildren();
self.initialize();
//Binding forces the scope to stay as 'this' instead of the domWindow (which calls setTimeout).
refreshTimeout = setTimeout(function () { self.refresh(refreshInterval, refreshTimeout) }.bind(self), refreshInterval);
},
}
})(jQuery);
Does it seem like I am going about things incorrectly?
EDIT: I think the answer is probably to use setInterval and not setTimeout.
The first problem is that you forgot the underscore
refreshTimeout should be _refreshTimeout
second, your variable needs to be global to be accessible in both files, so declare it outside of the function:
var _minZoom = -2.0;
var _maxZoom = 2.0;
var _stepZoom = (_maxZoom - _minZoom) / 100;
var _refreshTimeout = null;
var _refreshInterval = 60000; //One minute
(function ($) {
....
})(jQuery)
You can't pass values by reference. I see two options:
pass an Object. If you have it referenced from two variables, you can access its properties in both scopes.
split up you functionality in two functions, where it belongs: One masters the interval loop and triggers the refresh function, and the other does things to refresh. The refreshTimeout variable only belongs to the scope of the first one. point. You may add the interval function to you widget if it is often needed.
The answer was very 'oh derp.'
//Initialize Refresh combo box.
$('#PlanViewRefreshCheckbox').click(function () {
if (this.checked) {
_refreshTimeout = setInterval(function(){$('#PlanViewCanvas').PlanViewCanvas('refresh')}, _refreshInterval)
}
else {
clearTimeout(_refreshTimeout);
}
});

jQuery/javascript and setInterval not working correctly within mouseenter

Ok so basically I'm implementing a simple horizontal scroller. The function gets fired when I move the mouse into the correct div but only once and doesn't keep looping after the interval time. Any help appreciated:
$(document).ready(function() {
$('#toparrow').mouseenter(function(e) {
var func = scrollroller(1);
setInterval(func,1);
}).mouseleave(function() {
});
function scrollroller(velocity) {
$('#roller').animate({left:'+='+velocity},1);
}
});
var func = function(){ scrollroller(1); };
The problem is with this line:
var func = scrollroller(1);
This isn't assigning the scrollroller function to func, it is invoking scrollroller() with a parameter of '1' and storing the result in func. You probably want to do something like:
setInterval("scrollroller(1);",1); //alternately: setInterval(scrollroller,1,1);
Note that the alternate syntax may have issues in IE.

Categories