Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.javascript > #20869 > unrolled thread

Enhancing typical CSS drop menu with onclick event

Started byTuxedo <tuxedo@mailinator.com>
First post2013-08-05 19:35 +0200
Last post2013-08-09 21:23 +0200
Articles 5 — 2 participants

Back to article view | Back to comp.lang.javascript


Contents

  Enhancing typical CSS drop menu with onclick event Tuxedo <tuxedo@mailinator.com> - 2013-08-05 19:35 +0200
    Re: Enhancing typical CSS drop menu with onclick event JJ <duh@nah.meh> - 2013-08-06 15:59 +0700
      Re: Enhancing typical CSS drop menu with onclick event Tuxedo <tuxedo@mailinator.com> - 2013-08-06 16:46 +0200
        Re: Enhancing typical CSS drop menu with onclick event JJ <duh@nah.meh> - 2013-08-07 03:23 +0700
          Re: Enhancing typical CSS drop menu with onclick event Tuxedo <tuxedo@mailinator.com> - 2013-08-09 21:23 +0200

#20869 — Enhancing typical CSS drop menu with onclick event

FromTuxedo <tuxedo@mailinator.com>
Date2013-08-05 19:35 +0200
SubjectEnhancing typical CSS drop menu with onclick event
Message-ID<ktonod$ei8$1@news.albasani.net>
Below is an old-fashioned dropdown method that can still be good for 
devices that support :hover but which works poorly on touch screen devices 
where :hover and onmouseover doesn't exist or isn't usable anymore. In this 
example there are two independent 'navmenu' class span blocks, which may 
appear in different places on a page, although more typically similar 
things may be done as a nested group of <ul> dropdowns in a holding <ul> in 
horizontal or vertical site navigation style (e.g. A List Apart, Son of 
Suckerfish etc.).

However, using a same html class for separate menus, I would like a 
javascript to apply 'left: auto' to the dropdown navmenu that is clicked on 
(instead of using hover as below). Or perhaps it could equally be done by 
'display' a previously 'hidden' html object.

Thereafter when either clicking the same node link a second time or another 
node link on the same page, the previously visible dropdown should become 
hidden again, so only one dropdown is visible at any one time.

How can this be done in Javascript? (I guess collecting the relevant 
classes and applying changes in combination with 'this' keyword).

<!DOCTYPE html>

<style>
.navmenu .dropdown{
left: -999em;
position: absolute;
display: block;
}

.navmenu:hover .dropdown{
left: auto;
}

</style>


<span class="navmenu">
<a href="node1.html">Node 1</a>

<span class="dropdown">
<ul>
<li><a href="sub1.html">Sub 1</a></li>
<li><a href="sub2.html">Sub 2</a></li>
</ul>
</span>

</span>


<hr>

<span class="navmenu">
<a href="node2.html">Node 2</a>

<span class="dropdown">
<ul>
<li><a href="sub3.html">Sub 3</a></li>
<li><a href="sub4.html">Sub 4</a></li>
</ul>
</span>

</span>

For added usability an "x" could be included somewhere, maybe at the 
bottom-right or top-right of each dropdown span block, which could remove 
the current dropdown when clicked.

Any ideas, code bits or links to sites that have solved this already 
(although not by popular code libraries) would be much appreciated!

Tuxedo

[toc] | [next] | [standalone]


#20874

FromJJ <duh@nah.meh>
Date2013-08-06 15:59 +0700
Message-ID<19fdmc1ur4uoq$.11i5vm93mk195$.dlg@40tude.net>
In reply to#20869
On Mon, 5 Aug 2013 19:35:08 +0200, Tuxedo wrote:
> Below is an old-fashioned dropdown method that can still be good for 
> devices that support :hover but which works poorly on touch screen devices 
> where :hover and onmouseover doesn't exist or isn't usable anymore. In this 
> example there are two independent 'navmenu' class span blocks, which may 
> appear in different places on a page, although more typically similar 
> things may be done as a nested group of <ul> dropdowns in a holding <ul> in 
> horizontal or vertical site navigation style (e.g. A List Apart, Son of 
> Suckerfish etc.).
> 
> However, using a same html class for separate menus, I would like a 
> javascript to apply 'left: auto' to the dropdown navmenu that is clicked on 
> (instead of using hover as below). Or perhaps it could equally be done by 
> 'display' a previously 'hidden' html object.
> 
> Thereafter when either clicking the same node link a second time or another 
> node link on the same page, the previously visible dropdown should become 
> hidden again, so only one dropdown is visible at any one time.
> 
> How can this be done in Javascript? (I guess collecting the relevant 
> classes and applying changes in combination with 'this' keyword).

