Hiding and revealing divs - javascript

I've a script which works fine when written like this:
var isPaused = false,
jQuery(function () {
var $els = $('div[id^=film]'),
i = 0,
len = $els.length;
$els.slice(1).hide();
setInterval(function () {
if (!isPaused) {
if (len > 1) {
$els.eq(i).fadeOut(function () {
i = (i + 1) % len;
$els.eq(i).fadeIn();
});
}
}
}, 3500);
});
But I wanted to add a next and prev button so I rewrote like this, which I thought would work.
var isPaused = false,
$els = $('div[id^=film]'),
i = 0,
len = $els.length;
$els.slice(1).hide();
setInterval(Slide(1), 3500);
function Slide (x) {
if (!isPaused) {
if (len > 1) {
$els.eq(i).fadeOut(function () {
i = (i + x) % len;
if (i<0) {
i = len;
}
$els.eq(i).fadeIn();
});
}
}
}
$('#next').click(Slide(1));
$('#prev').click(Slide(-1));
But this code is just displaying all the divs when the page is loaded and then doesn't fade them in and out or allow the next and prev buttons to work. What am I doing wrong?
UPDATE
Perhaps I should ask this differently. Given the first block of code, how should I change it to enable Prev and Next buttons?

You want to do:
$('#next').click(function(){Slide(1);});
$('#prev').click(function(){Slide(-1);});
and
setInterval(function(){Slide(1);}, 3500);
instead, as with your current code, Slide(1) is already being computed, and the click function will just call the value returned from it (as it has no return in the function, this will not be defined)
By wrapping your calls to Slide in a function, this makes the clicks call that function, which in turn calls Slide
You also want to set your index to len - 1 if you go negative, rather than to len, as you're dealing with a zero indexed array:
if (i<0) {
i = len - 1;
}

You jQuery event callbacks must be inside a load structure like:
// Wait for DOM load
$(document).ready(function() {
$('#next').click(function(){ Slide(1); });
$('#prev').click(function(){ Slide(-1); });
});
And I would like to suggest you to change your:
setInterval(Slide(1), 3500);
to
setInterval(function() {
Slide(1);
}, 3500);
Hope it helps

Related

Javascript 'reset' function not working properly

