How do I make this jquery animation delay work properly? - javascript

I have an html table that I am trying to add some animation to with jQuery. I am very close to what I want but have one outstanding issue. I will try to describe what I am doing with as cut-down a version of the table and scripts as possible.
The table is defined as follows;
<table>
<tbody class="group">
<tr>First</tr>
<tr class='slide'><div class='hidden'>Surprise!</div></tr>
</tbody>
<tbody class="group">
<tr>Second</tr>
<tr class='slide'><div class='hidden'>Surprise!</div></tr>
</tbody>
<tbody class="group">
<tr>Third</tr>
<tr class='slide'><div class='hidden'>Surprise!</div></tr>
</tbody>
</table>
The effect I want is for all of the <tr class='slide'> rows to be hidden once the page is loaded and then to slideDown into view when the mouse pointer enters the row above. I want the .slide row to remain in view as long as the mouse pointer remains in either the row above or the .slide row (contained within the <tbody> tags). I then want the .slide row to slideUp out of view when the mouse leaves either of the rows within the <tbody> tags. I have managed to do this successfully with the following code.
<script>
$(function() {
hideRows();
setupEventHandlers();
});
function hideRows() {
$("div.hidden").each(function() {
$(this).hide();
});
};
//The hideRows function is much more complicated in my actual page
//as I have to calculate the div height prior to hiding so it animates
//correctly. That's why I don't just set display:none; in the stylesheet.
function setupEventHandlers() {
$('.group').mouseenter(function() {
var tr = $(this).children('tr.slide');
tr.find("div.hidden").each(function() {
$(this).slideDown();
});
});
$('.group').mouseleave(function() {
var tr = $(this).children('tr.slide');
tr.find("div.hidden").each(function() {
$(this).slideUp();
});
});
};
//note that I have to animate the div rather than the tr
//as slidedown and slideup don't work on table rows
</script>
While this works it has the problem that if you run the mouse over multiple rows it sets up an enormous jiggly mess that can run for a long time until all of the animations finish. What I want to do is add a timeout so the animation doesn't start for a second or so after entering the <tbody>. If the mouse leaves the <tbody> before the animation is triggered it should be cancelled. When the mouse pointer leaves a <tbody> the slideUp effect should also be delayed by the same amount - My hope with this last requirement is that if you move down from one <tbody> to the next the slideDown and slideUp effects will occur at the same time so it just appears that the title row between them is moving up (I hope this makes some sort of sense). I have tried several ways of doing this but have been unsuccessful. My best effort is as follows;
var timer;
function setupEventHandlers() {
$('.group').mouseenter(function() {
var tr = $(this).children('tr.slide');
tr.find("div.hidden").each(function() {
div = $(this);
timer = setTimeout("div.slideDown();",1000);
});
});
$('.group').mouseleave(function() {
var tr = $(this).children('tr.slide');
tr.find("div.hidden").each(function() {
clearTimeout(timer);
div = $(this);
setTimeout("div.slideUp();",1000);
});
});
};
This almost works. If I quickly enter then leave a row, no animation occurs. If I enter a row and wait 1 second the next row slides down as planned. If I then exit the row to the side then the hidden row slides up 1 second later as planned. But, if I enter the row below things go wrong. The new row slides into view as expected after 1 second but the old row persists. My assumption is that my timer variable is being unintentionally destroyed. I tried being a bit more specific about the process by having an array of timers and assigning a specific one for each row but this made no difference. Javascript is not my strong suit as you may be able to tell.

Try using the hoverIntent plugin instead of attempting to simulate "delayed hovering" with mouseenter and mouseleave. I almost always use hoverIntent instead of plain hover.

OK, I finally solved this (and learned a lot too).
The code as provided works if I replace div with div2 in the mouseleave part of the code. Using the same variable name for up and down resulted in the animation not occurring. The only problem is that if I moused over numerous rows quickly I would still get unexpected results as this one variable name gets overused and abused.
My solution is to provide each .slide row with a unique id like so;
<tr class='slide'><div class='hidden'>Surprise!</div></tr>
I then use this id to generate a dynamic variable name using eval and reference this in a string in setTimeout. My final code looks like this.
var timer;
function setupEventHandlers() {
$('.group').mouseenter(function() {
var tr = $(this).children('tr.slide');
tr.find("div.hidden").each(function() {
id = this.id;
eval("divdown_" + id + " = $(this);");
downstring = "divdown_" + id + ".slideDown();";
timer = setTimeout(downstring,1000);
});
});
$('.group').mouseleave(function() {
var tr = $(this).children('tr.slide');
tr.find("div.hidden").each(function() {
clearTimeout(timer);
id = this.id;
eval("divup_" + id + " = $(this);");
upstring = "divup_" + id + ".slideUp();";
setTimeout(upstring,1000);
});
});
};
and works beautifully (in firefox at least, I'm too scared to try IE yet - I'm going to the gym instead).

Related

Refresh div during JQuery update

