popup with :hover cannot be dismissed with blur() - javascript

I am trying to hide a popup (implemented as a "ul") when the user clicks on it.
This popup is only made visible when its parent node detects the mouse :hover.
In my onclick() handler, I call blur() on every conceivable node above the DOM element I pass to my onclick() handler and it's not hiding the popup.
Here's the code:
// the html....
<ul class="ul_nestedPopups">
<li>Breakfast choices
<ul class="ul_nestedPopups">
<li>Apple</li>
<li>Orange</li>
<li>Banana</li>
</ul>
</li>
</ul>
.ul_nestedPopups {
display: inline-table;
}
// I tried using visibility instead of display -- no real difference
.ul_nestedPopups ul {
/*visibility: hidden;*/
display: none;
}
.ul_nestedPopups li:hover > ul {
/*visibility: visible;*/
/*visibility: visible !important;*/
/*display: block !important;*/
display: block;
}
// onclick handler......
function setBreakfast(theNestedAnchor)
{
// yes I experimented
theNestedAnchor.blur();
theNestedAnchor.parentNode.blur();
theNestedAnchor.parentNode.parentNode.blur();
theNestedAnchor.parentNode.parentNode.parentNode.blur();
theNestedAnchor.parentNode.parentNode.parentNode.parentNode.blur();
// NOW, DO SOMETHING WITH THE USER'S CHOSEN POPUP HERE...
// ........some code.........
}
The way it currently works: When the user hovers the mouse over the "Breakfast choices" list item, the list with the three breakfast choices (unordered list with 3 list items) appears. When the user clicks one of the 3 breakfast choices Apple, or Orange, or Banana -- I have verified that the onclick() handler gets called, but the onblur() calls there do nothing -- the 3 breakfast choices UL stays visible.
So I'm guessing the blur() might get ignored because when the user clicks the mouse on the popup, the mouse is still hovering so the onclick handler's attempt to call blur() is ignored.
So I thought about structuring the onclick handler this way but don't think it will work and I suspect that there's a better/easier way:
// onclick handler......
function setBreakfast(theNestedAnchor)
{
1. change the class to a different class that has no :hover selector
2. then call blur() to make the popup disappear
3. then change the class back to the original one with the :hover selector
to re-enable the :hover behavior for next time
}
Is there an easier/better way to make the popup that appears on :hover -- disappear
when the user clicks on it?

The hover event is for when you want something to happen on mouseover, and then to un-happen on mouseout. Correct me if I have this wrong, but what you are describing is to open the popup on mouseover, then close it when it is clicked. So you don't care about mouseout, so you do not want to be using hover.
I think you want this. See fiddle: http://jsfiddle.net/DLYwV/
You'll prob want to restyle to fit your needs, but the logic is all here.
HTML
<div id='hoverOver' onMouseOver='showPopup();'>Hover over me to open the popup</div>
<div id='popup'><br>I'm a popup.<br>Click
<a href=javascript:closePopup();>here</a>
to close.</div>
JS
function showPopup()
{
document.getElementById('popup').style.visibility='visible';
}
function closePopup()
{
document.getElementById('popup').style.visibility='hidden';
}
CSS
#hoverOver{
display:inline-block;
background:red;
}
#popup{
visibility:hidden;
position:absolute;
top:0;
background:white;
height:200px;
width:400px;
border:2px solid black;
}

Related

How long does it take until click event is triggered?