Why am I required to click twice in order to change the classes after 'reseting'? How can I fix this issue?
The desired result is to bring the function back to the initial state and cycle through the arrays normally. Demo below.
$(function () {
var weights = ["jHairline", "jThin", "jLight", "jMedium"];
var currentIndex = 0;
$('#text').on('click', function (e) {
$("h1").removeClass().addClass(weights[currentIndex]);
$("h1").html(weights[currentIndex]);
if (currentIndex == weights.length - 1)
currentIndex = 0;
else
currentIndex++;
e.preventDefault();
});
$('#reset').click(function () {
currentIndex = 0;
$("h1").removeClass().addClass(weights[currentIndex]);
$("h1").html(weights[currentIndex]);
});
});
Demo
Because you reset it to 0 and don't increment it on the next update.
The quick fix would be
$('#reset').click(function() {
currentIndex = 0;
$("h1").removeClass().addClass(weights[currentIndex]);
$("h1").html(weights[currentIndex]);
currentIndex=1;
});
The proper way would be to do the incrementing at the beginning of the $('#text').on('click', function(e) { function
--Edit for clarification request--
This is how I would personally recommend writing it:
$(function () {
var weights = ["jHairline", "jThin", "jLight", "jMedium"];
var currentIndex = -1;
$('#text').on('click', function (e) {
currentIndex=(currentIndex+1)%weights.length;
$("h1").removeClass().addClass(weights[currentIndex]);
$("h1").html(weights[currentIndex]);
e.preventDefault();
});
$('#reset').click(function () {
currentIndex = 0;
$("h1").removeClass().addClass(weights[currentIndex]);
$("h1").html(weights[currentIndex]);
});
});
The 2 lines I added/modified were var currentIndex = -1; and currentIndex=(currentIndex+1)%weights.length; .
So essentially, you are incrementing the number by 1 when the text.click function starts. If you start it at -1 at the beginning, then when text.click enters, it will increment it to 0. And setting it to 0 in reset will make sure it increments to 1 next time text.click is ran.
Further,
if (currentIndex == weights.length - 1)
currentIndex = 0;
else
currentIndex++;
is a bit more concise and clear when written as currentIndex=(currentIndex+1)%weights.length; . It is adding 1 to the number, and then modding (taking the remainder when dividing) to loop it back around to 0 once weight.length is hit.

How to add javascript function on html?

I don't know what's wrong with my code. I cannot pass a function in my javascript, [i don't want to put it inline]
My problem is my prev button and next button doesn't work, I also tried to put return false on prev and next to stop refreshing the page, but it still refreshing on click.
This is my code [please also see my comments] and my codepen:
$(document).ready(function slider() {
$('#img1').show('fade', 500);
$('#img1').delay(5000).hide("slide", { direction: 'left' }, 500);
});
var count = 2;
setInterval(function loop() {
var all = document.getElementsByTagName('li').length; // <-- i got the li elements so i did the same to prev and next
$('#img' + count).show('slide', { direction: 'right' }, 500);
$('#img' + count).delay(5500).hide('slide', { direction: 'left' }, 500);
if (count === all) {
count = 1;
} else {
count += 1;
}
}, 6500);
var sliderInt = 1;
var sliderNext = 2;
document.getElementsByClassName('prev').onclick = function prev() { // <-- not working
console.log('clicked prev');
var newSlide = sliderInt - 1;
showSlide(newSlide);
return false;
}
document.getElementsByClassName('next').onclick = function next() { // <-- not working
console.log('clicked next');
var newSlide = sliderInt + 1;
showSlide(newSlide);
return false;
}
function stopLoop() {
window.clearInterval(loop());
}
function showSlide(id) { // <-- this function doesn't work from prev and next
stopLoop(); // <-- I want to stop the loop() function when prev and next is clicked
if (id > count) {
id = 1;
} else if (id < 1) {
id = count;
}
$('li').hide('slide', { direction: 'left' }, 500);
$('#img' + id).show('slide', { direction: 'right' }, 500);
sliderInt = id;
sliderNext = id + 1;
window.slider(); // <-- I want to call the function slider here
}
a fix demo will be much appreciated :)
When you use the document.getElementsByClassName('prev').onclick you got an array. Use it like below
document.getElementsByClassName('prev')[0].onclick
document.getElementsByClassName('next')[0].onclick
getElementsByClassName returns a HTMLCollection. So you need to pass the relevant index to which you want to add the onclick function
document.getElementsByClassName('next')[0]
But this will attach the event only on the first element in the collection.
An more relevant example is
var list = document.getElementsByClassName('next or prev');
for (var i = 0, len = list.length; i < len; i++) {
(function(i){ // creating closure
list[i].addEventListener('click',function(){
// code you want to execute on click of next or prev
}
}(i))
}
As you are already using jquery you can avoid all this if you use class selector
$('.next or .prev').on('click',function(event){
// relevant code
})

JQuery Auto Click

I have a problem, I have 3 button lets say it's called #pos1, #pos2 and #pos3.
I want to makes it automatically click #pos1 button in 2 seconds, after that click the #pos2 after another 2 seconds, and #pos3 after another 2 seconds,
after that back to the #pos1 in another 2 seconds and so on via jQuery.
HTML
<button id="pos1">Pos1</button>
<button id="pos2">Pos2</button>
<button id="pos3">Pos3</button>
Anyone can help me please?
Try
$(function() {
var timeout;
var count = $('button[id^=pos]').length;
$('button[id^=pos]').click(function() {
var $this = $(this);
var id = $this.attr('id');
var next = parseInt(id.substring(4), 10) + 1;
if( next >= count ){
next = 1
}
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(function() {
$('#pos' + next).trigger('click');
}, 2000);
})
timeout = setTimeout(function() {
$('#pos1').trigger('click');
}, 2000);
})
var posArray = ["#pos1", "#pos2", "#pos3"];
var counter = 0;
setInterval(function() {
$(posArray[counter]).triggerHandler('click');
counter = ((counter<2) ? counter+1 : 0);
}, 2000);
That should do the trick, though you did not mention when you want it to stop running.
Well I don't know what you already have but technically it could be done via triggerHandler()
var currentPos = 1,
posCount = 3;
autoclick = function() {
$('#pos'+currentPos).triggerHandler('click');
currentPos++;
if(currentPos > posCount) { currentPos = 1; }
};
window.setInterval(autoclick,2000);
If I have understood you question right, you need to perform click in a continuous loop in the order pos1>pos2>pos3>pos1>pos2 and so on. If this is what you want, you can use jQuery window.setTimeout for this. Code will be something like this:
window.setTimeout(performClick, 2000);
var nextClick = 1;
function performClick() {
if(nextClick == 1)
{
$("#pos1").trigger("click");
nextClick = 2;
}
else if(nextClick==2)
{
$("#pos2").trigger("click");
nextClick = 3;
}
else if(nextClick == 3)
{
$("#pos3").trigger("click");
nextClick = 1;
}
window.setTimeout(performClick, 2000);
}
This is quite buggy but will solve your problem.
using setInterval()
Calls a function or executes a code snippet repeatedly, with a fixed time delay between each call to that function.
var tempArray = ["pos1", "pos2", "pos3"]; //create an array to loop through
var arrayCounter = 0;
setInterval(function() {
$('#' + tempArray[arrayCounter ]).trigger('click');
arrayCounter = arrayCounter <2 ? arrayCounter +1 : 0;
}, 2000);
fiddle here
check your console for fiddle example

javascript 'over-clicking' bug

I have a bug in Javascript where I am animating the margin left property of a parent container to show its child divs in a sort of next/previous fashion. Problem is if clicking 'next' at a high frequency the if statement seems to be ignored (i.e. only works if click, wait for animation, then click again) :
if (marLeft === (-combinedWidth + (regWidth) + "px")) {
//roll margin back to 0
}
An example can be seen on jsFiddle - http://jsfiddle.net/ZQg5V/
Any help would be appreciated.
Try the below code which will basically check if the container is being animated just return from the function.
Working demo
$next.click(function (e) {
e.preventDefault();
if($contain.is(":animated")){
return;
}
var marLeft = $contain.css('margin-left'),
$this = $(this);
if (marLeft === (-combinedWidth + (regWidth) + "px")) {
$contain.animate({
marginLeft: 0
}, function () {
$back.fadeOut('fast');
});
} else {
$back.fadeIn(function () {
$contain.animate({
marginLeft: "-=" + regWidth + "px"
});
});
}
if (marLeft > -combinedWidth) {
$contain.animate({
marginLeft: 0
});
}
});
Sometimes is better if you create a function to take care of the animation, instead of writting animation code on every event handler (next, back). Also, users won't have to wait for the animation to finish in order to go the nth page/box.
Maybe this will help you:
if (jQuery) {
var $next = $(".next"),
$back = $(".back"),
$box = $(".box"),
regWidth = $box.width(),
$contain = $(".wrap")
len = $box.length;
var combinedWidth = regWidth*len;
$contain.width(combinedWidth);
var currentBox = 0; // Keeps track of current box
var goTo = function(n) {
$contain.animate({
marginLeft: -n*regWidth
}, {
queue: false, // We don't want animations to queue
duration: 600
});
if (n == 0) $back.fadeOut('fast');
else $back.fadeIn('fast');
currentBox = n;
};
$next.click(function(e) {
e.preventDefault();
var go = currentBox + 1;
if (go >= len) go = 0; // Index based, instead of margin based...
goTo(go);
});
$back.click(function(e) {
e.preventDefault();
var go = currentBox - 1;
if (go <= 0) go = 0; //In case back is pressed while fading...
goTo(go);
});
}
Here's an updated version of your jsFiddle: http://jsfiddle.net/victmo/ZQg5V/5/
Cheers!
Use a variable to track if the animation is taking place. Pseudocode:
var animating = false;
function myAnimation() {
if (animating) return;
animating = true;
$(this).animate({what:'ever'}, function() {
animating = false;
});
}
Crude, but it should give you the idea.
Edit: Your current code works fine for me as well, even if I jam out on the button. On firefox.

ExtJs Animation - Show/Fade elements ad infinitum

I'm trying to create a never-ending looping animation using ExtJs, but am hitting the obvious barrier - calling a function recursively that potentially never ends tends to fill the stack pretty quickly. My (non-functioning) code is below; this function gets passed an array of strings which correspond to the divs to be shown and hidden. Without the callback, the code produces the desired show/fade effect, but obviously only the once.
// 'divArray' - a string array containing the IDs of the divs to cycle.
function cycleDivs (divArray) {
var len, i, el;
// Sanity check.
if (divArray === null || !Ext.isArray(divArray) || divArray.length === 0) {
return;
}
// Get the length of the array; we'll need this to loop the animation.
len = divArray.length;
for (i = 0; i < len; i++) {
// Get the element.
el = Ext.fly(divArray[i]);
// Sanity check.
if (el) {
el.sequenceFx();
el.fadeIn({
endOpacity: 1,
easing: 'easeOut',
duration: 3
});
el.pause(2)
el.fadeOut({
endOpacity: 0,
easing: 'easeOut',
duration: 3
});
if (i === len - 1) {
// Recursive call if this is the last element in the array.
el.callback = cycleDivs(divArray);
}
}
}
}
Caveat: I've achieved this sort of effect before with jQuery and its wide variety of plugins, but as it's a work project I can only use the library I've got, which is ExtJs.
Thanks in advance for any pointers.
I ended up porting parts of jquery.slideShow by Marcel Eichner, and the SO answer for execute a method on an existing object with window.setInterval for my requirements. Code below for any who might find a use for it. I also now pass the elements to be animated into the constructor, rather than just their IDs.
// Constructor function.
MyNamepsace.Slideshow = function (divArray) {
// Persist the div array.
this.divArray = divArray;
// Internal vars
this.numSlides = this.divArray.length;
this.current = 0;
if (this.current >= this.numSlides) {
this.current = this.numSlides - 1;
}
this.last = false;
this.interval = false;
};
Ext.apply(MyNamespace.Slideshow.prototype, {
// Initialisation method.
init: function() {
this.gotoSlide(this.current);
this.auto();
},
// This is a "toy" version of "bind".
bind: function(object, method) {
return function() {
method.call(object);
};
},
// Set up automatic slideshow.
auto: function() {
this.interval = window.setInterval(this.bind(this, this.next), 3000);
},
// Stop automatic slideshow.
stopAuto: function() {
if (this.interval) {
window.clearInterval(this.interval);
this.interval = false;
}
},
// Go to next slide.
next: function() {
this.gotoSlide(this.current + 1);
},
// Go to specific slide.
gotoSlide: function(index) {
var oldSlide, newSlide;
if (index < 0) {
index = this.numSlides - 1;
}
if (index >= this.numSlides) {
index = 0;
}
if (index === this.current) {
return;
}
// get slide elements
oldSlide = this.divArray[this.current];
newSlide = this.divArray[index];
this.stopAuto();
// Start transition
oldSlide.fadeOut({
easing: 'easeOut',
duration: 3,
callback: this.auto,
useDisplay: true,
scope: this
});
newSlide.fadeIn({
easing: 'easeIn',
duration: 3
});
this.last = this.current;
this.current = index;
}
});
I would do something like :
var cycleDivs = function(divs) {
var i = 0;
var fadeFn = function() {
divs[i].sequenceFx().fadeIn({
...
}).pause(2).fadeOut({
...,
callback : fadeFn
});
i = (i+1)%divs.length;
}
fadeFn();
}
Of course I removed all sanity checks ;)

Categories

Resources