MouseEvent swallowed if element hidden by css - javascript

I have a element (it is an icon) with onClick event. I want to show that element only if certain criteria is met and I'm able to read that criteria and handle the show/hide completely from CSS.
If the element is visible and I click on it, I want it to make two actions:
trigger JS event,
hide the element.
But CSS hides the element somehow quicker than JS can respond and the event is simply not triggered.
I did not studied how the the event system in JS works, but it seems to me, that firstly the CSS is resolved and then the JS event system receives info about click event on certain x/y position, where the element is no more, so the event is not triggered.
I tried several CSS options to hide the element including this CSS properties:
display: block / none;
visibility: visible / hidden;
z-index: 1 / -1;
width|height: auto / 0;
top|left|right|bottom: 0 / -9999px;
If I hide the element with opacity: 1 / 0, the event is triggered, because the element remains clickable in place, where it resides, but that is also problem, because I do not want the element to be clickable if not visible.
Is there any hack, how to hide element via pure CSS and at the same time trigger event on it?
I tried delay the CSS with transition: all 160ms;, but the CSS rules, that hides the element, are instant (you cannot transition display, visibility or z-index) so this had no use.
I challenge this problem in Google Chrome 53.0.2785.116 platform Win10 x64
EDIT : JSBin

Problem with click is when the item is hidden, the click action can not be completed. So you can switch to mousedown instead of click
document.getElementById("test_click").addEventListener("mousedown", function(){
console.log("Clicked");
});
#test_container {
position: relative;
display: inline-block;
}
#test_click {
position: absolute;
display: none;
left: 100%;
top: 50%;
}
#test_input:focus + #test_click {
display: inline;
}
#test_input:focus + #test_click:hover {
color: dodgerblue;
cursor: pointer;
}
<div id="test_container">
<input type="text" id="test_input" placeholder="focus me...">
<span id="test_click">CLICK</span>
</div>
<h3>Click on the input and "CLICK" appears, click on "CLICK" and "CLICK" hides and no JS event is triggered although it is attached. Then, remove "display: none;" from CSS and try click again, the JS event is triggered.</h3>
or add css to keep element visible when hovered
document.getElementById("test_click").addEventListener("click", function(){
console.log("Clicked");
});
#test_container {
position: relative;
display: inline-block;
}
#test_click {
position: absolute;
display: none;
left: 100%;
top: 50%;
}
#test_click:hover,
#test_input:focus + #test_click {
display: inline;
}
#test_input:focus + #test_click:hover {
color: dodgerblue;
cursor: pointer;
}
<div id="test_container">
<input type="text" id="test_input" placeholder="focus me...">
<span id="test_click">CLICK</span>
</div>
<h3>Click on the input and "CLICK" appears, click on "CLICK" and "CLICK" hides and no JS event is triggered although it is attached. Then, remove "display: none;" from CSS and try click again, the JS event is triggered.</h3>

Related

Why is my website is not responding to my JS script?

I am trying to use JavaScript so when you click on the menubars icon, the whole menu appears when opening the website on a phone using Google. (when device width is larger than 470px, menubars have a display: none)
This is the necessary html:
<div id="menu">
<i class="fa fa-bars" aria-hidden="true" style='font-size:36px; color: white' id="menubars"></i>
<nav>
Home
Projects
Contact
</nav>
</div>
This is the css:
#media only screen and (max-width: 470px) {
body{
background-image: none;
}
#menu {
position: absolute;
display: flex;
flex-direction: row-reverse;
justify-content: flex-end;
z-index: 3;
padding-bottom: 30px;
padding-right: 5px;
width: 75%;
background-color: black;
transform: translate(-195px, 0);
}
#menubars {
display: initial;
}
and this is the JS:
const menubars =document.getElementById("menubars");
const menu = document.getElementById("menu");
menubars.addEventListener("touch", function() {
menu.style.borderRight = "white thin";
menu.style.transform = "translate(195px)";
});
I know the script is linked correctly because I can see the event listener when clicking inspect element.
website opened with devtools
The website is :
https://maria-shn.github.io/Portfolio
I am new to JS and I know there is probably a better way to achieve my goal but I would like to understand why it is not working.
AFAIK there is no touch event, there are touchstart, touchend, touchmove and touchcancel. If you put a breakpoint inside your event handler, you can see it is never triggered. (maybe try first with click event, since this is hidden on desktop anyway...)
The border-right value is missing what kind of line you want to present (solid, dashed, etc.) (explanation)
Not sure if this is what you intended, but translate(195px) will translate both the X and Y positions, you can use translateX() (or Y) to move just one
we don't have touch event, you can see about touchcancel, touchend, touchmove, touchstart. We can start testing by click.