I have a html file with many div. Beside this I have a JQuery file too which run an AJAX request in every 30 seconds. In my JQuery If a condition met and the jQuery reloded 3 times I want to update my div-s. I tried to add a div, but it does not appear just when I reload my whole page. If I set to remove my div and not add, the JQuery removes my div, so it is very odd for me, because just the .add or .append or .html do not working. furthermore I set a class with invisibility too, and I also set that when the condition met the jQuery file remove the invisibility class, but the div do not want to appear. I am trying to create a sample code.
My html
<div class="row">
<div id="myclass" class= "invisible" >
<div <p> Please appear for me!<p></div>
</div>
</div>
My JQuery:
if (condition) {
$('#myclass').removeClass('invisible');
if (n >= 2) {
$('#myclass').addClass('invisible');
}
}
The main point is, If the conditions met the class not removes just When I reload my page. In the jQuery file the n is equal with the times when the AJAX reloaded with setInterval. My question is why my div not appear just when I reload my whole page? Also why remove without reload?
Thank you in advance the answers!
removeClass and addClass perfectly work. at first, you can check if you jQuery works, just try to write you script (with ".append",".html",".insertAfter") directly in browser console, else you can add some console.log in your code, when you change invisibility like:
console.log('removeClass');
$('#myclass').removeClass('invisible');
if (n >= 2) {
console.log('addClass');
$('#myclass').addClass('invisible');
}
for update some div content, you can use simple function for it. like -
call self page in get request, and replace div content.
function reloadMyDiv(selector){
var element = $(selector);
if(element.length===1){
$.get(window.location.href,{},function(response){
var new_page = $('<div></div>').append(response);
var new_element = new_page.find(selector);
if(new_element.length===1){
element.html( new_element.html() );
console.log('update');
}else{
console.log('need contains only one element, '+new_element.length+' found!');
}
});
}else{
console.log('need contains only one element, '+element.length+' found!')
}
};
and if you more _ not use your interval, is good practice for clear it.
var n = 0;
var myInterval = setInterval(function(){ myFunction() },30*1000);
function myFunction(){
n+=1;
//do, what you want to do
if(n>2){
clearInterval(myInterval);
}
}

How to add a second slideshow on the same webpage

I want my webpage to have two slideshows. I have one at the moment that works fine, but having trouble adding the second slideshow, as the images for the second slideshow appear outside of it.
I thought I could probably just copy and paste the code I did for the first slideshow and just change the div class names, but this did not work. I also have javascript controlling my slideshow but I didn't think copying the function I did for the first slideshow, would work for the second.
Can someone give me advice on how I can create the second slideshow using HTML, javascript and css?
Well, part of the key is to parameterize all of the required details - that is, don't hard-code things like the output container's id or that of the target image element if you decide to use a single image and change it's source.
Some approaches use an unordered-list and set it (with css) so only the first item is visible. Changing slides then becomes a matter of moving the li items around inside their container. I.e if one calls appendChild on the parent with the first li item as a parameter, it will be hidden since it's now the last li item and the 2nd item will now be the first and this will be the one displayed.
While the second approach is a little more straight forward, the 1st has the benefits of (0) not needing to know or care how many images there are - you simply move the first li item to be the last, or move the last one to be first and, (1) all the images are loaded at the start, so you don't get a small delay as each slide is shown for the first time and loaded.
Other approaches change the src of an image element.
I've utilized the second here. I've not bothered with prev/next buttons - this may mean this answer is beyond you at the moment. I would add prev/next functions inside the startSlideshow function and return the function itself - i.e return this;, rather than the id of the timer (which is to allow it to be stopped via clearInterval)
JS
function newEl(tag){return document.createElement(tag)}
function byId(id){return document.getElementById(id)}
window.addEventListener('load', onDocLoaded, false);
function onDocLoaded(evt)
{
var slideshow1TimerId = startSlideshow( ['uqrsGpO.jpg', 'vote-pedro.jpg'], 'slide1', 3000 );
var slideshow2TimerId = startSlideshow( ['zS0lOud.jpg', 'tree.png', 's13.bmp'], 'slide2', 1000 );
}
function startSlideshow(imgNameArray, idOfContainer, msPerSlide)
{
var container = byId(idOfContainer);
var tgtImgElem = newEl('img');
container.appendChild(tgtImgElem);
var timerId = setInterval(setSlideImg, msPerSlide);
var slideIndex = 0;
var numSlides = imgNameArray.length;
function setSlideImg()
{
tgtImgElem.src = imgNameArray[slideIndex];
slideIndex++;
if (slideIndex >= numSlides)
slideIndex = 0;
}
return timerId;
}
CSS
#slide1 img, #slide2 img
{
height: 128px;
}
HTML
<div id='slide1'></div>
<div id='slide2'></div>

Generic code to fade in divs with particular class, remove function after first run

