Javascript body OnClick - javascript

I'm in the process of learning Javascript and I'm trying to create a simple dropdown menu. An example of my desired functionality can be seen on the google homepage in the top menu with the "more" and "settings" dropdown. Specifically when you click off the menu, the menu disappears.
What code do I need to place in the hideMenus function in Javascript to hide the visible uls when a click occurs anywhere on screen?
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<title>Untitled 1</title>
<style type="text/css">
a
{
color:blue;
}
.info ul.submenu
{
border: solid 1px #e0e0e0;
background-color: #fff;
position: absolute;
padding: 0;
z-index: 2;
display: none;
}
.info ul.submenu li
{
display: block;
border-top: solid 1px #e0e0e0;
margin: 0px 10px 0 10px;
}
.info ul.submenu li a
{
display: block;
padding: 7px 0px 6px 0;
color: #1177ee;
cursor:pointer;
}
</style>
<script type="text/javascript">
function hideMenus()
{
//TODO
}
function menu(id) {
var myLayer = document.getElementById(id);
myLayer.onblur = function() {
myLayer.style.display = 'none';
};
if (myLayer.style.display == "none" || myLayer.style.display == "") {
myLayer.style.display = "block";
} else {
myLayer.style.display = "none";
}
}
</script>
</head>
<body onclick="hideMenus();">
<div class="info">
Some Text Boom A <a onclick="menu('id1');">Link</a> | More text
<a onclick="menu('id2');">Another Link</a> | more text
<ul id="id1" class="submenu">
<li>A1</li>
<li>A2 This is Long</li>
<li>A3</li>
</ul>
<ul id="id2" class="submenu">
<li>B1</li>
<li>B2</li>
<li>B3</li>
</ul>
</div>
</body>
</html>
I do not want to use jQuery.

It looks like you have a pretty decent setup as-is. You'll likely run into some event bubbling problems (for more info, take a look at PPK's Event Order Article). That seems to be outside of the scope of your current question, so I'll just give you what you asked for:
hideMenus()
{
var uls = document.getElementsByTagName('ul'), i;
for (i = 0; i < uls.length; i++)
{
if (uls[i].className === 'submenu' && uls[i].style.display !== 'none')
{
uls[i].style.display = 'none';
}
}
}
First, we get all the <ul>'s on the page. Then, we loop through all of them, check to see if it's a submenu, and if it's currently displayed. If both are true, then we hide it.
There are a couple faults with this code:
If the uls have more than one class (class="animal submenu"), then it will not hide the menu
It will look through all the <ul>'s on the page. This isn't exactly efficient, but it's the only way to do it without cross-browser support for getElementsByClass.
These aren't huge faults, especially if you're only using this to learn about javascript, and if you closely control your code (i.e. no other developers are working on it). All in all, it's a good stepping stone.
In the future, I'd suggest using addEvent - a fairly common function that allows you to add event handlers to elements without using onclick="...". There are a couple different implementations of it, but they (almost) all work the same from your perspective. Here are links to Dean Edwards's Version and John Resig's Version
Good luck!

Here is more or less the logic we use in our web app for drop down menus:
<html>
<head>
<title></title>
</head>
<body>
<div style="position:relative;width:250px">
<a id="link" href="javascript:" onclick="showDiv(this)">Show menu</a>
<ul id="entries" style="display:none;background:#DEF;padding:0;margin:0">
<li>item 1</li>
<li>item 2</li>
</ul>
<input id="inp" style="position:absolute;left:-30px;width:0" />
</div>
<script>
function showDiv(lnk){
var entries = document.getElementById('entries'),
inp = document.getElementById('inp'),
nh = 'data-nohide';
//show the entries
entries.style.display = 'block';
entries.removeAttribute(nh);
inp.focus();
//if mouse over, can't close it
entries.onmouseover = function(){
this.setAttribute(nh, true);
inp.focus();
};
//if mouse out, can close it
entries.onmouseout = function(){
this.removeAttribute(nh);
};
entries.onclick = function(e){
//code when the user clicks on the menu...
alert((e.target||e.sourceElement).innerHTML);
this.style.display = 'none';
};
//if the user press ESC
inp.onkeyup = function(e){
if(e.keyCode === 27){
this.style.display = 'none';
this.removeAttribute(nh);
}else{
//do something else with other keys(ie:down, up, enter)...
inp.focus();
}
};
//click somewhere else input onblur
inp.onblur = function(){
if(!entries.getAttribute(nh)){
entries.style.display = 'none';
entries = inp = null;
}
};
}
</script>
</body>
</html>
The trick is to use an input field that has the focus, and when it looses it an onblur is triggered and close the menu.
The mouseover, mouseout are there to prevent the onblur to fire when the user click an item in the menu.
To have a toggle effect like an open/close on the link, I guess 2 links that hide each other are needed.