Use this.

<script>
(function() {
  function init() {
    function navClick(ev) {
      var ev = ev || event;
      var node = ev.target || ev.srcElement;
      var dropdown = node.parentNode.querySelector(".dropdown");
      dropdown.style.left = dropdown.style.left === "" ? "auto" : "";
      return false;
    }
    var navs = document.querySelectorAll(".navmenu");
    for (var i = 0; i < navs.length; i++) {
      var node = navs[i].querySelector("a");
      node.onclick = navClick;
    }
  }
  if (window.addEventListener) {
    addEventListener("DOMContentLoaded", init, false);
  } else {
    attachEvent("onload", init);
  }
})();
</script>

> <style>
> .navmenu .dropdown{
> left: -999em;
> position: absolute;
> display: block;
> }
> 
> .navmenu:hover .dropdown{
> left: auto;
> }
> </style>

Remove the CSS for ".navmenu:hover .dropdown" since it's no longer used.

> <span class="navmenu">
> <a href="node1.html">Node 1</a>
> 
> <span class="dropdown">
> <ul>
> <li><a href="sub1.html">Sub 1</a></li>
> <li><a href="sub2.html">Sub 2</a></li>
> </ul>
> </span>
> 
> </span>
> 
> <hr>
> 
> <span class="navmenu">
> <a href="node2.html">Node 2</a>
> 
> <span class="dropdown">
> <ul>
> <li><a href="sub3.html">Sub 3</a></li>
> <li><a href="sub4.html">Sub 4</a></li>
> </ul>
> </span>
> 
> </span>

Consider adding this CSS rule to hide the list bullets.

.dropdown {
  list-style: none;
}

[toc] | [prev] | [next] | [standalone]


#20875

FromTuxedo <tuxedo@mailinator.com>
Date2013-08-06 16:46 +0200
Message-ID<ktr27r$nsh$1@news.albasani.net>
In reply to#20874
JJ wrote:

Thanks, it works. I tested on most current browsers as well as on IE8.

I added <a href="javascript:void(0);"> in the node links and removed the 
hover CSS:
.navmenu:hover .dropdown{
left: auto;
}

Your solution is a nice and short procedure without need for special HTML 
IDs for each drop down, etc. magically working on any number of independent 
structures on a page:

<span class="navmenu"><a href="javascript:void(0);">Node ...</a>
<span class="dropdown">
<ul>
<li><a href="sub...html">Sub ...</a></li>
</ul>
</span>
</span>

It's particularly useful if showing more than one drop down at the time is 
a good feature.

Alternatively, can your script easily be made to show only one dropdown at 
any one time?

For example, if Node 1 is clicked and directly thereafter Node 2 is clicked 
without first closing Node 1, can the dropdown for Node 1 close by the 
action of clicking/opening Node 2  (or any other node)? I guess it could be 
done somehow by closing the dropdown of any/all other open dropdowns, if 
there are, just before opening the latest clicked node/dropdown?

Yes, the idea is to hide the bullets. The HTML/CSS example is a skeleton, 
just to keep the code short in the original post.

Many thanks again for your script!

Tuxedo

> On Mon, 5 Aug 2013 19:35:08 +0200, Tuxedo wrote:

[...]