I try to make a select box whose entries are opened after clicking into the input box. After selecting one of the items, the dropdown should be closed again.
I want to achieve the open/close part of the dropdown without the use of javascript.
The html looks like this:
<div id="outer">
<input type="text" id="input">
<div id="results">
<div>Test 1 </div>
<div>Test 2 </div>
<div>Test 3 </div>
<div>Test 4 </div>
</div>
</div>
<div id="label">
</div>
After clicking onto an item, the selected value should appear below the #outer div (just for demonstration purposes).
The Javascript for assigning click events to the dropdown values:
document.querySelectorAll("#results div").forEach(setClick);
function setClick(node) {
node.addEventListener("click", setText.bind(null, node.innerHTML))
}
function setText(t) {
document.getElementById("label").innerHTML = t;
}
Now I will show you my first draft of css code:
#outer {
width: 200px;
position: relative;
}
#input {
width: 100%;
}
#results {
position: absolute;
width: 100%;
display: block;
visibility: hidden;
background-color: white;
}
#results > div:hover {
background-color: lightblue;
cursor: pointer;
}
#outer:focus-within #results, #results:hover {
visibility: visible;
}
This works like a charm but fails in one point:
After clicking an item, the dropdown is not closed. This is because of the #results:hover selector which is needed to keep the dropdown open after clicking onto an item. The click takes the focus out of the input field, thus the focus-within selector is not applied anymore. As the focus is removed from the input before the click occurs, the dropdown is hidden when the final click arrives in the document (this is my understanding of the problem).
Thus I use the hover selector which forces the div to keep open as long as the mouse is above the div.
You can test this here:
https://jsfiddle.net/hcetz1og/3/
My solution for this was a transition that hides the dropdown after the focus has been taken away:
#outer:not(:focus-within) #results:hover {
visibility: hidden;
transition-property: visibility;
/*use 10 ms and the clicked value in the drop down won't be shown */
transition-delay: 100ms;
transition-timing-function: step-end;
}
This works on my machine when I use 100ms as a delay. If I use 10ms, I have the same problem again. It seems that the click event is triggered "very" late.
Feel free to test it here:
https://jsfiddle.net/hcetz1og/2
Question:
How long will it take until the click event arrives at the document? Is there a fixed time span I have to wait or can the delay depend on every machine?
If so, I am forced to not use plain CSS but must use javascript for this I think.
Edit:
Feel free to post an alternative solution using plain css. But please be aware that I mainly want to focus on getting an answer to this question, not alternative solutions.
As #Mark Baijens said in the comments, using timeouts is a bad practice, so here is a pretty clean solution.
I used JavaScript to render the dropdown, not the CSS, because the CSS is where Your issue is coming from.
I don't know why would You want to set the innerHTML, but not some other property, like style.visibility for example. It just doesn't make sense to me, so with that in mind, let's get our hands on this :)
Working demo >> HERE <<.
Step 1 - remove the #outer...:hover parts of CSS
So, You are left with this:
#outer {
width: 200px;
position: relative;
}
#input {
width: 100%;
}
#results {
position: absolute;
width: 100%;
display: block;
visibility: hidden;
background-color: white;
}
#results > div:hover {
background-color: lightblue;
cursor: pointer;
}
Step 2 - add the onfocus event to the input field
Just assign a function call to the onfocus attribute of the input. Everything else in the HTML stays the same.
<div id="outer">
<input type="text" id="input" onfocus="showElements()">
<div id="results">
<div>Test 1 </div>
<div>Test 2 </div>
<div>Test 3 </div>
<div>Test 4 </div>
</div>
</div>
<div id="label">
</div>
Step 3 - create the showElements and hideElements function:
function showElements() {
document.getElementById("results").style.visibility = 'visible';
}
function hideElements() {
document.getElementById("results").style.visibility = 'hidden';
}
Step 4 - call the hideElements() when clicked outside the input element
There are two cases for the click outside the input element:
Case 1 - we clicked on one of the divs inside the #results wrapper
Case 2 - clicking outside the input field, but not on one of the divs inside the #results wrapper
In the first case, we will modify the assignment of the onclick handler like this:
document.querySelectorAll("#results div").forEach(setClick);
function setClick(node) {
node.addEventListener("click", setTextAndHideElements.bind(null, node.innerHTML));
}
So, the setText function now becomes setTextAndHideElements and looks like this:
function setTextAndHideElements(t) {
document.getElementById("label").innerHTML = t;
hideElements();
}
For the second case (clicking outside the input field, but not on one of the divs inside the #results wrapper), we must watch for the click on the whole page (document element), and respond to the action like this:
document.onclick = function(e) {
if (e.target.id !== 'input'){
hideElements();
}
}
Note: this will override any previously assigned onclick events assigned to the document element.
As mentioned in the beginning, working demo is >> HERE (codepen.io) <<.
I tried another solution which requires no setting of additional JS events.
See: https://jsfiddle.net/hcetz1og/4/
I gave every result item a tabindex of "0" to ensure, those items can be focusable.
Then i removed the #outer:not() part from the css and replaced the hover selector with this: #results:focus-within. Additional I called node.blur() on the node after clicking onto them.
Summary:
Change in HTML:
<div tabindex="0">Test 1 </div>
Change in JS:
function setText(t, node) {
document.getElementById("label").innerHTML = t;
node.blur();
}
Change in CSS:
#outer:focus-within #results, #results:focus-within {
visibility: visible;
}
What do you think about this one? Should be stable I think because the focus onto the #results div is set before the click event is triggered onto the result item.
Event order should be (based on my observation):
input focus -> input blur -> item focus -> item click
Not sure if the step between blur and focus can lead to a visible problem. Theoretically, the results div must be hidden and shown again in a very small amount of time.
But I investigated this with chrome's performance timeline and did not recognize a new render between both events. One can see, that the result item is focused (outline is set onto it) and then it disappears as expected.

mouseout closes the trigger and not the target