You can capture a click anywhere if you put onclick on the body.
Because of the javascript event propagation model, if you click anywhere on any element and you don't stop the event from propagating, it will reach body and hide the menus.
So basically this means that you want to capture body onclick and make it to hide the menus so when you click on any area of the page, it will close the menus.
But this hides a bit of unwanted behaviour - when you click on the button to show the menu, the menu will display and quickly after that hide (when the event reaches body). To prevent this, you will want to stop the event from propagating when you click on the button which shows the menu (you can see how this works in the code I posted below). The code shows where you need to touch to make it work nicely.
// this function stops event e or window.event if e is not present from
// propagating to other elements.
function stop_event(e) {
if(!e) {
e = window.event;
}
if (e.stopPropagation) e.stopPropagation();
e.cancelBubble = true;
if (e.preventDefault) e.preventDefault();
e.returnValue = false;
return false;
}
// now you just hide all the menus in your hideMenus
function hideMenus()
{
//pseudocode!
for all visible menus - hide // or if you want you can hide all menus,
// the hidden will remain hidden
}
Now the important part.
function menu(id) {
// your stuff here
stop_event(); // this will stop the event going down to the body
// and hiding it after showing it
// this means it will not flicker like: show-hide
}
And finally on your whole UL element:
//partly pesudocode
ul.onclick = function() { stop_event(); }
To explain again what this does:
1st. You hook your hideMenu function to body.onclick. This means that it will always hide the menus if we don't stop the event.
2nd. When you click the menu button, you show the menu and then we stop the event from going to the body. This way, the body.onclick will not fire and it will not hide the menu right after we opened it.
3rd. The ul.onclick means that the menu will not hide itself when we click on it (though if you want the menu to hide when you click the menu itself, you can remove this part).

Related

Displaying a block on mouseover inline

I want to display a <div> block when mouse enters an element
My code so far:
<div class="dropdown">
MEN
<div class="dropdowncontent" id="ddmen" style="margin-left:100px;">
TOPWEAR <br/>
BOTTOMWEAR </br>
FOOTWEAR
</div>
</div>
JavaScript Code:
var ddm=document.getElementById("ddmen")
function ddmenin()
{
ddm.style.display="block";
}
function ddmenout()
{
ddm.style.display="none";
}
But when i hover over <a href="#men"> I cannot click on the links in the <div> with class="dropdowncontent" as the block disappears when i leave the <a href="#men">
I don't understand why this is happening since i have used onmouseover, which is valid even for child elements.
I have tried doing it using css but for some reason the following does not work (Style.css is used in above html)
STYLE.CSS
.dropdowncontent{
display:none;
}
.dropdown:hover .dropdowncontent{display:block;}
Can someone please correct my code to satisfy my needs or has any other simple alternative?
Well, your div link container is not part of a link, so when you move cursor to dropdown menu you leave the link and onmouseout listener does its job.
What you want is to hide the menu when it's not needed anymore. E.g. you clicked on the menu item or you left the menu and didn't return for some time.
To achieve this you can do the following:
Add hiding the menu to click listener on menu items
Add a function that starts some timer as soon as you leave the dropdown button or the menu (so that makes two onmouseout listeners). If you return there, you can reset the timer in onmouseover. When timer is done you can hide the menu.
It can look like this:
const $ = document.querySelector;
let menuTimeoutId;
const menu = $('#ddmen');
function stopMenuTimeoutAndShowMenu() {
if (menuTimeoutId) {
clearTimeout(menuTimeoutId);
menuTimeoutId = null;
}
menu.style.display = 'block';
}
function startMenuTimeout() {
window.menuTimeoutId = setTimeout(() => {
menu.style.display = 'none';
}, 2000); // possible timeout value
}
$('#men, #ddmen').addEventListener('onmouseover', stopMenuTimeoutAndShowMenu);
$('#men, #ddmen').addEventListener('onmouseout', startMenuTimeout);
I think you should use the onmouseover and onmouseout in your <div class="dropdown"> instead. Because, when you go to the div.dropdowncontent you probably invokes the onmouseout event. So the code will be like this:
<div class="dropdown" onmouseover="ddmenin()" onmouseout="ddmenout()">
MEN
<div class="dropdowncontent" id="ddmen" style="margin-left:100px;">
TOPWEAR <br/>
BOTTOMWEAR </br>
FOOTWEAR
</div>
</div>
See if it works ;D
You can try simple CSS changes, that can even help you :
In your Style.css file:
.dropdown .dropdowncontent {
visibility: hidden;
}
.dropdown:hover .dropdowncontent {
visibility: visible;
}