> 
> Use this.
> 
> <script>
> (function() {
>   function init() {
>     function navClick(ev) {
>       var ev = ev || event;
>       var node = ev.target || ev.srcElement;
>       var dropdown = node.parentNode.querySelector(".dropdown");
>       dropdown.style.left = dropdown.style.left === "" ? "auto" : "";
>       return false;
>     }
>     var navs = document.querySelectorAll(".navmenu");
>     for (var i = 0; i < navs.length; i++) {
>       var node = navs[i].querySelector("a");
>       node.onclick = navClick;
>     }
>   }
>   if (window.addEventListener) {
>     addEventListener("DOMContentLoaded", init, false);
>   } else {
>     attachEvent("onload", init);
>   }
> })();
> </script>
> 
> > <style>
> > .navmenu .dropdown{
> > left: -999em;
> > position: absolute;
> > display: block;
> > }
> > 
> > .navmenu:hover .dropdown{
> > left: auto;
> > }
> > </style>
> 
> Remove the CSS for ".navmenu:hover .dropdown" since it's no longer used.
> 
> > <span class="navmenu">
> > <a href="node1.html">Node 1</a>
> > 
> > <span class="dropdown">
> > <ul>
> > <li><a href="sub1.html">Sub 1</a></li>
> > <li><a href="sub2.html">Sub 2</a></li>
> > </ul>
> > </span>
> > 
> > </span>
> > 
> > <hr>
> > 
> > <span class="navmenu">
> > <a href="node2.html">Node 2</a>
> > 
> > <span class="dropdown">
> > <ul>
> > <li><a href="sub3.html">Sub 3</a></li>
> > <li><a href="sub4.html">Sub 4</a></li>
> > </ul>
> > </span>
> > 
> > </span>
> 
> Consider adding this CSS rule to hide the list bullets.
> 
> .dropdown {
>   list-style: none;
> }

[toc] | [prev] | [next] | [standalone]


#20878

FromJJ <duh@nah.meh>
Date2013-08-07 03:23 +0700
Message-ID<169h2xu4o50sk.ejf3hto84v6q.dlg@40tude.net>
In reply to#20875
On Tue, 6 Aug 2013 16:46:18 +0200, Tuxedo wrote:
> It's particularly useful if showing more than one drop down at the time is 
> a good feature.
>
> Alternatively, can your script easily be made to show only one dropdown at 
> any one time?
> 
> For example, if Node 1 is clicked and directly thereafter Node 2 is clicked 
> without first closing Node 1, can the dropdown for Node 1 close by the 
> action of clicking/opening Node 2  (or any other node)? I guess it could be 
> done somehow by closing the dropdown of any/all other open dropdowns, if 
> there are, just before opening the latest clicked node/dropdown?

I forgot about that, sorry. This one's better.

(function() {
  var last = null;
  function closeLast(now) {
    if (last) {
      last.style.left = "";
    }
    last = now;
  }
  function clicked(ev) {
    var ev = ev || event;
    var node = ev.target || ev.srcElement;
    if ((node.tagName==="A") && (node.parentNode.className==="navmenu")) {
      var dropdown = node.parentNode.querySelector(".dropdown");
      if (dropdown !== last) {
        dropdown.style.left = "auto";
        closeLast(dropdown);
      } else {
        closeLast(null);
      }
      if (ev.preventDefault) {
        ev.preventDefault();
      } else {
        ev.returnValue = false;
      }
    } else {
      closeLast(null);
    }
  }
  if (document.addEventListener) {
    document.addEventListener("click", clicked, false);
  } else {
    document.attachEvent("onclick", clicked);
  }
})();

[toc] | [prev] | [next] | [standalone]


#20903

FromTuxedo <tuxedo@mailinator.com>
Date2013-08-09 21:23 +0200
Message-ID<ku3fjj$pso$1@news.albasani.net>
In reply to#20878
JJ wrote:

> On Tue, 6 Aug 2013 16:46:18 +0200, Tuxedo wrote:

[...]

> 
> I forgot about that, sorry. This one's better.

Many thanks for posting the below version. It works perfectly!

Tuxedo

> 
> (function() {
>   var last = null;
>   function closeLast(now) {
>     if (last) {
>       last.style.left = "";
>     }
>     last = now;
>   }
>   function clicked(ev) {
>     var ev = ev || event;
>     var node = ev.target || ev.srcElement;
>     if ((node.tagName==="A") && (node.parentNode.className==="navmenu")) {
>       var dropdown = node.parentNode.querySelector(".dropdown");
>       if (dropdown !== last) {
>         dropdown.style.left = "auto";
>         closeLast(dropdown);
>       } else {
>         closeLast(null);
>       }
>       if (ev.preventDefault) {
>         ev.preventDefault();
>       } else {
>         ev.returnValue = false;
>       }
>     } else {
>       closeLast(null);
>     }
>   }
>   if (document.addEventListener) {
>     document.addEventListener("click", clicked, false);
>   } else {
>     document.attachEvent("onclick", clicked);
>   }
> })();

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.javascript


csiph-web