I have set a popup to open at mouse over on an element. The popup should close when the cursor is off it.
For some reason the popup window closes when the cursor is right off its opener button, instead.
You can see an example here:
http://www.friends.wwz.co.il/Lab/Gefen/Generali/es/popup.html
Please try to hover with the mouse on the "lee mas" button. A popup will open. It should close at hovering off it. But instead it closes at hovering off the lee mas button, so it closes immediately.
Any idea where do I go wrong?
Many thanks in advance for you advice
The main problem is you are attaching hover events to the button. Once you hover out of the button element, it fires the hoverOut button.
So the ideal course of action can be:
Bind hover event to the button.
Once hovered, a pop-up is dynamically added.
Then, a hover event is bounded to the pop.
And the code of dissolving the pop-up is attached in the hover-out function.
So that when actually the cursor is hovered out of the pop-up it dissolves.
Apart from that, Just have a look at this fiddle. It has two hyperlinks for hover. The first is the one you are facing. The second one is the one you are looking for. :D
Code for it:
$(document).ready(function() {
$("#toggleSwitch_j").hover(
function() {
$("#theBox_3").slideDown(500);
}, function() {
$("#theBox_3").slideUp(500);
});
$("#StayOpen").hover(
function() {
$("#theBox_2").slideDown(500);
}, function() {
$("#theBox_2").slideUp(500);
});
});
body {
background-color: #eef;
}
#theBox_3,
#theBox_2 {
display: none;
border: 1px solid #000;
width: 200px;
height: 100px;
background-color: #ddf;
}
#toggleSwitch_j,
#StayOpen {
background-color: #cacaca;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
This layout will only keep the hidden div visible as long as you point to the link
<br>You'll never be able to reach anything inside the div
<br>jQuery Hover
<div id="theBox_3">Peek-a-boo!</div>
<hr>This layout puts the link and hidden div inside a wrapper - hovering anywhere inside the wrapper expands the hidden div, so you can reach content inside it. This would be handy if you need to put links or form elements inside the hidden div, instead of
just text to read.
<div id="StayOpen">
jQuery Hover
<div id="theBox_2">Peek-a-boo!</div>
</div>

Click doesn't always trigger toggle-event

I have sort of an imagemap, which is basically a lot of absolutely positioned divs, which, when clicked, will show or hide a tooltip. Looks pretty great, apart from the fact, that it doesn't always "work". It sounds silly, but some times I will have to click a couple of times to trigger the event. Maybe I'm just not clicking hard enough? ;)
Markup
<div class="container">
<img src="img.png" />
<div class="trigger"
<div class="tooltip">
Awesome tooltip is awesome!
</div>
</div>
</div>
Style
.container {
width:100px;
height:100px;
position:relative; }
img {
position:relative; }
.trigger {
width:50px;
height:50px;
position:absolute;
top:50px;
left:50px; }
.tooltip {
width:100px;
height:20px;
position:absolute;
top:35px;
left:35px;
display:none; }
Javascript
$(".trigger").toggle(function () {
$(this).children(".tooltip").stop(true, true).fadeTo(200, 0.9);
$(this).siblings(".trigger").children(".tooltip").stop(true, true).fadeOut(200);
}, function () {
$(this).children(".tooltip").fadeOut(200);
});
The markup and CSS is simplified, but imagine I have several tooltips over the image. When I open one tooltip, all others should be closed. I'm guessing this is where things go wrong, but I can't see the error.
In a similar function on the same site, I've semi-dynamically added some IDs, and hide all that is :not(ID), but I just can't believe that should be necessary.
EDIT:
Behold, a Fiddle: http://jsfiddle.net/CfYRv/
change your javascript to something like
$(".trigger").click(function () {
$(".tooltip").fadeOut();
$(this).children(".tooltip").fadeIn();
});
Gah! Need to finish my homework, but long answer short: toggle doesn't work here because you toggle a submenu but then click another. this hides the first submenu, but it's still considered open (it was only hidden). Thus you need to click it twice to open it... I hacked together an alternative but it's not the best code. It'll at least give you an idea what needs done:
http://jsfiddle.net/uj2A4/
$(".trigger").click(function () {
if($(this).hasClass("active"))
$(".tooltip",this).fadeOut(200);
else {
$(this).children(".tooltip").stop(true, true).fadeTo(200, 0.9);
$(this).siblings(".trigger").children(".tooltip").stop(true, true).fadeOut(200);
}
$(this).toggleClass("active");
$(this).siblings(".trigger").removeClass("active");
});
Rather than toggle, let's use click: http://jsfiddle.net/CfYRv/3/
This assigns the "active" tooltip a css class "ttactive". Clicking on "some trigger" will fade out every active tooltip, and activate the one you just clicked. If the one you just clicked was the active one, all it does is fade that one out.
You could probably still use toggle this way:
$(".trigger").click(function () {
$(this).children(".tooltip").stop(true, true).toggle();
$(this).siblings(".trigger").children(".tooltip").stop(true, true).fadeOut(200);
});

