Skip to content
View in the app

A better way to browse. Learn more.

Web Designer Forum

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

Javascript menu animation....almost

Featured Replies

Hello,

 

I'm having a real nightmare making my menu images animate out and back again, triggered from a mouseover/mouseout event. I finally got it working, but I'm getting a horrible 'flickering' effect when I mousover/mouseout quickly (I'm assuming this is because the functions are in conflict).

 

I need to find a way for the menuSlideIn() function to be interrupted and replaced by the menuSlideOut() function on mouseout so the transition is smooth regardless of how fast you run the mouse over the list items/anchors.

 

I'm very much a beginner at this (as you'll see) and I'd be extremely grateful if anyone can offer me some help regarding a solution to this...having sat here looking at this for a week and pulling up nothing but jQuery tutorials on Google, my brain is now officially jelly!

 

Here's my Javascript:

 

window.onload = function() {
menuAnchor1.addEventListener('mouseover', animateIn1, false);
menuAnchor2.addEventListener('mouseover', animateIn2, false);
menuAnchor3.addEventListener('mouseover', animateIn3, false);
menuAnchor4.addEventListener('mouseover', animateIn4, false);
menuAnchor5.addEventListener('mouseover', animateIn5, false);
menuAnchor6.addEventListener('mouseover', animateIn6, false);
menuAnchor7.addEventListener('mouseover', animateIn7, false);

function animateIn1() {
	menuHighlight = document.getElementById('menuHighlight1');
	menuSlideIn(menuHighlight, -340, 0, 0);
}

function animateIn2() {
	menuHighlight = document.getElementById('menuHighlight2');
	menuSlideIn(menuHighlight, -340, 0, 0);
}

function animateIn3() {
	menuHighlight = document.getElementById('menuHighlight3');
	menuSlideIn(menuHighlight, -340, 0, 0);
}

function animateIn4() {
	menuHighlight = document.getElementById('menuHighlight4');
	menuSlideIn(menuHighlight, -340, 0, 0);
}

function animateIn5() {
	menuHighlight = document.getElementById('menuHighlight5');
	menuSlideIn(menuHighlight, -340, 0, 0);
}

function animateIn6() {
	menuHighlight = document.getElementById('menuHighlight6');
	menuSlideIn(menuHighlight, -340, 0, 0);
}

function animateIn7() {
	menuHighlight = document.getElementById('menuHighlight7');
	menuSlideIn(menuHighlight, -340, 0, 0);
}


menuAnchor1.addEventListener('mouseout', animateOut1, false);
menuAnchor2.addEventListener('mouseout', animateOut2, false);
menuAnchor3.addEventListener('mouseout', animateOut3, false);
menuAnchor4.addEventListener('mouseout', animateOut4, false);
menuAnchor5.addEventListener('mouseout', animateOut5, false);
menuAnchor6.addEventListener('mouseout', animateOut6, false);
menuAnchor7.addEventListener('mouseout', animateOut7, false);

function animateOut1() {
	if(menuHighlight = document.getElementById('menuHighlight1')) {
		menuSlideOut(menuHighlight, 0, -340, 0);
	}
}

function animateOut2() {
	if(menuHighlight = document.getElementById('menuHighlight2')) {
		menuSlideOut(menuHighlight, 0, -340, 0);
	}
}

function animateOut3() {
	if(menuHighlight = document.getElementById('menuHighlight3')) {
		menuSlideOut(menuHighlight, 0, -340, 0);
	}
}

function animateOut4() {
	if(menuHighlight = document.getElementById('menuHighlight4')) {
		menuSlideOut(menuHighlight, 0, -340, 0);
	}
}

function animateOut5() {
	if(menuHighlight = document.getElementById('menuHighlight5')) {
		menuSlideOut(menuHighlight, 0, -340, 0);
	}
}

function animateOut6() {
	if(menuHighlight = document.getElementById('menuHighlight6')) {
		menuSlideOut(menuHighlight, 0, -340, 0);
	}
}

function animateOut7() {
	if(menuHighlight = document.getElementById('menuHighlight7')) {
		menuSlideOut(menuHighlight, 0, -340, 0);
	}
}
}



/*
******************** SLIDE MENU HIGHLIGHT ********************
*/


