Strange combination of jQuery mouseenter, mouseleave, and append - javascript

I've got an interesting issue with combining jQuery's mouseenter and mouseleave events with a call to append. My object is to show extra content when the mouse enters something and then remove it when the mouse leaves. It's very similar to what happens when you mouse over a tag here on StackExchange. The sequence is:
In mouseenter, create content and position it by the element under the mouse via .offset and .append.
In mouseleave, remove that content from the screen.
The element I'm operating on is an img, and I'm using jQuery 1.6.2. The problem is that .append somehow triggers mouseleave, which is quickly followed by .mouseenter, ad infinitum. It appears as a strange flickering effect on the content being added, as it's removed and re-added repeatedly. See an example here on jsFiddle. Why is this happening, and how do I resolve it?
EDIT: figured it out. D'oh. The added content was appearing under the mouse.

The reason this happens is that you're adding content where your mouse is. That new content is not part of your original element so by definition when you show the new DIV your mouse is not over the IMG any more.
One way to solve this would be to use the image as a background of a parent DIV and then append the new DIV to the parent so that the new DIV is a child of the parent.
On a side note is there a reason you chose not to use .hover()?
Here's a working jsfiddle for how I'd do this.
HTML:
<div class="papa">
<div class='myDiv'>
<div id='divHover'>Hello World</div>
</div>
</div>
Javascript/Jquery:
$(document).ready(function() {
$(".papa").hover(
function () {
$(".myDiv").show();
},
function () {
$(".myDiv").hide();
}
);
});
CSS:
.papa {
background-image:url('http://dummyimage.com/100x100/000/fff');
height: 100px;
width: 100px;
}
.myDiv {
display: none;
height: 30px;
border: 1px solid black;
}
#divHover {
width:100px;
height:20px;
background-color:white;
border:1px solid black;
position: relative;
top: 10px;
left: 10px;
}

Related

Strange behavior with absolute positioning, style.top, style.left and "a" tag

