CSS - Hover passes through elements to activate hover on covered element - html

I have a page layout involving a lot of position absolute and z-indexing, so there are a lot of elements that are overlapping one another.
One of the elements just contains text and it hovers over top of a lot of other things.
Underneath that element there are several elements that have CSS Hover Pseudo Classes applied.
When the mouse passes over the element containing text, I would like for somehow the Element underneath to respond to the presence of the mouse and activate the pseudo class styles.
Is there any way to style an element so it allows the hover to pass through it to any elements underneath?
This wouldn't be too hard with JavaScript, but I would rather not go that route at the moment in order to keep things as simple as they can be. I'll be complicating things with JavaScript enough later.
Thanks
P.S. - I'm using HTML5 and CSS3 already, so I would have no problem with solutions utilizing these newer standards.

you can use pointer-events: none; to the element on top:
div {
width : 200px;
height : 200px;
float : left;
cursor : pointer;
border : 1px green solid;
}
div + div:hover { background: #a1a6c8; }
div:first-child {
pointer-events : none;
position : absolute;
top : 100px;
left : 100px;
border : 1px green solid;
background : #c1c6c8;
}
<div> on top of all: hover me </div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
as you can see, when you hover the element on the top the element behind is activated. For more info about this property: https://developer.mozilla.org/en/CSS/pointer-events

Also, something that people may find useful is that you can re-enable pointer events on child elements of the pointer-events:none element by setting them to pointer-events:auto explicitly. This doesn't seem to be well documented and turns out to be very useful. – slicedtoad
#slicedtoad's comment was hugely useful for me, so I think it deserves a full answer. If you set pointer-events: none to a parent, you'll disable the events for it's children. HOWEVER, if you set point-events: auto to the children, they will work again.
This also works when the children have a negative z-index, and therefore become unresponsive to pointer-events.

This is a Javascript solution which the question doesn't call for, but this page is connected to this sort of hover problem search query:
I have a case where this arrow button is sitting on top of an item that I want to remain hovered in grey. More specifically, the item is in a list of items that each will have various buttons appear on top when hovered over.
So with jQuery events:
var selectedItem;
var lastSelectedItem;
$(".item").mouseover(function(){
$(this).addClass("item-hovered") // css class with hover styles
selectedItem = $(this);
});
$(".item").mouseleave(function(){
$(this).removeClass("item-hovered")
selectedItem = null;
lastSelectedItem = $(this);
});
$(".button").mouseover(function(){
selectedItem = lastSelectedItem;
selectedItem.addClass("item-hovered")
});
"lastSelectedItem" is used to differentiate between moving off item onto whitespace and off item onto button, so selectedItem holds a value only if the mouse is either over an item or its corresponding button.

Related

How to use divs to grab users attention without overflowing window

I am trying to add a pulsing ring that rapidly and repeatedly shrinks around a target element to draw a user's attention to that location. Not subtle, but it'll do the job. I have several places on my site I want to place it, so I want to be able to attach it to any element without significantly altering the element that I attach it to.
My current attempt uses a div with absolute positioning with a wide border and border radius of 50%:
HTML Declaration:
<a href="mysite/readme">
<div class="attention-ring"></div>
Click Me
<a>
CSS
.attention-ring {
border-radius: 50%;
border: 10px solid red;
position: absolute;
z-index: 100;
}
The animation is achieved with jQuery. It gives it a width of 100% and quickly shrinks it using setInterval(), resetting when width reaches 0:
function animateRing() {
if ($('.attention-ring') != null) {
var ringDiameter = 100;
setInterval(function () {
if (ringDiameter > 0) {
$('.attention-ring').css({ "padding":`${ringDiameter}%`, "margin":`${-1 * ringDiameter}%`})
ringDiameter -= 1
} else {
ringDiameter = 100;
}
}, 10)
}
}
What I have does work. But there are a few problems:
The ring element at maximum diameter extends past the edge of the page. This is particularly bad for elements already close to the edge of the page. It causes intermittent blank space to appear past the edge of the page and makes the scroll-bars go haywire. Ideally I want the page to ignore that this element is going out of bound.
I currently need to place the div with the ring class directly inside elements. This means for elements, the user will have difficulty clicking on other elements on pages where this feature is active. Not ideal because I eventually want to add a way to turn this off using a different button.
How would I solve the above problems?
This is a perfect use-case for pseudo-elements and CSS animations. In fact, you don't need any Javascript at all.
Problem #1 happens because the script resizes the actual .attention-ring div itself. With position: absolute this doesn't affect the size of its container, but it will still trigger overflow if it gets beyond the container's bounds. Hence the dancing scrollbars. (You'd have to set any container to position:relative; overflow:hidden to prevent that... which could get hairy if you want to apply this in a lot of locations. Fortunately that's not necessary!)
Instead of resizing the div as an element, you can use transform: scale(<some number>) to scale the rendered element visually. (See CSS transform property.) This takes place after the "boxes" for each element are laid out in the browser, so it doesn't cause anything to overflow.
You can then animate the transform property in CSS with a named #keyframes rule, which gets attached to the animated element with the animation property.
Problem #2 can be solved with two steps. First, set pointer-events: none on the ring element. This makes clicks go "through" it, as if it's not part of the a tag it belongs to. Then you can avoid adding a separate div by turning the ring into a ::before pseudoelement. This way you can turn it on or off simply by adding or removing a class from the element you want to highlight.
Here's a demo of the whole thing in action:
.attention-ring::before {
display: block;
content: "";
pointer-events: none;
border-radius: 50%;
border: 10px solid red;
position: absolute;
animation: attention 1s cubic-bezier(0,0,.2,1) infinite;
}
#keyframes attention {
50%,to {
transform: scale(5); /* multiple of the initial size */
opacity: 0;
}
}
<p><a href="mysite/readme" class="attention-ring">
Click Me
</a></p>
<p><a href="#">
Also Click Me
</a></p>
<p
Note: the display:block; content:"" on the before:: makes the ring display, otherwise the browser treats it as an empty node and won't render it.
Credit: this is partly inspired by the ping animation in Tailwind CSS, but my solution above is a bit more flexible.

