1

I wrote a code in JS for my future site and it's all fine but the only bad thing here is in menu the rectangle (div) moves with brakes. I mean it must be like in a site some features of which I want to have on my own. http://lusens.ru/ There on main menu the rectangle mouse very smoothly. I can't understand why in my case it doesn't happen.

This is a part of html

<body>
    <ul >           
        <li id="a1" onmouseover="highlightMenu('a1')">Первый пункт</li>
        <li id="a2" onmouseover="highlightMenu('a2')">Второй пункт точка</li>
        <li id="a3" onmouseover="highlightMenu('a3')">Третьий пункт точка и запятая</li>
        <li id="a4" onmouseover="highlightMenu('a4')">Четвёртый пункт</li>
    </ul>
<div id="d1"></div>

and the JS file

function highlightMenu(id) {
    time = 0;
    

    var rect = document.getElementById(id).getBoundingClientRect();
    var width = document.getElementById(id).offsetWidth;
    var idTop = rect.top;
    var idLeft = rect.left;

    var rect1 = document.getElementById('d1').getBoundingClientRect();
    var shadowWidth = document.getElementById('d1').offsetWidth;
    var shadowLeft = rect1.left;
    var shadowTop = rect1.top;

   
    if (shadowLeft < idLeft) {
        for (i = shadowLeft, time = 50; i < idLeft - 3; i++, time += 5) {
            setTimeout("document.getElementById('d1').style.left='" + i + "px'", time);
        }
    } else {
        for (i = shadowLeft, time = 50; i > idLeft - 3; i--, time += 5) {
            setTimeout("document.getElementById('d1').style.left='" + i + "px'", time);
        }
    }
            
    if (shadowWidth < width) {
        for (i = shadowWidth; i < width + 10; i++, time += 0.01) {
            setTimeout("document.getElementById('d1').style.width='" + i + "px'", time);
        }
    } else {
        for (i = shadowWidth; i > width + 10; i--, time += 0.01) {
            setTimeout("document.getElementById('d1').style.width='" + i + "px'", time);
        }
    }


    if (shadowLeft < idLeft) {

        for (i = idLeft + 3; i < idLeft + 20; i++, time += 25) {
            setTimeout("document.getElementById('d1').style.left='" + i + "px'", time);

        }
        for (i = idLeft + 20; i > idLeft - 5; i--, time += 50) {
            setTimeout("document.getElementById('d1').style.left='" + i + "px'", time);

        }
    } else {
        for (i = idLeft - 3; i > idLeft - 20; i--, time += 25) {
            setTimeout("document.getElementById('d1').style.left='" + i + "px'", time);

        }
        for (i = idLeft - 20; i < idLeft - 5; i++, time += 50) {
            setTimeout("document.getElementById('d1').style.left='" + i + "px'", time);

        }

    }
}

http://jsfiddle.net/m2SBm/

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Vadim
  • 85
  • 1
  • 9

1 Answers1

1

Using the onmouseover causes to fire the event handler function multiple times, here is better to use the onmouseenter event. And also instead of scheduling 50 timeouts, rather use single timeout and global variables, so that the animation will be stoppable.

HTML:

<ul id="menu">
    <li>Первый пункт</li>
    <li>Второй пункт точка</li>
    <li>Третьий пункт точка и запятая</li>
    <li>Четвёртый пункт</li>
</ul>
<div id="d1"></div>

Javascript:

var rect, rect1; // rectangles
var shadow; // shadow div
var bpos; // begin position
var epos; // end position
var width, shadowWidth;
var step; // animation step 1..50
var timer = null;

function animateRect() {
    step++;
    if (step > 50) {
        clearInterval(timer);
        timer = null;
        return;
    }
    var t = bpos.t + Math.round((epos.t - bpos.t) * step / 50);
    var l = bpos.l + Math.round((epos.l - bpos.l) * step / 50);
    var w = shadowWidth + Math.round((width - shadowWidth) * step / 50);
    shadow.style.top = t + "px";
    shadow.style.left = l + "px";
    shadow.style.width = w + "px";
}

function highlightMenu(e) {
    e = e || window.event; // for IE8,7 compatibility
    var item = e.target || e.srcElement; // for IE8,7
    if (timer) {
        clearInterval(timer);
    }
    step = 0;
    rect = item.getBoundingClientRect();
    width = item.offsetWidth;
    epos = {
        t: rect.top,
        l: rect.left
    };

    rect1 = shadow.getBoundingClientRect();
    shadowWidth = shadow.offsetWidth;
    bpos = {
        t: rect1.top,
        l: rect1.left
    };
    timer = setInterval(animateRect, 5);
}



function init() {
    var menu = document.getElementById('menu');
    var items = menu.getElementsByTagName('li');
    for (var i = 0; i < items.length; i++) {
        items[i].onmouseenter = highlightMenu;
    }
    shadow = document.getElementById('d1');
    shadow.style.width = items[0].offsetWidth + 'px';
    shadow.style.top = items[0].getBoundingClientRect().top + 'px';
    shadow.style.left = items[0].getBoundingClientRect().left + 'px';
}

if (window.addEventListener) {
    window.addEventListener('load', init, false);
} else if (window.attachEvent) {
    window.attachEvent('onload', init);
}

JSFiddle: http://jsfiddle.net/m2SBm/6/ (using the getBoundingClientRect function)

Update:

To make your menu work correctly also with eventual scrollbars, compute the element offset position rather than using the getBoundingClientRect function as shown in the following answer: finding element's position relative to the document

Here is additional CSS for transparency in menu:

ul#menu, ul#menu li {
    position:relative;
    background-color:transparent;
}
#d1 {
    z-index:-10;
}

The complete menu here: http://jsfiddle.net/m2SBm/8/

Community
  • 1
  • 1
Stano
  • 8,749
  • 6
  • 30
  • 44
  • Thanks for the code and it solves the main problem. I even learn more thing from it compare to what I read from books. But here is another problem. In the site http://lusens.ru/ the rectangle goes a bit further than an
  • and then returns back. It looks like it's two actions. First go forward then get back. It's difficult because using timeout or interval two actions would work simultaneously and the only thing comes to my head is to put something that will stop workflow of the function. I heard about succession of actions in JQuery. Is there such a thing in JS?
  • – Vadim Oct 14 '13 at 15:38
  • Hello Vadim. You can start the CSS3 animation via javascript, as shown in this answer: http://stackoverflow.com/questions/12866385/start-css-animations-javascript#12866442 Here is the updated fiddle: http://jsfiddle.net/m2SBm/20/ , I used the shake effect from this page: http://daneden.me/animate/ (you can customize the css by your needs) Hope it helps :-) – Stano Oct 14 '13 at 16:19