I was trying to complete a simple task on Javascript.info and I'm getting my ass beaten by an "a" tag. The task is to simply place (and remove) a tooltip above the element on hover and I have no problem with the roof or the house, but when I try to place the box above the link, it breaks and I can't solve it for my life.
I'm asking for help here because the solution on the site uses position:fixed while I'm trying to use position:absolute and simply mimicking the solution won't help me learning anything. The problem is all on line 77 and 78, when I try to assign tooltip.style.left and tooltip.style.top.
If I try to assign it usign a literal (for example, "-58px"), it works. Otherwise, it just defaults to whatever value the tooltip on "house" would have. I tried to see what is going on with some tactical alerts and it drove me insane. It shows me that if I use a computed value, it defaults and if I use a literal, it will work normally.
I'd like someone to explain what is going on and possibly some insight (pointing out if I got wrong how position:absolute works, how element size properties works or something on this nature)
The code (I only made the part that is inside of the script tag on line 64, the rest is from the authors of the task):
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<style>
body {
height: 2000px;
/* the tooltip should work after page scroll too */
}
.tooltip {
position: fixed;
z-index: 100;
padding: 10px 20px;
border: 1px solid #b3c9ce;
border-radius: 4px;
text-align: center;
font: italic 14px/1.3 sans-serif;
color: #333;
background: #fff;
box-shadow: 3px 3px 3px rgba(0, 0, 0, .3);
}
#house {
margin-top: 50px;
width: 400px;
border: 1px solid brown;
}
#roof {
width: 0;
height: 0;
border-left: 200px solid transparent;
border-right: 200px solid transparent;
border-bottom: 20px solid brown;
margin-top: -20px;
}
p {
text-align: justify;
margin: 10px 3px;
}
</style>
</head>
<body>
<div data-tooltip="Here is the house interior" id="house">
<div data-tooltip="Here is the roof" id="roof"></div>
<p>Once upon a time there was a mother pig who had three little pigs.</p>
<p>The three little pigs grew so big that their mother said to them, "You are too big to live here any longer. You must go and build houses for yourselves. But take care that the wolf does not catch you."</p>
<p>The three little pigs set off. "We will take care that the wolf does not catch us," they said.</p>
<p>Soon they met a man. Hover over me</p>
</div>
<script>
house.onmouseover= function(event){
let target= event.target.closest('[data-tooltip]');
let tooltip= document.createElement('div');
tooltip.textContent= target.dataset.tooltip;
tooltip.classList.add("tooltip");
target.append(tooltip);
if(!tooltip.parentElement.style.position){
tooltip.parentElement.style.position= 'relative';
}
tooltip.style.position= 'absolute';
tooltip.style.top= "-"+(tooltip.offsetHeight+5)+"px";
tooltip.style.left= -target.clientLeft+(target.offsetWidth-tooltip.offsetWidth)/2+"px";
//alert("-"+(tooltip.offsetHeight+5)+"px");
//alert(tooltip.style.top);
}
house.onmouseout= function(event){
let target= event.target.closest('[data-tooltip]');
tooltips= target.querySelectorAll('.tooltip');
for(tooltip of tooltips){
tooltip.remove();
}
}
</script>
</body>
</html>
Thanks already :)
when I try to place the box above the link, it breaks
This is because the tooltip box is positioned to appear under the mouse. Hovering over the link generates a regenerative feedback loop of
Create and append the tooltip element to the <a> element
The mouse is over the tooltip element
Fire mouseout on the a element in preparation of firing mouseover on the tooltip.
mouseout handling removes the tooltip element
The mouse is now over the a element,
Fire mouseover on the a element and repeat from step 1.
The roof and interior mouseover events don't trigger the loop because the tooltip box is outside the target element with the data-tooltip attribute.
You could try
Moving the tooltip box so it cannot appear under the mouse, or
Think of creative ways of using mousenter and mouseleave events on the anchor element that don't fire when hovering over the tooltip because it is a child of the anchor element, or
Turn off pointer events from tooltip elements:
.tooltip {
pointer-events: none;
}
Additional listeners used to verify the problem:
house.addEventListener("mouseover", e=>console.log("over"));
house.addEventListener("mouseout", e=>console.log("out"));
The additional delay caused by console.log did result in the tooltip box being rendered and becoming visible in Firefox, but the log output definitely confirms the feed back loop in action.

hover style on page refresh