Force/3dTouch gesture on iphone gives weird background for <a> element

I have button which is <a> element with href, which doesnt have any background set on :active/:focus/:visited, but on force/3dTouch tap it gets this weird #b8b8bc background under the text only (while <a> doesnt have any children e.g. <span> etc so I suppose this is the text node highlight).
here's the gif to illustrate the behavior.
I've tried adding -webkit-tap-highlight-color: transparent but it changes only regular tap color, not the forced/3d one
also I thought maybe that's selection color (as I can reproduce this on various websites) so tried to use selection selectors which didn't help as well
::selection {
background: transparent;
}
::-webkit-selection {
background: transparent;
}
::-moz-selection {
background: transparent;
}
Any ideas about possible origin of this?
Good job digging up.
I had the same issue plus another one and here are my solutions.
Post is old but someone could find it useful like me today.
First of all, the forced background was covering my link text totally because I was using user-select: none; on my header links.
So that's something to check, just in case.
Regarding the background color, Force Touch doesn't use the link parent element background but the one that's under it.
If you want to "feel it", we could say that Forced Touch digs into the direct parent background and let the under layer appears.
So, to counter that without having to touch to background color, I use some z-index in the parent element to elevate it, preventing Forced Touch to "dig" :)
So if your links parent element is named card, you can add to your CSS:
.card {
isolation: isolate;
z-index:1;
}
Now, Force Touch will use the parent background color as we want to.
Okay so I found sort of "solution" based on parent's color.
Try to set *{background: red}.
If worked try set same on few parents .parent1 { background: pink}, .parent2 { background: lightblue}, .parent1 { background: salmon} etc.
In my case I found the color applied to force touched text was menu wrapper's background that takes most of the screen when menu is opened.
Side effect of this change - all forcetouched elements will have same color, no option to specify :hover or :active colors (you can see the color is slightly different on the 1st click) and ALL links will have same background:
I imagine you can try setting wrapper's background via JS based on what is clicked. Not sure if that will work. see docs here:
WebKit DOM Programming Topics
So far this seems to me too fragile to touch and I would not recommend doing this. Though you can change this color I've decided to let OS do what it wants here.

Position SVG lines over HTML elements, while still allowing interaction with the HTMl elements [duplicate]