Use jQuery to click anywhere and remove 'active' class

I'm having some trouble figuring out how to close a div by clicking anywhere on the screen.
I'm currently toggling an 'active' class in order to display a drop down div, then attempting to remove that class by clicking on the body:
$(document).ready(function () {
$('.navbar a').click(function () {
$(this).next('.navbar-dropdown').toggleClass('active');
});
$(body).click(function() {
if($('.navbar-dropdown').hasClass('active')){
$('.navbar-dropdown').removeClass('active');
}
});
});
<ul class="navbar">
<li>
Link
<div class="navbar-dropdown">
Dropdown Content
</div>
</li>
</ul>
However they are conflicting with each other, so as soon as the class is toggled on, the 'body' click toggles it off at the same time. Have spent some time looking on here and came across this method a few times:
$(document.body).click( function() {
closeMenu();
});
$(".dialog").click( function(e) {
e.stopPropagation();
});
However any attempts to configure this to work correctly seemed to fall on deaf ears!
The click event from the navbar is bubbling up to the body, so both events fire. stopPropagation() is one way to prevent that, but you need to do it in the navbar link's event handler, so it stops that particular event; not in a separate event handler as you had it.
Another change you might consider making is to only assign the body click handler when you need it, instead of firing all the time -- create that handler inside the navbar's click handler, and deactivate it again when it's used:
$(document).ready(function() {
$('.navbar a').click(function(e) {
var myDropdown = $(this).next('.navbar-dropdown');
$('.navbar-dropdown.active').not(myDropdown).removeClass('active'); // close any other open dropdowns
myDropdown.toggleClass('active'); // open this one
$('body').click(function() {
// no need for an if statement here, just use a selector that matches the active elements:
$('.navbar-dropdown.active').removeClass('active');
$('body').off('click'); // cancel the body's click handler when it's used
});
e.stopPropagation(); // prevent the navbar event from bubbling up to the body
});
});
.active {
color: red
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul class="navbar">
<li>
Link
<div class="navbar-dropdown">
Dropdown Content
</div>
</li>
<li>
Link 2
<div class="navbar-dropdown">
Dropdown Content 2
</div>
</li>
<li>
Link 3
<div class="navbar-dropdown">
Dropdown Content 3
</div>
</li>
</ul>
(If there's a chance you might need more than one separate click event handler on the body, you can namespace the event so you can control which one you're turning off:
$('body').on("click.myNamespace", function() {
// do other stuff
$('body').off("click.myNamespace")
})
I did the exact thing as you and it works for me. Are you sure you don't have any other event listeners attached? Or maybe a z-index on the menu bringing it underneath other elements?
$(document).click(function(e) {
$(".dialog").text('closed')
});
$(".dialog").click(function(e) {
e.target.innerText = 'open';
e.stopPropagation();
});
.dialog {
width: 200px;
height: 200px;
background: antiquewhite;
text-align: center;
}
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<div class="dialog">open</div>
</body>
</html>

Toggle menu but also allows click inside without closing

Firstly apologies for the random title, I really can't think of another way to word it succinctly.
This is my issue.
I have a couple of nav icons, that when clicked toggle menu displays, just like you see everywhere: facebook, etc.
When you click outside of the div it hides the menu.
It works but I have two problems.
Clicking outside works to close the open div, but clicking on the icon that triggers the toggle doesn't close it, it just re-toggles it instantly.
I would like to be able to click inside of the menus without them closing, which they are currently doing onclick.
The html looks something like this, where the user-menu is the click-able icon that toggles the div contained within.
HTML
<nav>
<div class="user-menu">
<div id="user-dropdown">MENU CONTENTS HERE</div>
</div>
</nav>
jQuery
$('.user-menu').click(function () {
$('#user-dropdown').fadeToggle();
});
$(document).mouseup(function(e) {
var container = $("#user-dropdown");
if (!container.is(e.target) && container.has(e.target).length === 0) {
container.hide();
}
});
FIDDLE
https://jsfiddle.net/vo8a1r0p/
EDIT - ANSWER
Using a mixture of Bhuwan's answer and a stopPropagation() it's now working.
Working jQUERY
$(document).on("click", function(e) {
if ($(e.target).hasClass("user-menu")) {
$('#user-dropdown').fadeToggle();
} else {
if ($(e.target).hasClass("dropdown-menu")) {
$('#user-dropdown').show();
} else {
$('#user-dropdown').fadeOut();
}
}
});
$("#user-dropdown").click(function(e){
e.stopPropagation();
});
Working FIDDLE
https://jsfiddle.net/ne4yfbjp/
You can try this
$(document).on("click", function(e) {
if ($(e.target).hasClass("user-menu")) {
$('#user-dropdown').fadeToggle();
} else {
if ($(e.target).closest("#user-dropdown").hasClass("dropdown-menu")) {
$('#user-dropdown').show();
} else {
$('#user-dropdown').fadeOut();
}
}
});
.dropdown-menu {
display: none;
background: gray;
padding: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<nav>
<div>
<button class="user-menu">Menu</button>
<div id="user-dropdown" class="dropdown-menu">
<div class="username">
Some User
</div>
<ul>
<li>Link1</li>
<li>Link1</li>
</ul>
</div>
</div>
</nav>

Listen for `mousedown` on parent but let `click` to pass to children

I have a div that listens for mousedown event. The div has some child buttons (absolutely positioned outside the div). I want to be able to click these buttons. But when I press the mouse button, the parent div intercepts the mousedown event. I can test the .target member to ignore the event if it happened on the buttons, but it seems that I never get the click event on these buttons this way.
Is there a way to solve this without adding yet another ancestor div?
You can use event.target == this
Example :
<html>
<head>
<style>
div {
width: 100px;
background-color: yellow;
height: 100px;
}
</style>
</head>
<body>
<div id="d">I am div
<button id="btn1">Button1</button>
</div>
<script>
var divMousedown = document.getElementById("d");
var Child = document.getElementById("btn1");
divMousedown.onmousedown = function(event) {
if(event.target == this)
alert("You Mouse down on Div");
}
Child.onclick = function(event){
if(event.target == this)
alert("You Click on Button")
}
</script>
</body>
</html>

Tabindex on javascript elements

I have a side nav with dots (each of these dots on click scrolls to anchor down the page).
For accessibility purposes, I'd like users to be able to tab through the dots. I have set up a <span> element to wrap the anchors of each dot and the tabindex works fine, however..
I'd like the click event to be fired when they tab on the nav element. ie: If they tab to dot 2 it should imitate a click event so it takes them to that anchor.
Possible?
If I understand correctly, I think that you should be able to solve your problem by listening for the "focus" event on the dots and simulating a click on the dot when you receive it. There are several ways to do this, depending on what frameworks (if any) you are using. For example, if you are working with jQuery and the dots are identified by having the "dot" class:
$('.dot').focus(function(e) {
e.target.click();
}
Here's a small test page that I put together - it seemed to work in both IE10 and Chrome:
<html>
<head>
<script type="text/javascript">
function handleOnLoad() {
var elems = document.getElementsByTagName("span");
var i;
for (i = 0; i < elems.length; ++i) {
elems[i].onfocus = function(event) {
if (event.target) {
event.target.firstChild.firstChild.click();
}
else if (event.srcElement) {
event.srcElement.firstChild.firstChild.click();
}
}
}
}
</script>
</head>
<body onload="handleOnLoad()">
<span class="focus" tabindex="1"><ul class="inactive" tag="scroller">Foo</ul></span>
<span class="focus" tabindex="2"><ul class="inactive" tag="scroller">Bar</ul></span>
<span class="focus" tabindex="3"><ul class="inactive" tag="scroller">Baz</ul></span>
<span class="focus" tabindex="4"><ul class="inactive" tag="scroller">Ban</ul></span>
</body>
</html>

Categories