I'm looking for a simple way to persist a "hover" style on a page refresh if the user did not move his mouse. The issue is that the hover style is only triggered on a mouse enter/mouseover event, so on a page refresh it will not be applied even if the cursor is above the item in question until the user touches the mouse again and moves it slightly. The code below has this issue.
$('div').click(function () {
window.location.reload();
});
div {
width: 100px;
height: 100px;
background: grey;
}
div:hover {
background: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div></div>
can you set the a:visited { background-color:black;color:#fff} Of course this would apply to the whole page so all your visited backgrounds would be black. I've never tried to marry div a:visited{background-color:black;color:#fff;} so not sure that would work. They say nothing ventured, nothing gained.

Capturing an event through a DOM layer that contains other event listeners

tldr; I want to have a button's event captured (click) even though it's under a DOM layer.
Here's my problem, I have a DOM layer that's relatively positioned and has a z-index set higher than 1, let's just say 2. That DOM layer is above the button (Button A) I'd like to have triggered when clicked. The reason that DOM layer is above the button (Button A) in question, is that the button (ShoreMore) across from it has another event that when clicked, opens a drawer of other little links.
Here's what I've tried:
I tried adding pointer-events: none; to the DOM layer above my button. problem is that while it now allows the button to be pressed, the DOM layer with the button that opens the drawer of other link no longer works. Suggested by this SO question.
I also came across this little trick found on this website. It essentially, hides the mask and rechecks the user's click coordinates and fires the event that is found within the coordinate. However, I found myself unsatisfied with the results, as I'm often given DOM that's unhelpful too specific or too broad based on the user's click. (e.g. getting the icon, text next to the icon, etc. of the Button).
For illustration purposes, here's what I have:
Here's my code:
HTML
<div id="drawer" class="drawer">
<div id="shield" class="shield"></div>
<div id="expander" class="expander">
<div class="inner">
<ul>
<li>links</li>
<li>links</li>
<li>links</li>
<li>links</li>
<li>links</li>
</ul>
<div id="tab" class="tab" >
<i class="icon"></i> Show More
</div>
</div>
</div>
</div>
<span id="btnA" class="btn">
<i class="icon"></i>
<span>Button A</span>
</span>
CSS
.drawer {
position: relative;
height: 0;
z-index: 2;
margin-bottom: .5em;
}
.expander {
position: relative;
height: 28px;
transition: height .2s ease;
overflow: hidden;
}
.inner {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
margin-bottom: 28px;
}
I didn't include the javascript, but "Button A" and "Show More" have a click listener. They both work, but Button A is confirmed to work if pointer-events: none; is added to the CSS of the class "expander."
EDIT: spelling
One possible solution is to use more absolute positioning.
The problem you're running into is that HTML elements, no matter their shape, end up as rectangles when rendered. Your blue outlined layer has a complex shape that's not strictly rectangular, but HTML doesn't care - it expands the layer's shape into a big rectangle to cover the parent element and all of its children elements, as you've correctly drawn in your diagram.
Absolute positioning helps prevent that from happening. Instead of leaving space for an element in the document flow, absolute positioning sort of pops the element out and positions it relative to its parent. The result is an element that doesn't expand the borders of its parent element, because it essentially takes up zero space in the normal document flow.
Consider the following example:
$(function(){
function slideDown(){
this.innerHTML = "Close";
$("#tray").animate({top: "50px"});
$("#higher-button").off("click").on("click", slideUp);
}
function slideUp(){
this.innerHTML = "Show More";
$("#tray").animate({top: "0px"});
$("#higher-button").off("click").on("click", slideDown);
}
$("#higher-button").on("click", slideDown);
$("#lower-button").on("click", function(){
alert("Lower button clicked.");
});
});
* {
margin: 0;
padding: 0;
text-align: center;
line-height: 50px;
}
#box {
position: absolute;
top: 20px;
left: 20px;
}
#lower-button {
width: 200px;
height: 50px;
background-color: #cccccc;
position: absolute;
top: 50px;
}
#higher-button {
width: 200px;
height: 50px;
background-color: #888888;
position: absolute;
top: 50px;
left: 200px;
}
#tray {
width: 400px;
height: 50px;
background-color: #aaaaaa;
position: absolute;
}
#mask {
position: absolute;
width: 400px;
height: 50px;
background-color: #dddddd;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="box">
<div id="lower-button">Button A</div>
<div id="tray">
<div id="higher-button">Show More</div>
</div>
<div id="mask">Mask</div>
</div>
Absolute positioning lets you easily layer and position elements in a way that avoids them taking up excess space.
The caveat to all this is that absolute positioning can be pretty messy. Since it removes elements from the normal document flow, they don't take up any space, and it can wreak havok with your layouts. So use absolute positioning sparingly, for cases like this where you're building a UI element that you probably don't need taking up space in the layout anyway.
As always there are dozens of ways to solve this problem and this is only one possibility, but I hope it helps you figure out your own solution. Good luck!
Edit: Note you don't necessarily need to make all of the UI elements absolutely positioned, only the ones you need in order to manage the document flow. For example, the parent UI element could still be relatively positioned, and you just "pop out" the individual UI components. You still need to manually manage the size of the parent UI container, because absolutely positioned elements take up zero space in the flow. jsfiddle.net/v2646v41
One easy solution would be to change the z-index of Button A. When the drawer is closed, set it higher than the drawer's div, and when Show More is clicked, set the z-index underneath, then back above after the drawer has slid back under the mask.

How to use anchor tag to scroll within div, without autoscrolling entire page?

