I have a js file with two functions, one in plain javascript and one using jQuery. I want both to work on window load. But if I enable both, only the plain javascript function works. When I disabled the "highliter" function "slideSwitch" works ok. What is the problem? How can I fix this?
function slideSwitch() {
var active = $('#slideshow img.active'),
next;
if (active.length == 0){
active = $('#slideshow img:last');
};
next = active.next().length ? active.next() : $('#slideshow img:first');
active.addClass('last-active');
next.addClass('active')
.css({opacity:0.0})
.animate({opacity:1.0}, 1000, function() {
active.removeClass('active last-active');
});
};
function startSlideShow() {
setInterval("slideSwitch()", 5000);
};
function highliter() {
var current = document.location.pathname;
var nav = document.getElementById('pages');
var a_tags = nav.getElementsByTagName('a');
for (var i = 0; i <= a_tags.length; i++) {
if (a_tags[i].getAttribute('href') === current) {
a_tags[i].parentElement.className = 'highlited';
}
}
};
window.onload = function() {
highliter();
startSlideShow();
};
Is there a javascript error occurring in highliter()? I see two possible issues:
Your for loop is checking <= length instead of < length, meaning you're exceeding the size of the array.
I could see there not being an element on the page with an id of 'pages', which would mean nav is null and the subsequent access on nav results in a null reference.
It isn't parentElement, it is parentNode.
Related
Here is a code that should open and close my site's menu. The menu is divided to divs and each one is timed to enter the screen after the other.
<script type="text/javascript">
var s=0;
function menuOpen() {
if (s==0){
document.getElementById("menu_icon").src = "x.png";
document.getElementById("nav_menu").style.zIndex = "3";
$('.box-wrapper').each(function(index, element) {
setTimeout(function(){
element.classList.remove('loading');
}, index * 100);
});
s++;
} else {
document.getElementById("menu_icon").src = "menu_icon.png";
$('.box-wrapper').each(function(index, element) {
setTimeout(function(){
element.classList.add('loading');
}, index * 100);
});
s=0;
// how to make this part run after the menu has finished folding?
setTimeout(function(){
document.getElementById("nav_menu").style.zIndex = "1";
}, 1500);
}
}
</script>
The content of the page is at z-index 2. The menu when folded is at 1 and when open at 3.
Is there a way to run the command moving the menu to z-index 1 after the menu has finished folding completely?
Currently what I did was to time the animation (1600ms) and use setTimeout. But this timing will change if I'll add more lines to my menu or if someone is clicking rapidly on the menu icon.
I'm rather new to JS and Jquery so go easy on me (:
Thanks of your help.
Below you can find the code and link to jsfiddle. Unfortunetly jsfiddle blocks the animate method for unknown reason so I don't debug, but even if it code will not work :))) I hope you will cathch the idea. And also some explanation.
Firstly our items are hidden. There are two functions displayMenu and hideMenu, they are similar, but display - run animations from the top invisible, and hide - start hide items from the bottom visible. To prevent mess I use two flags and two classes, first openFlag it is say what animations should be played now. Our hide and display functions are recursive and after they end current animation(hide or show) they check openFlag, and play next hide/show or start another chain of hide/show functions. It is the most difficult to understand part) But important that with it you can click as many times as you want and all would be fine and would be never broken by clicks.
Two classes we use as animation-chain can change behaviour and we need the way to choose items that alredy visible or hidden, so this why after each animation we set only one of this classes and remove another.
Now there is one problem if all animation are ended when we click button we should start new chain of animations, but if chain has been already started we need just to switch openFlag, and when current animation stops, it will change the behaviour. So this is the reason for btnFlag it is 1 if no active chain-animations at this moment.
After the last execution of element of animation-chain it will call callback arg, that you should pass, also at this moment will set btnFlag to 0, that means that animation-chain stopped. The openFlag as you remember changed at moment og click.
function end() {
console.log("here you can set zIndex");
}
var openFlag = 0; //become 1 after we start open elems
var btnFlag = 1;
$(document).ready(function() {
$('.toggleMenu').click(function() {
if (!$('.menuBlocks').hasClass('visible')) {
if (openFlag == 0) {
openFlag = 1;
if (btnFlag) {
var items = $('.invisibleItem');
var amount = items.length;
displayMenu(0, amount, items, end);
}
}
} else {
openFlag = 0;
if (btnFlag) {
var items = $('.visibleItem');
var amount = items.length;
hideMenu(amount - 1, items, end);
}
}
});
});
function displayMenu(i, amount, items, callback) {
if (i < amount && openFlag) {
items[i].animate({
"width": "100px"
}, 1000, function() {
items[i].removeClass('invisibleItem');
items[i].addClass('visibleItem');
displayMenu(i + 1, amount, items);
});
} else if (!openFlag) {
var items = $('.visibleItem');
var amount = items.length;
hideMenu(amount - 1, items, makeToggleVisible);
} else {
btnFlag = 1; //all aniimations ended
callback();
}
}
function hideMenu(i, items, callback) {
if (i < 0 && openFlag) {
items[i].animate({
"width": "0px"
}, 1000, function() {
items[i].removeClass('visibleItem');
items[i].addClass('invisibleItem');
hideMenu(i - 1, amount, items);
});
} else if (!openFlag) {
var items = $('.invisibleItem');
var amount = items.length;
displayMenu(0, amount, items, makeToggleVisible);
} else {
btnFlag = 1; //all animations are ended
callback();
}
}
https://jsfiddle.net/ShinShil/nrtyshv5/4/
Ok fixed it.
I moved everything to jquery. Used animate and promise.
This is what came out at the end. It is a side menu that will open it's li elements one-by-one.
var s=0;
var navMenu = document.getElementById("nav_menu");
var navBtn = document.getElementById("btn");
$(document).ready(function(){
$("button").click(function(){
if (s==0) {
navMenu.style.zIndex = "4";
navBtn.classList.add('close');
$('ul').each(function() {
$(this).children().each(function(i) {
$(this).delay(i * 100).animate({left:0});
});
});
$( "li" ).promise().done(function() {
navMenu.style.zIndex = "4";
});
s++;
}
else {
navBtn.classList.remove('close');
$('ul').each(function() {
$(this).children().each(function(i) {
$(this).delay(i * 100).animate({left:"100%"});
});
});
s=0;
$( "li" ).promise().done(function() {
navMenu.style.zIndex = "1";
});
}
});
});
and with CSS transitions.
var s=0;
function menuOpen() {
if (s==0){
document.getElementById("menu_icon").src = "x.png";
document.getElementById("nav_menu").style.zIndex = "3";
$('.box-wrapper').each(function(index, element) {
setTimeout(function(){
element.classList.remove('loading');
}, index * 100);
});
s++;
$("#last").bind("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", function(){
document.getElementById("nav_menu").style.zIndex = "3";
});
} else {
document.getElementById("menu_icon").src = "menu_icon.png";
$('.box-wrapper').each(function(index, element) {
setTimeout(function(){
element.classList.add('loading');
}, index * 100);
});
s=0;
$("#last").bind("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", function(){
document.getElementById("nav_menu").style.zIndex = "1";
$("#nav_menu").scrollTop(0);
});
}
}
I'm trying to create a slider with three slides. Originally, the slides will display as a loop. My goal is, if a button like #fancy_1 is clicked, the loop with a function name "runem" will be stopped and the loop just ends there. I tried put a stop(true,true), but with no success. No matter whether the button is clicked or not, the loop is always running. Does anyone have any idea how to achieve this?
(function($) {
$(document).ready(function() {
function runem() {
var allofEm = $('.j_fancy .j_fancy_block');
var $active = allofEm.eq(0);
$active.show();
var $next = $active.next();
var timer = setInterval(function() {
$next.fadeIn();
$active.hide();
$active = $next;
$next = (allofEm.last().index() == allofEm.index($active)) ?
$next = allofEm.eq(0) : $active.next();
}, 5000);
}
runem();
$("#fancy_1").click(function() {
$('.j_fancy_block').eq(0).show();
$('.j_fancy_block').eq(1).hide();
$('.j_fancy_block').eq(2).hide();
runem().stop(true, true); //this doesn't work.
})
$("#fancy_2").click(function() {
$('.j_fancy_block').eq(0).hide();
$('.j_fancy_block').eq(1).show();
$('.j_fancy_block').eq(2).hide();
runem().stop(true, true); //this doesn't work.
})
$("#fancy_3").click(function() {
$('.j_fancy_block').eq(0).hide();
$('.j_fancy_block').eq(1).hide();
$('.j_fancy_block').eq(2).show();
runem().stop(true, true); //this doesn't work.
})
})
}(jQuery));
Have you tried using clearInterval()? The reason that stop() was not working is because that is a function to stop jQuery animations, not Javascript setInterval - so setInterval will continue to run
The way I would suggest using it is like so...
var boxInterval;
function runem(){
/*Function Stuff*/
boxInterval = setInterval(function(){...});
}
function stopBox(){
clearInterval(boxInterval);
}
https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval#Example
I've looked all over the internet and I can't seem to find a good way to do this.
I've got an accordion menu that I've built primarily using addClass/removeClass and css. It has special functionality, the accordion tabs open after a delay on mouseover and they open and close on click. I can currently open all of them at once, but I'd like to limit this to 2 or 3 with the earliest selected panel closing after I hit that limit. So I'd either need to make the classes numbered and switch them on every action, or perhaps apply a variable that keeps track of the order in which the panels were selected and switch them.
Below is the code I have so far. I've only been able to get as far as keeping count of how many tabs there currently are open. Does anyone have an idea as to what the best way to approach this is?
var timer;
var counter = 0;
$('li.has-dropdown').mouseenter(function() {
dd_item = $(this);
if(!$(this).hasClass('expand-tab')){
timer = setTimeout ( function () {
$(dd_item).addClass('expand-tab');
counter++;
}, 200);
};
}).mouseleave(function(){
clearTimeout(timer);
console.log(counter);
}).click(function() {
if ($(this).hasClass('expand-tab')){
$(this).removeClass('expand-tab');
counter--;
console.log(counter);
}else{
$(this).addClass('expand-tab');
console.log(counter);
}
});
Add a incrementting data-index to each opened tab.
count the tabs on the end of the hover effect, if they are to many, sort them by the index, and hide the lowest/oldest.
var timer;
var index = 1;
$('li.has-dropdown').mouseenter(function() {
dd_item = $(this);
if(!$(this).hasClass('expand-tab')){
timer = setTimeout ( function () {
$(dd_item).addClass('expand-tab');
$(dd_item).attr('data-index', index++);
counter++;
}, 200);
};
}).mouseleave(function(){
clearTimeout(timer);
console.log(counter);
}).click(function() {
$(this).taggleClass('expand-tab'); // see jQuery toggleClass();
$(this).attr('data-index', index++);//this will add index on closed tabs also.. but it does not matter at the end.
});
if($('.expand-tab').length> 3){
//custom inline sorting function.
var expanded_tabs = $('.expand-tab').sort(function (a, b) {
return (parseInt( $(a).attr('data-index')) < parseInt( $(b).attr('data-index')) ? -1 : 1 ;
});
//time out .. effect etc.
expanded_tabs[0].removeClass('expand-tab');
}
P.S I don't like havving Hover and Click in the same place ... try to separate the events and call a unified collapseIfToMany function in on each event
This is a corrected version. I decided to use a variable for the maximum panels opened, this way you don't have to dig if you decide you want to change it, or if you add more to the code.
var timer;
var index = 1;
var maxOpen = 2;
$('li.has-dropdown').mouseenter(function() {
dd_item = $(this);
if(!$(this).hasClass('expand-tab')){
timer = setTimeout ( function () {
$(dd_item).addClass('expand-tab');
$(dd_item).attr('data-index', index++);
collapseIfTooMany();
}, 200);
};
}).mouseleave(function(){
clearTimeout(timer);
}).click(function() {
$(this).toggleClass('expand-tab'); // see jQuery toggleClass();
$(this).attr('data-index', index++);//this will add index on closed tabs also.. but it does not matter at the end.
});
function collapseIfTooMany(){
if($('.expand-tab').length > maxOpen){
//custom inline sorting function.
var expanded_tabs = $('.expand-tab').sort(function (a, b) {
return (parseInt( $(a).attr('data-index')) < parseInt( $(b).attr('data-index'))) ? -1 : 1 ;
});
//time out .. effect etc.
$(expanded_tabs[0]).removeClass('expand-tab');
}
};
Im trying to pause a timed slideshow when you're hovering over a div
<div id="play_slide" onMouseOver="clearTimeout(playTime)"></div>
If i put onMouseOver="clearTimeout(playTime)" inside an li on the page, it'll pause, so I know my code is correct, it just wont work on the div! Also if i get rid of the id, it will alert when i put an alert function into an event handler
This is the js.
var playTime;
function playSlide()
{
var slideshow = document.getElementById("play_slide").style;
var images = new Array("an", "complete", "red", "thirteen");
indexPlay++;
if(indexPlay > images.length - 1)
{
indexPlay = 0;
}
slideshow.backgroundImage = "url('assets/images/play/"+images[indexPlay]+".png')";
playTime = setTimeout("playSlide()", 2500);
}
you can see this here: www.nicktaylordesigns.com/work.html
I would do it like this:
( and no inline script... just <div id="play_slide">Something</div> )
var playTime;
var indexPlay = 0;
var slideElement;
window.onload = function () {
slideElement = document.getElementById("play_slide");
slideElement.addEventListener('mouseenter', function () {
console.log('stop');
clearTimeout(playTime);
});
slideElement.addEventListener('mouseleave', function () {
console.log('continue');
playTime = setTimeout(playSlide, 2500);
});
playSlide();
}
function playSlide() {
var slideshow = slideElement.style;
var images = new Array("an", "complete", "red", "thirteen");
indexPlay++;
if (indexPlay > images.length - 1) {
indexPlay = 0;
}
slideshow.backgroundImage = "url('assets/images/play/" + images[indexPlay] + ".png')";
playTime = setTimeout(playSlide, 2500);
}
Fiddle
This issue is related to the script loading. Your script gets loaded after the DOM is processed so function doesn't get attached to the event.
If you are using jQuery then you can use below code.
$(function () {
$("#play_slide").mouseover(function(){
var playTime = 33;
clearTimeout(playTime);
});
});
If you don't want to use JQuery then you can do the same thing in JavaScript as below.
window.onload = function(){
document.getElementById("play_slide").onmouseover = function(){
var playTime = 33;
clearTimeout(playTime);
};
}
I got it! the div tag was somehow broken, I coded a div around it and gave it the event handlers and a class. That worked, then i simply changed the class to an id and got rid of the original div. Idk what was going on but it works now. Thanks for your suggestions!
Can you try this,
<div id="play_slide" onmouseover="StopSlide();">Stop</div>
<script>
var playTime;
var indexPlay;
function playSlide()
{
var slideshow = document.getElementById("play_slide").style;
var images = new Array("an", "complete", "red", "thirteen");
indexPlay++;
if(indexPlay > images.length - 1)
{
indexPlay = 0;
}
slideshow.backgroundImage = "url('assets/images/play/"+images[indexPlay]+".png')";
playTime = setTimeout("playSlide()", 2500);
}
function StopSlide(){
window.clearTimeout(playTime);
}
playSlide();
</script>
I created a very basic slideshow by repeating the jQuery effects with setInterval. However, there is a mismatch in timing after each cycle of the setInterval. After a few cycles, it leads to a visible mismatch in two parallel slide effects.
The code is
$(document).ready(function(){
var frontslides =$("#first li").length,
slideTime=500,
slideCycle=parseInt(slideTime*frontslides);
function frontSlide(){
$("#first li").each(function(n){
$(this).delay(slideTime*n).fadeIn(400).fadeOut(100);
$("#second li").eq(n).delay(slideTime*n).fadeIn(400).fadeOut(100);
});}
frontSlide();setInterval(frontSlide, slideCycle);});
The working example is here
To save your valuable time, its speed is fast, but it happens on any speed. After a few cycles, you can see that left and right slides are no longer synchronized.
First approach: setTimeout
You could move the transitioning to the next slide in a separate function and call it with setTimeout. Then, you just store the slide number in a variable and increment it after each function call.
$(document).ready(function(){
var firstSlides = $("#first li"),
secondSlides = $("#second li"),
nbSlides = firstSlides.length,
slideTime = 500,
nextSlide = 0;
function slideshow() {
firstSlides.eq(nextSlide).fadeIn(400).fadeOut(100);
secondSlides.eq(nextSlide).fadeIn(400).fadeOut(100);
nextSlide = (nextSlide+1) % nbSlides;
setTimeout(slideshow, slideTime);
}
slideshow();
});
Here's a fiddle of this first approach.
Second approach: promises
If you want to absolutely guarantee that both animations are completed when starting the next animation, you can use the new promises from jQuery 1.6. You can call $.promise() on both jQuery objects to get a promise which will be resolved when all animations of that element are completed. Then, you can set up a master promise with $.when(promises...) which will be resolved when all the given promises are resolved and set up a then handler.
$(document).ready(function(){
var firstSlides = $("#first li"),
secondSlides = $("#second li"),
nbSlides = firstSlides.length,
fadeInTime = 400,
fadeOutTime = 100,
nextSlide = 0;
function slideIn() {
var p1 = firstSlides.eq(nextSlide).fadeIn(400).promise();
var p2 = secondSlides.eq(nextSlide).fadeIn(400).promise();
$.when(p1, p2).then(slideOut);
}
function slideOut() {
var p1 = firstSlides.eq(nextSlide).fadeOut(100).promise();
var p2 = secondSlides.eq(nextSlide).fadeOut(100).promise();
nextSlide = (nextSlide+1) % nbSlides;
$.when(p1, p2).then(slideIn);
}
slideIn();
});
For a simple slideshow, you probably won't notice much of a difference. However, with promises you can make much more advanced transition sequences with funky timings while still maintaining synchronisation.
Here's a fiddle of that approach.
Set the animations to both run at the "same" time:
function frontSlide(){
$("#first li").each(function(n){
var _ = $(this);
setTimeout(function() {
_.fadeIn(400).fadeOut(100);
$("#second li").eq(n).fadeIn(400).fadeOut(100);
}, slideTime*n);
});
}
Since I opened my mouth, here is the version I came up with. I added a blocked flag in there to indicate an effects loop was currently running, hopefully preventing the script from overrunning itself. I also added a factor value so that if overrunning did occur, it would loop more quickly, to preserve the appearance that the loop was not disrupted.
If you have any other questions about it, just let me know. It's pretty straight forward.
$(document).ready(function() {
var $sel = $('#left, #right').find('li:first-child'),
slidetime = 200,
timein = 400,
timeout = 100,
blocked = false;
var fadeout = function() {
$sel.fadeOut(timeout, next);
};
var next = function() {
if ($sel.is(':last-child')) {
$sel = $sel.siblings(':first-child');
} else {
$sel = $sel.next();
}
blocked = false;
};
(function slider() {
var factor = .2;
if (!blocked) {
factor = 1;
blocked = true;
$sel.fadeIn(timein, fadeout);
}
setTimeout(slider, slidetime * factor);
})();
});
http://jsfiddle.net/j63vH/