0

I'm very new to javascript and I'm trying to give some dynamic features to a site I'm working on. In particular, I want to have an unfolding menu item that unfolds and folds back whenever the mouse is on and off of it, respectively.

I got the unfolding part down but the event listener that triggers the folding back does it whenever the mouse if off of the area where the menu item used to be, event though it's just been extended.

The HTML markup looks like this:

<nav id="nav">
<ul>
    <li id="elemPlaces"><ul id="drop"><li>Places</li></ul></li>
</ul>
</nav>

The event listeners are declared like this:

<script type="text/javascript">

var extended = false;
var listPlace = (<?php echo json_encode($list_place); ?>);

document.getElementById("elemPlaces").addEventListener("mouseover", extend);

document.getElementById("elemPlaces").addEventListener("mouseout", retract);

</script>

and the extend and retract functions are the following:

function extend()
{
    if(!extended)
    {
        var drop = document.getElementById("drop");
        var form = document.createElement("form");

        form.setAttribute("action", "place.php");
        form.setAttribute("method", "get");
        drop.appendChild(form);

        for(var i = 0; i < listPlace.length; i++)
        {
            var li = document.createElement("li");
            var input = document.createElement("input");

            li.setAttribute("class", "dropOption");

            input.setAttribute("type", "submit");
            input.setAttribute("name", "location");
            input.setAttribute("value", listPlace[i]);

            li.appendChild(input);
            form.appendChild(li);
        }
        extended = true;
    }
}

function retract()
{
    var dropOption = document.getElementsByClassName("dropOption");

    while(dropOption[0])
    {
        dropOption[0].parentNode.removeChild(dropOption[0]);
    }
    extended = false;
}

I realize it all must look amateurish but like I said, I'm new to this. I'd really appreciate it if someone could help me.

Afunakwa
  • 437
  • 1
  • 7
  • 20
  • 1
    Not directly linked to the problem, but Instead of really adding/removing new elements, why not just show/hide them ? (using `display: none;` for example ?) – Cyril Duchon-Doris Feb 17 '15 at 11:56
  • I thought about doing that, it would be more efficient. I just wasn't sure how to go about it. I look into it, thanks – Afunakwa Feb 17 '15 at 12:29
  • Ok I must admit the HTML documentation was a bit confusing, but it would seem like using mouseenter/mouseleave instead of mouseover/mouseout fixes the problem. Anyway I'm going to repost an answer with a cleaner solution for what you want to do... – Cyril Duchon-Doris Feb 17 '15 at 14:01

2 Answers2

1

Okay JSFiddle seems to be down, so here is a Plunker

Basically :

  • Use mouseenter and mouseleave (see this question)
  • I believe in your case you just want to redirect to a page passing a GET option. You can do it using <a href="yourpage.php?yourOption=yourValue">. It is simpler. You don't need forms for this.
  • As I said, here it's a bad idea to modify the dom. Or well, it depends on where your list comes from. If your $list_place comes from an AJAX asynchronous request, then obviously you can't do anything else but modify the DOM dynamically. However if you know beforehand what the content of your list will be, best it to just write everything to the HTML, and add classes like class="unexpanded/expanded", and have a CSS .unexpanded{display: none}. Then you just need to toggle/change the class
Community
  • 1
  • 1
Cyril Duchon-Doris
  • 12,964
  • 9
  • 77
  • 164
  • I'm indeed making a server request beforehand so I have no other option but to modify the dom. The point is the make the site as generative as possible so the user has very little to worry about; he needs only to post articles and everything is updated on the fly. However, playing with display options and links is definitely a more efficient solution, and I'll look into mouseenter and mouseleave. You've been very helpful, thanks again! – Afunakwa Feb 17 '15 at 14:32
  • Done, thanks for your help. First question I post here and I'm not disappointed. I'll definitely stick around – Afunakwa Feb 17 '15 at 15:27
0

So basically what you want to so, is make sure the menu stays unfolded a certain amount of time before folding back ? Here are some ideas

Instead of calling retract on mouseout, you could call another function with a timeout, like retract_after

function retract_after(){
    setTimeout(function(){ retract() }, 3000);
}

But this could lead to weird situations (imagine the user moves the mouse on the menu just after moving it out, before the timeout expired...). So you might want to lookup how to empty the queue or remove EventListeners.

Or, what you could do, is only attach the "retract" eventListener at the end of the mouseover function (and also eventually with a timeout), and remove the eventlistener while or after it is retracting.

Also the animate() function of jquery library already somewhat produces by default the behavior you're looking (it queues animation, but if a user quickly triggers mouseenter and mouseout event listeners, the menu will keep folding/unfolding till it empties the queue)

Cyril Duchon-Doris
  • 12,964
  • 9
  • 77
  • 164
  • I tried both solutions and it sort of works but I want the menu to remain unfolded as long as the mouse is over it and with a setTimeout, that's not really what it does. The second event listener is still not "listening" to the updated elemPlaces which takes more room than before the first event listener was triggered. As soon as the mouse goes down the menu, it's like a race against time to choose an option. I'll look into listener removing solution though, thanks for the suggestion – Afunakwa Feb 17 '15 at 12:36