I'm trying to create a generic function that can be placed just once in my site and work across multiple pages, nice and lightweight.
I want to be able to make certain divs on the site fade-in when you reach 10px above them on the scroll.
I want to do this by simply adding the following attributes to my divs:
.fade-in-block
#specific-block-name
The idea is that I could go through the site, add this class and an ID, and the animation would work.
I almost have it working except for one thing, the scroll listening constantly continues to console.log after the function has been called. I don't like this as it feels like it's going to be constantly trying to apply the animation, which won't really be seen from the front-end but I feel the constant maths behind the scenes could slow stuff down.
Here is my jQuery:
$('body .fade-in-block').each(function(){
var block = '#'+$(this).attr('id');
console.log('Block class is = '+block);
var offset = $(block).offset().top;
var $w = $(window).scroll(function () {
if ($w.scrollTop() > offset - 10) {
console.log('reached block turn-on point for '+block);
$(block).removeAttr('id'); // remove the ID from the element so the script doesn't continue to find the element
// fade and rise animation here
}
});
});
And here is a JSFiddle. It works just fine, but once you hit the block you'll see it logs constantly every pixel scrolled.
I tried to remedy this by removing the selecting id from the element once the event has occurred, but it continues to run.
Scroll and resize events both have this problem and the solution is said to be debouncing. However, I've never actually gotten debouncing to work properly. Instead I typically create a sort of switch that is turned off once the scroll condition has activated. In your case, since you have multiple elements, you would need to assign a switch to each element.
$(window).on('scroll', function(){
$('.fade-in-block').each(function(){
var appear = $(this).attr('data-appeared');
if(!appear){
$(this).attr('data-appeared', true);
//do something to $(this)
}
})
})
Here I'm adding a data attribute after it has appeared and checking for it again once it has.

JS: Applying focus effect on an element when scrolled to

Here is the fiddle: http://jsfiddle.net/fz5Yk/5/
All I want to achieve is to highlight (e.g adding a background color) the heading (in <strong> </strong> tag) of the section-3 when I scroll to section-3.
I'd like to know if there's a way for me to trigger certain events when I'm at a certain section. There must be a thing for this because when you scroll the page manually, you'll notice that, in the navigation menu, link to the section gets selected automatically, as if it was clicked.
Anything helpful will be much appreciated as I've been working on this since yesterday and hav yet to solve it.
There isn't any way to achieve this using CSS, so I edited the jquery.nav.min.js. (added only 4 lines) It works great now. http://jsfiddle.net/fz5Yk/10/
adjustNav=function(a,b,d){
var sec = a.find("."+d+">a").attr("href");
$(sec+">strong").css('background','none'); //Find and remove previous highlight of strong
a.find("."+d).removeClass(d);b.addClass(d); //ORIGINAL
sec = b.find("a").attr("href");
$(sec+">strong").css('background','aqua'); //Find and highlight the strong
};
EDIT: Animation added by request:
http://jsfiddle.net/fz5Yk/11/
add animateSomething function on top:
function animateSomething(sec) {
if(sec == "#section-2")
$("#testBlock").animate({
width:"50%",
opacity:0.5
}, 1500);
}
add animateSomething(sec); at the end of adjustNav function.
Voila!
EDIT FINAL: Animate AFTER scroll ends http://jsfiddle.net/fz5Yk/12/
In your click action have something like this:
$("#container .section strong").css('background-color', 'transparent');
$("#container .section strong:contains('" + $(this).text() + "')").css('background-color', 'yellow');
Not sure if that's what you want exactly, but you could use this to add a class to the every strong which is currently in view:
$(document).scroll(function(){
var t = $(this).scrollTop();
var b = t + $(this).height();
$('.section>strong').removeClass('highlight').filter(function(){
var ot = $(this).position().top;
return ot > t && ot < b;
}).addClass('highlight');
});
http://jsfiddle.net/fz5Yk/7/
But it is a bit pointless in my opinion because when it's not in view why do you want to remove the highlight? It won't be visible anyway!?
If you really only want the functionality for section 3 you could change $('.section>strong') to $('#section-3>strong')

jQuery: Why is by box collapsing on first click

The homepage of our website (legendboats.com) is 4 small cells, that if you click on, the large image changes. Pretty standard stuff, but there is a problem.
When you click on one of them the first time, the large area collapses before the new image reappears. This doesn't happen again, you can continue to click on different images, and they will fade in and out properly. Here is the code I'm using:
$('.home_boxes a.content_box').on('click', function(e) {
e.preventDefault();
var new_slide = this.hash;
$('#home_box div.active').fadeOut(function() {
$('#home_box ' + new_slide).fadeIn().addClass('active');
}).removeClass('active');
});
Any ideas on what I'm doing wrong, or any improvements?
It collapses because you're removing the class="active".
The reason it doesn't collapse after the first time is because jQuery is adding an inline style "display:block" which overrides the missing class="active".
Initially:
<div id="standard_equipment" class="active">
Subsequently:
<div id="more_power" style="display: block;" class="active">
Well I don't know why it happens, but I can see the symptoms. For some reason on first click the content collapses. This causes the odd flashing. If you set a min-height on home_box it sorts it out.
#home_box { min-height: 482px; }
Try doing this way
$('.home_boxes a.content_box').on('click', function(e) {
e.preventDefault();
var new_slide = this.hash;
$('#home_box div.active').fadeOut(function() {
var parent = this;
$('#home_box ' + new_slide).fadeIn(function(){
//You can shuffle following two lines and try
$(parent).removeClass('active');
$(this).addClass('active');
});
});
});
Hope this works you.

Categories

Resources