I have a div that has background:transparent, along with border. Underneath this div, I have more elements.
Currently, I'm able to click the underlying elements when I click outside of the overlay div. However, I'm unable to click the underlying elements when clicking directly on the overlay div.
I want to be able to click through this div so that I can click on the underlying elements.
Yes, you CAN do this.
Using pointer-events: none along with CSS conditional statements for IE11 (does not work in IE10 or below), you can get a cross browser compatible solution for this problem.
Using AlphaImageLoader, you can even put transparent .PNG/.GIFs in the overlay div and have clicks flow through to elements underneath.
CSS:
pointer-events: none;
background: url('your_transparent.png');
IE11 conditional:
filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='your_transparent.png', sizingMethod='scale');
background: none !important;
Here is a basic example page with all the code.
Yes, you CAN force overlapping layers to pass through (ignore) click events.
PLUS you CAN have specific children excluded from this behavior...
You can do this, using pointer-events
pointer-events influences the reaction to click-, tap-, scroll- und hover events.
In a layer that should ignore / pass-through mentioned events you set
pointer-events: none;
Children of that unresponsive layer that need to react mouse / tap events again need:
pointer-events: auto;
That second part is very helpful if you work with multiple overlapping div layers (probably some parents being transparent), where you need to be able to click on child elements and only that child elements.
Example usage:
.parent {
pointer-events:none;
}
.child {
pointer-events:auto;
}
<div class="parent">
I'm unresponsive
I'm clickable again, wohoo !
</div>
Allowing the user to click through a div to the underlying element depends on the browser. All modern browsers, including Firefox, Chrome, Safari, and Opera, understand pointer-events:none.
For IE, it depends on the background. If the background is transparent, clickthrough works without you needing to do anything. On the other hand, for something like background:white; opacity:0; filter:Alpha(opacity=0);, IE needs manual event forwarding.
See a JSFiddle test and CanIUse pointer events.
I'm adding this answer because I didn’t see it here in full. I was able to do this using elementFromPoint. So basically:
attach a click to the div you want to be clicked through
hide it
determine what element the pointer is on
fire the click on the element there.
var range-selector= $("")
.css("position", "absolute").addClass("range-selector")
.appendTo("")
.click(function(e) {
_range-selector.hide();
$(document.elementFromPoint(e.clientX,e.clientY)).trigger("click");
});
In my case the overlaying div is absolutely positioned—I am not sure if this makes a difference. This works on IE8/9, Safari Chrome and Firefox at least.
Hide overlaying the element
Determine cursor coordinates
Get element on those coordinates
Trigger click on element
Show overlaying element again
$('#elementontop').click(e => {
$('#elementontop').hide();
$(document.elementFromPoint(e.clientX, e.clientY)).trigger("click");
$('#elementontop').show();
});
I needed to do this and decided to take this route:
$('.overlay').click(function(e){
var left = $(window).scrollLeft();
var top = $(window).scrollTop();
//hide the overlay for now so the document can find the underlying elements
$(this).css('display','none');
//use the current scroll position to deduct from the click position
$(document.elementFromPoint(e.pageX-left, e.pageY-top)).click();
//show the overlay again
$(this).css('display','block');
});
I currently work with canvas speech balloons. But because the balloon with the pointer is wrapped in a div, some links under it aren't click able anymore. I cant use extjs in this case.
See basic example for my speech balloon tutorial requires HTML5
So I decided to collect all link coordinates from inside the balloons in an array.
var clickarray=[];
function getcoo(thatdiv){
thatdiv.find(".link").each(function(){
var offset=$(this).offset();
clickarray.unshift([(offset.left),
(offset.top),
(offset.left+$(this).width()),
(offset.top+$(this).height()),
($(this).attr('name')),
1]);
});
}
I call this function on each (new) balloon. It grabs the coordinates of the left/top and right/down corners of a link.class - additionally the name attribute for what to do if someone clicks in that coordinates and I loved to set a 1 which means that it wasn't clicked jet. And unshift this array to the clickarray. You could use push too.
To work with that array:
$("body").click(function(event){
event.preventDefault();//if it is a a-tag
var x=event.pageX;
var y=event.pageY;
var job="";
for(var i in clickarray){
if(x>=clickarray[i][0] && x<=clickarray[i][2] && y>=clickarray[i][1] && y<=clickarray[i][3] && clickarray[i][5]==1){
job=clickarray[i][4];
clickarray[i][5]=0;//set to allready clicked
break;
}
}
if(job.length>0){
// --do some thing with the job --
}
});
This function proofs the coordinates of a body click event or whether it was already clicked and returns the name attribute. I think it is not necessary to go deeper, but you see it is not that complicate.
Hope in was enlish...
Another idea to try (situationally) would be to:
Put the content you want in a div;
Put the non-clicking overlay over the entire page with a z-index higher,
make another cropped copy of the original div
overlay and abs position the copy div in the same place as the original content you want to be clickable with an even higher z-index?
Any thoughts?
I think the event.stopPropagation(); should be mentioned here as well. Add this to the Click function of your button.
Prevents the event from bubbling up the DOM tree, preventing any parent handlers from being notified of the event.
Just wrap a tag around all the HTML extract, for example
<a href="/categories/1">
<img alt="test1" class="img-responsive" src="/assets/photo.jpg" />
<div class="caption bg-orange">
<h2>
test1
</h2>
</div>
</a>
in my example my caption class has hover effects, that with pointer-events:none; you just will lose
wrapping the content will keep your hover effects and you can click in all the picture, div included, regards!
An easier way would be to inline the transparent background image using Data URIs as follows:
.click-through {
pointer-events: none;
background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);
}
I think that you can consider changing your markup. If I am not wrong, you'd like to put an invisible layer above the document and your invisible markup may be preceding your document image (is this correct?).
Instead, I propose that you put the invisible right after the document image but changing the position to absolute.
Notice that you need a parent element to have position: relative and then you will be able to use this idea. Otherwise your absolute layer will be placed just in the top left corner.
An absolute position element is positioned relative to the first parent
element that has a position other than static.
If no such element is found, the containing block is html
Hope this helps. See here for more information about CSS positioning.
You can place an AP overlay like...
#overlay {
position: absolute;
top: -79px;
left: -60px;
height: 80px;
width: 380px;
z-index: 2;
background: url(fake.gif);
}
<div id="overlay"></div>
just put it over where you dont want ie cliked. Works in all.
This is not a precise answer for the question but may help in finding a workaround for it.
I had an image I was hiding on page load and displaying when waiting on an AJAX call then hiding again however...
I found the only way to display my image when loading the page then make it disappear and be able to click things where the image was located before hiding it was to put the image into a DIV, make the size of the DIV 10x10 pixels or small enough to prevent it causing an issue then hiding the containing div. This allowed the image to overflow the div while visible and when the div was hidden, only the divs area was affected by inability to click objects beneath and not the whole size of the image the DIV contained and was displaying.
I tried all the methods to hide the image including CSS display=none/block, opacity=0, hiding the image with hidden=true. All of them resulted in my image being hidden but the area where it was displayed to act like there was a cover over the stuff underneath so clicks and so on wouldn't act on the underlying objects. Once the image was inside a tiny DIV and I hid the tiny DIV, the entire area occupied by the image was clear and only the tiny area under the DIV I hid was affected but as I made it small enough (10x10 pixels), the issue was fixed (sort of).
I found this to be a dirty workaround for what should be a simple issue but I was not able to find any way to hide the object in its native format without a container. My object was in the form of etc. If anyone has a better way, please let me know.
I couldn't always use pointer-events: none in my scenario, because I wanted both the overlay and the underlying element(s) to be clickable / selectable.
The DOM structure looked like this:
<div id="outerElement">
<div id="canvas-wrapper">
<canvas id="overlay"></canvas>
</div>
<!-- Omitted: element(s) behind canvas that should still be selectable -->
</div>
(The outerElement, canvas-wrapper and canvas elements have the same size.)
To make the elements behind the canvas act normally (e.g. selectable, editable), I used the following code:
canvasWrapper.style.pointerEvents = 'none';
outerElement.addEventListener('mousedown', event => {
const clickedOnElementInCanvas = yourCheck // TODO: check if the event *would* click a canvas element.
if (!clickedOnElementInCanvas) {
// if necessary, add logic to deselect your canvas elements ...
wrapper.style.pointerEvents = 'none';
return true;
}
// Check if we emitted the event ourselves (avoid endless loop)
if (event.isTrusted) {
// Manually forward element to the canvas
const mouseEvent = new MouseEvent(event.type, event);
canvas.dispatchEvent(mouseEvent);
mouseEvent.stopPropagation();
}
return true;
});
Some canvas objects also came with input fields, so I had to allow keyboard events, too.
To do this, I had to update the pointerEvents property based on whether a canvas input field was currently focused or not:
onCanvasModified(canvas, () => {
const inputFieldInCanvasActive = // TODO: Check if an input field of the canvas is active.
wrapper.style.pointerEvents = inputFieldInCanvasActive ? 'auto' : 'none';
});
it doesn't work that way. the work around is to manually check the coordinates of the mouse click against the area occupied by each element.
area occupied by an element can found found by 1. getting the location of the element with respect to the top left of the page, and 2. the width and the height. a library like jQuery makes this pretty simple, although it can be done in plain js. adding an event handler for mousemove on the document object will provide continuous updates of the mouse position from the top and left of the page. deciding if the mouse is over any given object consists of checking if the mouse position is between the left, right, top and bottom edges of an element.
Nope, you can't click ‘through’ an element. You can get the co-ordinates of the click and try to work out what element was underneath the clicked element, but this is really tedious for browsers that don't have document.elementFromPoint. Then you still have to emulate the default action of clicking, which isn't necessarily trivial depending on what elements you have under there.
Since you've got a fully-transparent window area, you'll probably be better off implementing it as separate border elements around the outside, leaving the centre area free of obstruction so you can really just click straight through.

