I am trying to create a pop-up where the container is just a black background with some opacity, and then that div container contains the content of the popup.
Basically it looks something like:
<button (click)="showPopup = !showPopup">Open popup</button>
<div class="overlay-bg" (click)="showPopup = !showPopup" [ngClass]="showPopup ? 'is-active' : ''">
<div class="content">Some content</div>
</div>
The CSS looks something like:
.overlay {
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.5);
display: grid;
position: fixed;
pointer-events: none;
opacity: 0;
&.is-active {
opacity: 1;
pointer-events: all;
}
.content {
width: 400px;
height: 400px;
place-self: center;
background: red;
}
}
So basically, when the state is not active, it's not showing, and when is-active is enabled by clicking the button, the overlay + content is displayed. Now, what I would also like is that when clicking the background, the pop-up should close - which is does. However, the problem is, that the pop-up also closes when I just click the content - which it shouldn't since it should be somewhat interactive.
So how do I prevent the pop-up from closing when interacting with the content div ?
Problem
The reason this happens is due to the event flow in javascript, which is the order in which events are handled. This is important when there are nested elements, like with your two divs ('overlay-bg' and 'content'). Modern browsers use event bubbling which means that the innermost child element handles the event first. The event then 'bubbles' and works its way outwards. In your case, the 'content' div must handle the event first. You do not want the pop-up to disappear when the 'content' div is clicked, but you do when the 'overlay-bg' div is clicked. Therefore you must find a way of stopping the event bubbling from occurring by capturing it with the onclick event on the 'content' div. This can be done by using the stopPropagation() method of the event object.
Solution
To stop the event from bubbling upwards when the user clicks on the 'content' div, add an onclick event handler in the HTML:
<div class="content" onclick="preventBubbling(event)">Some content</div>
Then, with JavaScript, you can use the stopPropagation() method of the event object which is passed as a parameter to the function. To achieve this, the function would look like this:
function preventBubbling(event) {
event.stopPropagation();
}
This stops the event from bubbling up to the parent div, so the click event is never triggered, and the pop-up does not hide (when the user clicks on the content div).
Related
I have an embedded iframe in my website that goes to a form on another (third-party) website. What I'd like to do is to run a function every time a user clicks into the iframe while also not preventing the user from using the iframe itself.
Because iframes don't seem to have click handlers, I decided to attempt to overlay a transparent div on top with a click handler while also simultaneously passing clicks through to the iframe underneath.
Here is a Fiddle: https://jsfiddle.net/r3u04fa9/1/
const el = document.getElementById("wrapper")
if (el) {
el.addEventListener('click', () => {
console.log("CLICKED")
})
}
#wrapper {
//background-color: firebrick;
position: absolute;
z-index: 1;
width: 800px;
height: 500px;
pointer-events: none; /* If this is not here, the clicks happen but can't interact with form */
}
<div id="wrapper"></div>
<iframe src="https://www.zillow.com" width=800 height=500></iframe>
With the addition of the pointer-events style, the clicks are able to pass through to the underlying iframe but the click events don't happen. Is there a way for me to achieve both?
I'm trying to build a button that looks like Zoom's button.
Here there is a button to pick a device inside the camera button. I'd like to create something similar, where you have a button and another button that expands a picker inside it.
How can you create this in an accessible way?
If I nest buttons in React, it throws errors that you can't nest a button inside another. Zoom's equivalent would be:
<button>
Stop Video
<button>Pick Device</button>
</button>
which doesn't work. How would you create an interface like this so it stays accessible (and valid)?
Preword
Don't nest interactive elements
There is a reason that it isn't valid HTML to nest buttons or hyperlinks, it causes nightmares for knowing which action should be performed on a click (for a start) and for assistive technology this makes things even worse as it can confuse the accessibility tree as to what it should present to screen readers.
The answer
If you look carefully you will see they aren't actually nested, the "picker" button is placed on top of the other button.
Now there is an issue here in terms of accessibility, click / tap target size.
A button / interactive element should be no less than 44px by 44px
So the Zoom example you gave fails this criteria. Additionally the tooltip that says "stop video" looks wrong if you have the picker selected as that should be the tooltip for the button that is currently hovered.
So how could we create an accessible version of what you want?
I would recommend having a large button with a 44 by 44 button placed on top to the right.
This can easily be done with absolute positioning.
To ensure that it is evident visually that the buttons are related I inset the second button by 2px.
The below is not a complete example but I have given you a start.
I added aria-expanded to the button that opens the sub menu, this gets toggled when the menu is opened.
I also added the aria-haspopup attribute to let users know that this button opens a sub menu.
I also added aria-controls to let assistive technology know the relationship between the button and the menu it opens.
Finally you will see I added a <span> with some visually hidden text inside so that screen reader users know that the picker button opens the video controls.
The example maintains logical tab order and is pretty accessible, but there are still things such as being able navigate the menu buttons with the arrow keys, closing the menu with Esc key and returning focus to the button that opened the menu etc. that you need to implement yourself. Oh and styling obviously!
var mainButton = document.querySelector('.main-button');
var menuToggle = document.querySelector('.sub-button');
var menu = document.getElementById('controls');
mainButton.addEventListener('click', function(){
alert("clicked the main button");
});
menuToggle.addEventListener('click', function(){
if(menu.classList.contains('open')){
menu.classList.remove('open');
menuToggle.setAttribute('aria-expanded', false);
}else{
menu.classList.add('open');
menuToggle.setAttribute('aria-expanded', true);
}
});
.container{
position: relative;
width: 144px;
height: 48px;
}
.main-button{
width: 144px;
height: 48px;
padding-right: 50px;
}
.sub-button{
position: absolute;
width: 44px;
height: 44px;
top:2px;
right:2px;
}
.visually-hidden {
border: 0;
padding: 0;
margin: 0;
position: absolute !important;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px 1px 1px 1px); /* IE6, IE7 - a 0 height clip, off to the bottom right of the visible 1px box */
clip: rect(1px, 1px, 1px, 1px); /*maybe deprecated but we need to support legacy browsers */
clip-path: inset(50%); /*modern browsers, clip-path works inwards from each corner*/
white-space: nowrap; /* added line to stop words getting smushed together (as they go onto seperate lines and some screen readers do not understand line feeds as a space */
}
#controls{
display: none;
}
#controls.open{
display: block;
}
<div class="container">
<button class="main-button">Stop Video</button>
<button class="sub-button" aria-expanded="false" aria-haspopup="true" aria-controls="controls">⌄ <span class="visually-hidden">Pick Device</span></button>
<ul id="controls"
role="menu"
aria-labelledby="sub-button">
<li><button>Option 1</button></li>
<li><button>Option 2</button></li>
</ul>
</div>
The react gives a warning if you try to do so and the reason is simple. It has everything to do with semantic HTML and you should never put a button inside a button.
Alternatively to get the desired behaviour you can do something like this:
<div style={{ position: "relative", width: "200px", height: "40px" }}>
<button
onClick={() => console.log("Stop Video")}
style={{ width: "100%", height: "100%" }}
>
Stop Video
</button>
<button
onClick={() => console.log("Pick Device")}
style={{ position: "absolute", right: 0, top: 0 }}
>
Pick Device
</button>
</div>
This will do the same thing you need. Here is the codesandbox example for the same implementing the exact same thing.
I have an element that overlays another element. The main element is a canvas where elements constantly have mouse interactions and the element directly overtop of it just shows elements that act as little markers. Same position, same size and it's important the overlay is overtop of the canvas.
What would it mean to make this "overlay" only exist visibility wise? As in having no possible user input because for its purposes it's not really there to be interacted with, just showing something.
Removing selection in CSS stops you from clicking on it but it's still overtop of the other element and doesn't allow mouse events. Hiding the element removes its presence but also makes it invisible.
In a normal desktop application you would just draw something to the screen and add functionality if you wanted but with HTML those two things are inherently the same.
I believe adding in the CSS the following code solves your issue:
.no-interaction {
z-index : -5
}
OR
.interaction {
z-index : 5
}
Turns out all it took was setting the pointer-events CSS attribute to none on whatever you want to have no presence.
I figured it would be a little more interesting than that, but there's a built in way in CSS.
<div id="canvas"></div>
<div id="overlay"></div>
#canvas, #overlay {
width: 500px;
height: 500px;
position: absolute;
}
#canvas {
background: blue;
}
#overlay {
background: red;
pointer-events: none; // right here
}
$('#canvas').click(function() {
alert('Clicked');
});
https://jsfiddle.net/ufsy33aw/
I got a button which is inside a div, but I need to get that div on top of this button, in order to be able to use onMouseOut event.
I tried to change z-indexes of those two, though that didn't helped. Any ideas? I can include code for better understanding but I think it's not necessary.
Impossible. Children will always appear above their parent.
You need to seek another approach; disabled elements do not appear to have events. I'd reconsider the UI to circumvent this issue. If you absolutely must have this functionality, perhaps replace the disabled button with another HTML element that can accept mouse over events.
http://jsfiddle.net/4HU72/
HTML
<button>Active Button</button>
<div>Disabled Button</div>
Disable button
<span></span>
CSS
button, div {
width: 200px;
height: 200px;
display: block;
background-color: #336699;
}
div {
display: none;
}
Javascript
$("a").click(function() {
$("button").hide();
$("div").show();
});
$("button, div").mousemove(function(e) {
$("span").html(e.pageX+"|"+e.pageY);
});
I have a page with a left sidebar that I want to be able to toggle on or off based on whether or not the user clicks it. Unfortunately entering JavaScript code on this website has been disabled and I only have access to CSS.
The left sidebar has
its main div (parentBlock)
a div for the show/hide, (toggleBlock)
a div for the logo, (div1)
a div for the navbar, and (div2)
a div for social icons (div2)
When the user clicks on "Show / Hide" I want to:
Hide (display:none) the logo, navbar, and social div's, and
Set the height of the main div to something smaller (say 30px).
Is there any way to do this in CSS?
<div class="parentBlock">
<div class="toggleBlock">Show / Hide</div>
<div class="divBlah">div1</div>
<div class="divBlah">div2</div>
<div class="divBlah">div3</div>
</div>
Then if the user clicks "Show / Hide" again, it will unhide the div's and set the height back to filling the screen.
Is this possible?
I found some code that would work if the "Show / Hide" button was in "parentBlock" but it didn't work if it was within "toggleBlock" (and I have to have the Show/Hide button in toggleBlock)
(http://tympanus.net/codrops/2012/12/17/css-click-events/)
I realize onClick events require JavaScript. Those are not possible since I can't use JavaScript :( Some people try to get around it by using either :active or creating checkboxes and having the checkbox:clicked value load the action ... but it only works with certain relations that I can't seem to nail down.
Unfortunately I cannot alter the ultimate structure of "toggleBlock", div1, div2, and div3 ... only what's in them and their CSS. Also making it even more difficult is that the website randomly generates ID="" each time the page loads so the TARGET method isn't possible. Also, the 3 div's (div1 thru div3) have the same class name. I'm beginning to think it's impossible :(
(For reference, I'm trying to use the tools on the New SmugMug and they're rather restrictive)
Here is a CSS only solution using target
DEMO http://jsfiddle.net/kevinPHPkevin/r4AQd/
.button {
display: block;
width:60px;
background: red;
z-index:1;
}
#element {
display: none;
background:#fff;
margin-top:-20px;
z-index:2;
}
#element:target {
display: block;
}
#show:target {
display: block;
}
#hide {
background: #000;
color: #fff;
}
As Joum has pointed out this is not possible to do via click events but using hover on siblings you might be able to achieve a similar effect. for example try adding this css:
div.toggleBlock { display: block; }
div.toggleBlock ~ div { display: none; }
div.toggleBlock:hover ~ div { display: block; }
for more information see this: http://css-tricks.com/child-and-sibling-selectors/