Add event listener to ::after with javascript [duplicate]

I have a div element with a CSS pseudo-element ::before used as a close button (instead of using an actual button). How do I apply an event listener to only the pseudo-element?
HTML
<div id="box"></div>
CSS
#box:before
{
background-image: url(close.png);
content: '';
display: block;
height: 20px;
position: absolute;
top: -10px;
right: -10px;
width: 20px;
}
#box
{
height: 100px;
width: 100px;
}
Was looking for a solution and found this thread. Now want to share my workaround:
CSS
element { pointer-events: none; }
element::after { pointer-events: all; }
JS
element.addEventListener('click', function() { ... });
This works if you don't need any pointer events on element. Check it in action.
No. The pseudo-element does not exist in the DOM so it has no HTMLElementNode object representing it.
There is no :before and :after selector in jQuery. However as an alternative, you can check the location of the click event and check if it is on your pseudo-element:
(I haven't tested this)
style:
#mything {
width: 100px;
height: 100px;
position: relative;
background: blue;
}
#mything:after {
content: "x";
font-size: 10px;
position: absolute;
top: 0;
right: 0;
background: red;
width: 10px;
height: 10px;
}
javascript:
$('#mything').click(function(e) {
if (e.clientX > $(this).offset().left + 90 &&
e.clientY < $(this).offset().top + 10) {
// do something
}
});
html:
<div id="mything">Foo</div>
If the position and dimensions of the generated content are known, you can add a click handler on the element itself and check the relative mouse position before handling it.
This is perverse, but possible.
Generally speaking no as indicated by others. But in case your tag with pseudo-element is empty, such as glyphicon in bootstrap.
You can wrap a div or span or whatever tag suitable in your case. Glyphicons in bootstrap uses :before but on the page you can see that it has hover event caught.
Example:
<span>
<span class="glyphicon glyphicon-asterisk" aria-hidden="true"></span>
</span>
Suppose you want to add event to the above glyphicon, you can use jquery parent selector
$('glyphicon').parent('span').click(function() {
// awesome things
});
Just a small trick in case your tag is empty.

Why does reflow need to be triggered for CSS transitions?