Element to be visible only for particular height

I am new to HTML, CSS and Javascript.
I have an element (navigation dots) that should be visible only for a particular segment of my page. The rest of the time I would prefer it to be hidden. I tried using media query:
#media screen and (min-height:200vh) and (max-height: 600vh)
{
.invisible{
visibility: visible;
}
}
The segment is in one div. So would it be possible to make this div visible while hovering on the other div?
First of all using vh and vw in your media queries is not going to work. By definition 100vh is 100% of the viewport height of a device viewing your site. What this means is a min-height: 200vh media query will only apply to devices whose viewport is 2x higher than the device viewport itself. Nothing can be two times higher than itself, unless it's 0, so this media query will never take effect.
That said, I hope you can clarify what exactly you mean by segment and what you are trying to accomplish. Are you trying to...
Hide the nav-dots when a client's viewport height doesn't meet a certain value?
Hide nav-dots on a particular DOM element such as <p> or <div>?
Hide nav-dots when a user has scrolled past a certain threshold?
To answer the second scenario and your later question about hiding a div while hovering on another I propose the following solution. However I'd be happy to update my answer if this is not what you were getting at.
HTML:
<div class="container" id="my_segment">
<div class="nav-dots">
Navigation dots go here
</div>
</div>
Here we have assigned the my_segment id to the element we want to hide the nav-dots on. Note that you can use a unique id only once per page.
CSS:
.container {
display: inline-block;
background-color: #FFCCAA;
}
.invisible {
visibility: hidden;
}
JavaScript:
// Find our element
let seg = document.getElementById('my_segment');
// Add a mouseover event handler to our element
seg.addEventListener('mouseover', function() {
// Find nav-dots inside our element
let nav_dot_elements = seg.getElementsByClassName('nav-dots');
// Remove the 'invisible' class
nav_dot_elements[0].classList.remove('invisible');
});
// Add a mouseout event handler
seg.addEventListener('mouseout', function() {
// Find nav-dots inside our element
let nav_dot_elements = seg.getElementsByClassName('nav-dots');
// Add the 'invisible' class
nav_dot_elements[0].classList.add('invisible');
});
Codepen demo
Since you are new to JS I tried to keep this example simple. This only works if you use the 'hover to display' behaviour on one element and you only use one 'nav-dots' element inside this container. Try it out for yourself and let me know how it works!

How to maintain the hover state while hovering the submenu?

im trying to make the button in hover state while the cursor still in the submenu but can't figure out. Really need help! Thanks!
function hideAllCat() {
$("#categories").hide();
}
hideAllCat();
$("#tab50").mouseover(function(){
hideAllCat();
$("#categories").slideDown();
});
$("#categories").mouseleave(function() {
hideAllCat();
});
http://jsfiddle.net/G5RtR/24/
Your #tab50 element are not the parent to #categories element, therefore you cannot use the pseudo-class :hover. :hover only works when the object is right under you mouse arrow. That being said, you now have 2 (++) possible solutions.
Make #tab50 parent to #categories. The when the sub-menu expands, the #tab50 also expands correspondingly.
Use javascript to do the job. $("#categories").mouseover(function({$("#tab50").css({'background':'#abc','color':'red'})});
And remember to change the colors of #tab50 back to normal in $("#categories").mouseleave();
Note:
#categories should be a class instead of an id, since hideAllCat() should hide all categories, and one cannot have more than 1 id in a html file.