function menuSlideIn(menuHighlight, currentBgPositionX, targetBgPositionX, currentBgPositionY) {
if(currentBgPositionX < targetBgPositionX) {
	currentBgPositionX += Math.ceil((targetBgPositionX - currentBgPositionX) / 20);
	menuHighlight.style.backgroundPosition = currentBgPositionX + 'px' + ' ' + currentBgPositionY;
	setTimeout(function() {menuSlideIn(menuHighlight, currentBgPositionX, targetBgPositionX, currentBgPositionY);}, 1);
}
}

function menuSlideOut(menuHighlight, currentBgPositionX, targetBgPositionX, currentBgPositionY) {
if(targetBgPositionX < currentBgPositionX) {
	currentBgPositionX -= Math.ceil((currentBgPositionX - targetBgPositionX) / 20);
	menuHighlight.style.backgroundPosition = currentBgPositionX + 'px' + ' ' + currentBgPositionY;
	setTimeout(function() {menuSlideOut(menuHighlight, currentBgPositionX, targetBgPositionX, currentBgPositionY);}, 1);
}
}

 

Here's my HTML which the above code relates to:

 

<ul id="bullet" class="bullet">
<li>
	<div id="menuHighlight1"><!-- --></div>
	<div id="menuItem">
		<a href="" id="menuAnchor1">Some Text</a>
		<p class="subList">Some Text</p>
	</div>
</li>

<li>
	<div id="menuHighlight2"><!-- --></div>
	<div id="menuItem">
		<a href="" id="menuAnchor2">Some Text</a>
		<p class="subList">Some Text</p>
	</div>
</li>

<li>
	<div id="menuHighlight3"><!-- --></div>
	<div id="menuItem">
		<a href="" id="menuAnchor3">Some Text</a>
		<p class="subList">Some Text</p>
	</div>
</li>

<li>
	<div id="menuHighlight4"><!-- --></div>
	<div id="menuItem">
		<a href="" id="menuAnchor4">Some Text</a>
		<p class="subList">Some Text</p>
	</div>
</li>

<li>
	<div id="menuHighlight5"><!-- --></div>
	<div id="menuItem">
		<a href="" id="menuAnchor5">Some Text</a>
		<p class="subList">Some Text</p>
	</div>
</li>

<li>
	<div id="menuHighlight6"><!-- --></div>
	<div id="menuItem">
		<a href="" id="menuAnchor6">Some Text</a>
		<p class="subList">Some Text</p>
	</div>
</li>

<li>
	<div id="menuHighlight7"><!-- --></div>
	<div id="menuItem">
		<a href="" id="menuAnchor7">Some Text</a>
		<p class="subList">Some Text</p>
	</div>
</li>
</ul>

 

Thank you in advance...

  • Author

Wow at all that JavaScript you've written. I've have a google of JQuery drop down menus personally.

 

Hi rallport,

 

I agree, jQuery would definitely be a LOT quicker. I'm just trying to learn 'under-the-hood' Javascript.

 

Nothing wrong with the libraries in my opinion.

 

Thanks for your reply

At the moment, you're not clearing your setTimeout functions via clearTimeout.

Basically, when one animation is in progress, it will interfere with another one unless you use clearTimeout to get rid first.

So the event (mouseover/mouseout) needs to look for a reference to an existing setTimeout call.