I was reading this article http://semisignal.com/?p=5298 and the author wrote that
"Reflow needs to be triggered before the invisible class is removed in order for the transition to work as expected. "
My questions are :
1) Why does reflow need to be triggered?
2) I understand that we should avoid using reflow, if that is true why is the author suggesting to use reflow in order to make the transition work?
3) Instead of using reflow, is there a different method to make the transition work?
Thank you.
(Effectively: "Why can't I easily use transitions with the display property")
Short Answer:
CSS Transitions rely on starting or static properties of an element. When an element is set to display: none; the document (DOM) is rendered as though the element doesn't exist. This means when it's set to display: block; - There are no starting values for it to transition.
Longer Answer:
Reflow needs to be triggered because elements set to display: none; are not drawn in the document yet. This prevents transitions from having a starting value/initial state. Setting an element to display: none; makes the document render as if the element isn't there at all.
He suggest reflowing because it's generally accepted to hide and show elements with display: none; and display: block; - typically after the element has been requested by an action (tab or button click, callback function, timeout function, etc.). Transitions are a huge bonus to UX, so reflowing is a relatively simple way to allow these transitions to occur. It doesn't have an enormous impact when you use simple transitions on simple sites, so for general purposes you can trigger a reflow, even if technically you shouldn't. Think of the guy's example like using unminified JavaScript files in a production site. Can you? Sure! Should you? Probably not, but for most cases, it won't make a hugely noticeable difference.
There are different options available that prevent reflowing, or are generally easier to use than the method in the link you provided. Take the following snippet for a few examples:
A: This element is set to height: 0; and overflow: hidden;. When shown, it's set to height: auto;. We apply the animation to only the opacity. This gives us a similar effect, but we can transition it without a reflow because it's already rendered in the document and gives the transitions initial values to work with.
B: This element is the same as A, but sets the height to a defined size.
A and B work well enough for fading in elements, but because we set the height from auto/100px to 0 instantly, they appear to collapse on "fade out"
C: This element is hidden and we attempt to transition the child. You can see that this doesn't work either and requires a reflow to be triggered.
D: This element is hidden and we animate the child. Since the animation keyframes give a defined starting and ending value, this works much better. However note that the black box snaps into view because it's still attached to the parent.
E: This works similarly to D but we run everything off the child, which doesn't solve our "black box" issue we had with D.
F: This is probably the best of both worlds solution. We move the styling off the parent onto the child. We can trigger the animation off of the parent, and we can control the display property of the child and animate the transition as we want. The downside to this being you need use animation keyframes instead of transitions.
G: While I don't know if this triggers a reflow inside the function as I haven't parsed it myself, you can just simply use jQuery's .fadeToggle() function to accomplish all of this with a single line of JavaScript, and is used so often (or similar JS/jQuery fadeIn/fadeOut methods) that the subject of reflowing doesn't come up all that often.
Examples:
Here's a CodePen: https://codepen.io/xhynk/pen/gerPKq
Here's a Snippet:
jQuery(document).ready(function($){
$('button:not(#g)').click(function(){
$(this).next('div').toggleClass('show');
});
$('#g').click(function(){
$(this).next('div').stop().fadeToggle(2000);
});
});
* { box-sizing: border-box; }
button {
text-align: center;
width: 400px;
}
div {
margin-top: 20px;
background: #000;
color: #fff;
}
.a,
.b {
overflow: hidden;
height: 0;
opacity: 0;
transition: opacity 3s;
}
.a.show {
height: auto;
opacity: 1;
}
.b.show {
height: 100px;
opacity: 1;
}
.c,
.d {
display: none;
}
.c.show,
.d.show {
display: block;
}
.c div {
opacity: 0;
transition: 3s all;
}
.c.show div {
opacity: 1;
}
.d div {
opacity: 0;
}
.d.show div {
animation: fade 3s;
}
#keyframes fade {
from { opacity: 0; }
to { opacity: 1; }
}
.e div {
display: none;
}
.e.show div {
display: block;
animation: fade 3s;
}
.f {
background: transparent;
}
.f div {
background: #000;
display: none;
}
.f.show div {
display: block;
animation: fade 3s;
}
.g {
display: none;
}
<button id="a">A: Box Height: Auto</button>
<div class="a">This<br/>Has<br/>Some Strange<br/><br/>Content<br>But<br>That doesn't really<br>Matter<br/>Because shown,<br/>I'll be<br/>AUTO</div>
<button id="b">B: Box Height: 100px</button>
<div class="b">Content For 2</div>
<button id="c">C: Hidden - Child Transitions (bad)</button>
<div class="c"><div>Content<br/>For<br/>3<br/></div></div>
<div style="clear: both;"></div>
<button id="d">D: Hidden - Child Animates (Better)</button>
<div class="d"><div>Content<br/>For<br/>4<br/></div></div>
<div style="clear: both;"></div>
<button id="e">E: Hidden - Child Hidden & Animates</button>
<div class="e"><div>Content<br/>For<br/>5<br/></div></div>
<button id="f">F: Child Has BG & Animates (Works)</button>
<div class="f"><div>Content<br/>For<br/>5<br/></div></div>
<div style="clear: both;"></div>
<button id="g">G: This uses fadeToggle to avoid this</button>
<div class="g">I animate with<br/>JavaScript</div>
<footer>I'm just the footer to show the bottom of the document.</footer>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Re-closing hidden content