How to make an underlaying element unclickable/deactive?

I have two elements on top of each other. When I click a button on the first div, the second div opens on top of the first div, and what I want to do is to make the underlaying div non-interactive (That I can't click on anything on the underlaying-div as long as the overlaying-div is open).
Javascript code:
$('#button').live('click', function()
{
$('#underlaying-div).fadeTo("fast", 0.7);
$('#overlaying-div).css('display', 'block');
//Do something here to make the underlaying div unclickable
});
$("#overlaying-div").live("click", function() {
$(this).hide();
$('#underlaying-div).fadeTo("slow", 1.0);
//Do something here to make the underlaying div clickable again
});
CSS-code:
#overlay-div
{
position:absolute;
left:0;
top:0;
display:none;
z-index: 20000;
}
I know I can use event.preventDefault() to make sure nothing happens if you click on an element in the underlaying-div, but I'd rather want that nothing happens at all when you for instance hover over an button (with preventDefault(), hover and other stuff still happens).
Any other ways in CSS or javascript/JQuery that can fix this problem??
Not sure of your final product, but if the underlaying div get overlapped by the overlaying in a way that the underlaying div is not visible anymore you could just display:block; the underlaying div.
This is a very old question, but if someone happens upon it, you might find it useful to toggle the pointer-events CSS property of the object you want to disable. You won't need to manually remove click bindings or add any other wrappers. If an object has pointer-events set to 'none', no events will fire when it is clicked.
In jQuery:
$('#underlaying-div).css('pointerEvents', 'none'); // will disable
$('#underlaying-div).css('pointerEvents', 'auto'); // will reenable
You could use unbind to remove the click event handler like this:
$(this).unbind('click'):
My concern is if this works with a live bind but you should at least try it :)
Why don't you use jQuery .fadeIn() and .fadeOut() functions? You have two divs with id="div1" and id="div2" and you have a button in div1 with id="button1" and a button in div2 with id="button2".
CSS code:
#div1 {
//some CSS code without z-index
}
#div2 {
//some CSS code without z-index
visibility:hidden;
}
jQuery code:
$('#button1').click(function(){$('#div1').fadeOut('slow', function(){$('#div2').fadeIn()})})
$('#button2').click(function(){$('#div2').fadeOut('slow', function(){$('#div1').fadeIn()})})

Is it possible to trigger a jQuery click event on a background-image in a list?

I have an unordered list of items, something like this, shortened for brevity:
<div id="elementsContainer">
<ul>
<li><a>One</a></li>
<li><a>Two</a></li>
</ul>
</div>
I have the list styled up, but these 3 styles deal with background images for the list items:
#elementsContainer ul li {
list-style:none;
}
#elementsContainer a {
background: transparent url(/images/icons/bullet_delete.png) no-repeat 5px 50%;
}
#elementsContainer a:hover,
#elementsContainer a:focus,
#elementsContainer a:active {
background:#fff url(/images/icons/delete.png) no-repeat 5px 50%;
}
The list looks great - it puts a little delete icon to the left of the text for each list item. However, I am looking to use jQuery (1.3) to handle the click events for each item, and I would like separate functionality between the background image of the list item and the text of the list item. If I click the image, I want to delete the item. If I click the text, I want to edit the item.
I started using something like this:
$("a").live("click", function(event){
alert( $(this).text() );
});
But I do not see anything in $(this) or "event" that I can determine if I am clicking the text or the image.
Yes, I know I could just have a separate "img" tag and handle the click on that separately. I'll go that route if that is the only option. I just want to know if there is some way to make it work on the background-image.
Thanks in advance!
Go with the IMG tag. The best you could do it detect a click on the LI element itself, which would end up being messy. An IMG tag (and even an A tag around it for semantic goodness and nicely-degrading pages) would work best.
You shouldn't have much issues styling it to look the same using an IMG within the LI, I do something similar all the time within lists where I need delete/edit icons.
You can't differentiate a click on the background image, since as far as the DOM is concerned, it's not really there. All you have is the a element itself (which happens to be presented with your background image), and its onclick handler will fire as long as you click anywhere inside the tag, text or not.
It probably is best to use an img tag (or some other separate tag) and handle the click on that separately, as you concluded in your write-up.
what you could do for the desired effect is to put a span with some spaces in the area that the delete image will eventually appear, and then hook the event to the click of that span.
Put an element over it, and steal register the event with that.

Categories

Resources