Sorry for not explaining very well :(

I'm off to bed now (v.tired) but have a google for clearTimeout.

If you haven't got it sorted tomorrow, I'll post an example for you.

  • Author

At the moment, you're not clearing your setTimeout functions via clearTimeout.

Basically, when one animation is in progress, it will interfere with another one unless you use clearTimeout to get rid first.

So the event (mouseover/mouseout) needs to look for a reference to an existing setTimeout call.

Sorry for not explaining very well :(

I'm off to bed now (v.tired) but have a google for clearTimeout.

If you haven't got it sorted tomorrow, I'll post an example for you.

 

Hi ElanMan,

 

Thank you for your help.

 

As you suggested I looked up the clearTimeout function, and it does indeed seem to be along the lines of what I need.

 

I followed the online documentation to the letter but sadly, that alone hasn't worked.

 

I feel like I need some kind of conditional statement whereby the menuSlideIn() will run UNTIL you mouseout, in which case the menuSlideOut() function will stop it (clearTimeOut perhaps?), grab the currentBgPositionX value from menuSlideIn() and run backwards from there.

 

I just can't wrap my head around it. I'm struggling to write it in a way that I can make menuSlideOut() pick up the returned value from menuSlideIn()....My apologies for the long and confusing explanation!

 

Again, thank you for your help and patience

Ok, here's some sample code to get you started.

It might look a bit daunting at first but most of it is comments to try and explain what it is doing.

 

html:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
	<title>Moving Background</title>
	<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
	<link rel="stylesheet" media="screen" type="text/css" href="style.css" />
	<script type="text/javascript" src="moveBackground.js"></script>
</head>
<body>
	<ul id="mylist">
		<li>One</li>
	<li>Two</li>
	<li>Three</li>
	<li>Four</li>
	</ul>
</body>
</html>

 

css:

ul {
list-style-type:none;
}
li {
width: 360px;
height: 100px;
background:#beb397 url('rect2816.png') no-repeat;
margin:5px 0;
}

 

js: the important stuff!!

var moveBackGround = {
//this is the function that we call on pageload
init: function(){
	//loop through the links and add events to each
	var myList = document.getElementById('mylist');
	var links = myList.getElementsByTagName("li");
	for (var i=0; i < links.length; i++) {
		//for each link, set the background position so no image shows 
links[i].style.backgroundPosition = '-360px 0px';

		//for each link, add events for mouseover and mouseout
		//this specifies a listener for each event
		links[i].addEventListener("mouseover",moveBackGround.overListener,false);
links[i].addEventListener("mouseout",moveBackGround.outListener,false);
	}
},

//animate on mouseover
//increments the background position on mouseover
slideIn: function(object, targBg){
	//this is the important bit
	//here we clear any setTimeout function referenced by object.moveBgOut (set in slideOut function)
	clearTimeout(object.moveBgOut);
	currentBgPosition = parseInt(object.style.backgroundPosition);
if (currentBgPosition == targBg) {
	clearTimeout(object.moveBgOver); 
	 	}
if (currentBgPosition < targBg) {
	 	//the amount of pixels moved each iteration
	 //increase or decrease according to taste
currentBgPosition += 20;
object.style.backgroundPosition = currentBgPosition + "px 0px";
	}
 //another important part
 //create a reference to this setTimeout 
 //this lets us cancel it in the slideOut function
 //alter '50' at the end for a different speed
object.moveBgOver = setTimeout(function(){moveBackGround.slideIn(object,targBg)},50);
},

	//animate on mouseout
//decrements the background position on mouseout
	slideOut: function(object, targBg){
	//this is the important bit
	//here we clear any setTimeout function referenced by object.moveBgOver (set in slideIn function)
		clearTimeout(object.moveBgOver);
	currentBgPosition = parseInt(object.style.backgroundPosition);
	if (currentBgPosition == targBg) {
	clearTimeout(object.moveBgOut); 
	}
	if (currentBgPosition > targBg) {
		//the amount of pixels moved each iteration
	currentBgPosition -= 20;
	object.style.backgroundPosition = currentBgPosition + "px 0px";
	}
	//another important part
	//create a reference to this setTimeout 
	//this lets us cancel it in the slideIn function
	//alter '50' at the end for a different speed
	object.moveBgOut = setTimeout(function(){moveBackGround.slideOut(object,targBg)},50);
	},

//these two functions are called by addEventListener in the init() function
//called on mouseover
overListener: function(event){
	var link = this;
	this._timer = setTimeout(function() {
	moveBackGround.slideIn(link, 0);},200);
},

//called on mouseout
outListener: function(event){
	var link = this;
	this._timer = setTimeout(function() {
		moveBackGround.slideOut(link, -360);},200);
	}
};

//finally, call moveBackground onload	
window.onload = function() {
moveBackGround.init();
}

I've put an example up for you here.

Read the comments in the code. Hopefully, they will make it easier for you to grasp what is going on regarding clearTimeout.

Also, at the moment, this won't work in IE. You'll need to account for attachEvent as well (IE is a pain in the arse regarding js) so there is a bit more work for you yet :)

 

Finally, if you're looking to buy a book on js, 'Simply Javascript' is a great read and covers this type of stuff in much better detail.

 

Good luck :)

 

ps this forum always shags my code indentation. Sorry if it looks a mess.

  • Author

Wow! I can't tell you how much of a help this has been...thank you!

 

Having only just read your example, I've yet to try it out but It's already made a couple of things crystal clear that I was at a dead end with. For example, adding an event listener to the links as you have, instead of my rather long way of doing it. Also, the previously discussed setting and canceling of Timeouts.

 

I hope to spend as much of today getting this working as I can, so I will let you know how it goes.

 

Thanks again ElanMan for taking the time to help :)

Create an account or sign in to comment

Account

Navigation

Search

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.