I have a large document where this is implemented A LOT. I am hoping there is a way to simply edit the JavaScript somehow, so I have less editing.
Basically, clicking on a line of text opens the hidden text beneath it. You can close and re-hide the text by clicking on that same line of text... THAT is the ONLY way I want it to operate. As it is now, you can click on the hidden text anywhere and that will also close it. That is becoming a problem because I have interactive content in the hidden text area, and an accidental click in the wrong area will collapse it all.
.results_container {
cursor: pointer;
line-height: 21px;
}
.hidden>span {
display: none;
}
.visible>span {
cursor: default;
display: block;
line-height: 18px;
background: #f5f5f5;
padding: 15px;
margin: 10px 0px 32px 25px;
}
<div class="results_container">
Click Me to show hidden content
<span>I am hidden in span tags. You can close me by clicking anywhere in this text, however, I ONLY want to close the same way I opened; by clicking "Click Me to show hidden content.</span>
</div>
Full Fiddle
NOTE: On the fiddle, my JavaScript is at the end, under the pasted-in jQuery... sorry, that's the only way I could get it to work.
See the fiddle or below snippet:
https://jsfiddle.net/ejbdb128/6/
By checking against "this" in regards to the parent selector, you can filter out when you click on the child "span" element. I should note a caveat to this is if you click anywhere outside the "span" and in the div element, it will hide the span, even if you don't click just on the "Click Me" text..
/* SCRIPT for HIDDEN DESCRIPTIONS for RESULTS */
$(document).ready(function() {
"use strict";
$('.results_container').addClass("hidden");
$('.results_container').click(function(e) {
if (e.target != this) {
return false;
}
var $this = $(this);
if ($this.hasClass("hidden")) {
$(this).removeClass("hidden").addClass("visible");
} else {
$(this).removeClass("visible").addClass("hidden");
}
});
});
.results_container {
cursor: pointer;
line-height: 21px;
}
.hidden>span {
display: none;
}
.visible>span {
cursor: default;
display: block;
line-height: 18px;
background: #f5f5f5;
padding: 15px;
margin: 10px 0px 32px 25px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="results_container">
Click Me to show hidden content
<span>I am hidden in span tags. You can close me by clicking anywhere in this text, however, I ONLY want to close the same way I opened; by clicking "Click Me to show hidden content.</span>
</div>
Add the click handler to to an external event and use that to hide . By the way, jQuery has built in functions hide and toggle for hiding elements.
HTML:
<div class="results_container">
<span class="clickme">Click Me to show hidden content</span>
<span class="hideme">
I am hidden in span tags. You can close me by clicking anywhere in this text, however, I ONLY want to close the same way I opened; by clicking "Click Me to show hidden content.
</span>
Javscript:
$(document).ready(function(){
"use strict";
$('.hideme').hide();
$('.clickme').on('click', function() {
$('.hideme').toggle();
});
});
Fiddle here: https://jsfiddle.net/fLj6c4q7/

Jquery animation works initially but when trying to undo the animation, it keeps bouncing back

I am using jquery to animate a div but when I try to click on the icon to close it, the animation executes for the duration but then reverts back.
html:
<div class="flex-container">
<div class="flex-items"><i class="fa fa-reply fa-2x"></i></div>
</div>
css:
.flex-container{
display: flex;
width: 80vw;
margin: 0 auto;
padding-top: 200px;
flex-flow: row wrap;
justify-content: center;
}
.flex-items{
width: 300px;
height: 300px;
border: 1px solid black;
}
i{
visibility: hidden;
float: right;
}
jquery:
$(document).ready(function(){
$(".flex-items").click(function(){
$(this)
.animate({
width: '+=800px',
height: '+=400px'},{
duration: 300,
ease: "linear"})
.css("order", "1");
i = $(this).children();
$(i).css("visibility" ,"initial");
currentFlexItem = $(this);
});
$("i").click(function(){
$(".flex-items").animate({
width: '300px',
height: '300px'},{
duration: 300,
ease: "linear"});
}).css("visibility", "hidden");
});
The idea I had in the jquery code was first, detecting whether the square (.flex-items) gets clicked on. Once the square is clicked on I animate the width and height (the order doesn't appear to have affect on the issue). I then have it detect if the i tag is clicked, once clicked, the idea was to "undo" the changes I initially made.
Is there a simpler way of doing this? I'm fairly new to jquery.
Codepen
The problem is that the click event is propagating to the parent container. Just take the event, and stop it propagating. This can be done with the plain javascript call to Event.stopPropagation().
$("i").click(function(evt){
evt.stopPropagation();
$(".flex-items").animate({
width: '300px',
height: '300px'},{
duration: 300,
ease: "linear"});
$("i").css("visibility", "hidden");
});
Also, note that chaining the css method after the click method probably isn't what you want. You should put the css call inside the event callback.
The reason why it is bouncing back is because the "i" tag is inside the "flex-items" div since there is open animation is triggered on click of flex-items it will open fine but when you click on the "i" tag which is for closing close animation it is working later the flex item event is triggered again since your "i" tag is inside flex-items so move the
<i class="fa fa-reply fa-2x"></i>
out of the flex items div it will work fine. and makes sure you show the i tag using a class or id reference your current code reference is
i = $(this).children();
$(i).css("visibility" ,"initial");
this should be taken care of when you place the <i> tag outside.

Categories