I would like to use anchor tags to scroll within a div on the webpage. However, when I click on the anchor tag, the entire page jumps to the anchor tag within the div.
The content within the div should scroll, without the body of the webpage autoscrolling.
I've been googling and trying to figure this out for weeks & have not yet found an acceptable solution. It seems to be a really commonly asked question, too.
I know very little about javascript, but from what I gather there seem to be two possible ways of accomplishing this:
1. To make the body of the page scrollable only by mousewheel/manually and not with anchor tags. This would apply to the body only and not other elements.
-or-
2. To scroll to the anchor tag within the div, and cancel the process before it affects the body.
A bonus would be if it did not add the anchor tag to the url.
Here are some additional links to similar questions, may have some ideas you could work with:
seems promising, could not get it to work
Scrolling within a div without moving page
uses event.preventDetfault() or event.returnValue = false
may work for body after anchor link scroll to right
http://js-unit-testing.com/2013/08/08/preventing-anchor-clicking-from-scrolling-to-the-top/
other ideas for similar problems
How to prevent page scrolling when scrolling a DIV element?
How to go to anchor tag in scrollable div without having the whole browser jump down?
How can I differentiate a manual scroll (via mousewheel/scrollbar) from a Javascript/jQuery scroll?
other approaches to similar question
HTML anchor link with no scroll or jump
Here is a sample HTML and CSS with the relevant elements. You may resize the browser window so that there is a scrollbar for the main page to see the undesirable autoscroll effect:
<html>
<body>
<div id="main">
<div id="mainwide">
<div id="left">
2
</div>
<div id="right">
<a id="link" name="2">2</a>
</div>
</div>
</div>
</body>
</html>
Here is the CSS
html {
background-color: LightGreen;
}
#main {
border: 1px solid black;
margin-top: 200px;
width: 500px;
height: 300px;
overflow: hidden;
}
#mainwide {
width: 1000px;
height: 300px;
}
#left {
background-color: MediumSpringGreen;
width: 500px;
height: 300px;
float: left;
display: inline;
}
#right {
background-color: MediumSeaGreen;
width: 500px;
height: 300px;
float: left;
display: inline;
}
a#link {
float: right;
margin: 0;
padding: 0;
}
Maybe you can gain some points for definitively answering this commonly asked question?
You can use the scrollTop function from Jquery to position the scrollable div exactly how you want it.
$( "#viv_button" ).click(function() {
var container = document.getElementById('col2');
var scrollTo = document.getElementById('viv');
container.scrollTop = scrollTo.offsetTop;
});
Here is a fiddle of it working
I used anchor tags to locate it, but you can use anything with an ID.
I found this occurred because there was a parent of the element within which you have your href='#id_of_element'.
To fix this....
// javascript
document.body.position = 'absolute';
// or css
.body {
position: absolute;
}
You could use fixed, or relative. But this stops the body ( main page ) from scrolling on selection of href somewhere within a child element.

Active state pseudo class and Adjacent element selector combined on IE

I'm currently having an issue with this on IE - http://jsbin.com/riyaxewo/4
HTML
<div class="a">
1
</div>
<div class="b">
2
</div>
CSS
.a, .b {
height: 50px;
width: 50px;
padding: 10px 10px;
border: 1px solid;
text-align: center;
}
.a:active, .b:active {
background-color: red;
}
.a:hover + .b {
background-color: transparent;
}
.a:active + .b {
background-color: yellow;
}
The expected result is for box #2 to be yellow whenever box #1 is pressed, however, on IE this
effect only occurs once, and then it just wont happen again.
The reason I'm doing this in CSS and not programatically is because I want the effect to take place as long as the mouse was pressed on the element, even if the mouse button was released somewhere else (meaning I can't rely on mouseup, and mouseleave/mouseout will not get me the wanted result)
Hope this helps: https://stackoverflow.com/a/17211251/3884420
apparently its a bug in IE and its the same for children of active elements and anything like that. you could try a js script that uses on mousedown and on mouseup triggers. I know you said you can't.
also something even funnier happened while testing your problem. if you try to leave your mouse while active it works every time. I removed hover property you added and even this stopped working. I'm guessing every time that your mouse leaves and hover effect stops, the active effect fires again.

Categories