Popup - Event propagation issue - popup not closing when clicking outside of DIV - javascript

I know my issue is event propagation related, but I can't manage to figure it out.
The code I attached is a loop on the page with a couple of items.
function popItUp() {
document.querySelector(".popuptext").classList.add("show");
document.querySelector("body").style.overflow = "hidden";
}
function popItDown() {
document.querySelector(".popuptext.show").classList.remove("show");
document.querySelector("body").style.overflow = "visible";
}
function cancelBubble(e) {
var evt = e ? e : window.event;
if (evt.stopPropagation) evt.stopPropagation();
if (evt.cancelBubble != null) evt.cancelBubble = true;
}
// define all popup elements.
let popups = document.querySelectorAll(".popup");
let popuptext = document.querySelector(".popuptext");
// add listener to each popup element, which binds handler function to click event.
popups.forEach((popup) => popup.addEventListener("click", popItUp));
.popup .popuptext {
visibility: hidden;
width: 160px;
background-color: #555;
color: #fff;
text-align: center;
border-radius: 20px;
padding: 8px 0;
z-index: 1;
}
.popup .popuptext.show::before {
content: "";
position: fixed;
top: -10%;
left: 0%;
width: 100%;
height: 200%;
background-color: rgba(0, 0, 0, 0.4);
/* Black w/opacity/see-through */
z-index: -50;
border-color: #555 transparent transparent transparent;
background-position: center;
background-repeat: no-repeat;
background-size: cover;
}
.popup .popuptext::after {
content: "";
position: fixed;
}
.popuptext.show {
visibility: visible;
-webkit-animation: fadeIn 1s;
animation: fadeIn 1s;
position: fixed;
top: 15%;
left: 11%;
justify-content: center;
display: block;
width: 75%;
height: 80%;
overflow-y: auto;
overflow-x: auto;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div class="popup popup--maxwidth" onclick="popItUp()">
CLICK ME FOR POPUP
<div class="popup--container" onclick="cancelBubble()">
<span class="popuptext">
<span class="popup--close" onclick="popItDown()"><i class="fa fa-close" style="font-size:36px; color:white;">CLOSEBUTTON</i></span>
<div class="partner-container">
Awesome Content
</div>
</div>
</div>
</div>
So this kind of works. Clicking on popItUp() opens up the popup, clicking on popItUp() closes it up.
Issue 1: ISSUE FIXED
However, it is quite unseemly, as every time I open it up, I get an error of:
Popup.js?ver=1.0:4 Uncaught TypeError: this.querySelector is not a function
at popItUp (Popup.js?ver=1.0:4)
at HTMLDivElement.onclick (hu:663)
Issue 2:
It is a clients request, that if we click outside of the popups container (so the target of the click in this case would be .popup--maxwidth, as it covers the whole body), then the popup should close up as well. Now the problem is, that if I take out cancelBubble(e) this sort of works, but it also closes up the popup if i click anywhere inside the popup, which is not really helpful. :)
If someone can help me out it would be greatly appreciated.
Issue 1: ISSUE FIXED -- was a typo in popItUp(), used this.querySelector instead of document. Code updated.

Related

How to disable background scroll on pop-up?

I'm new to the web development world and wanted to know if there is a way to disable background scrolling.
I've tried z-index for the pop-up to display above all the elements, but some background content was getting overlapped with the pop-up.
I'm not much familiar with JS but was not able to get any help.
Below please find my code
body {
height: 200vh;
}
.bg-noscroll {
}
.overlay {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.7);
transition: opacity 500ms;
visibility: hidden;
opacity: 0;
}
.overlay:target {
visibility: visible;
opacity: 1;
}
.popup {
transform: translateY(-60px);
margin: 70px auto;
padding: 20px;
background: #fff;
border-radius: 5px;
width: 30%;
position: relative;
transition: all 5s ease-in-out;
}
.popup .close {
position: absolute;
top: 20px;
right: 30px;
transition: all 200ms;
font-size: 30px;
font-weight: bold;
text-decoration: none;
color: #333;
}
.content {
height: 250px;
}
.popup .content {
overflow-y: scroll;
}
#media screen and (max-width: 700px){
.popup{
width: 70%;
}
<body class="bg-noscroll bg-scroll">
<span><a class="popupBG-Disable" href="#popup">Full Recipe</a></span>
<div id="popup" class="overlay">
<div class="popup">
<h3>Foxtail Millet Porridge:</h3>
<a class="close" href="#">×</a>
<div class="content">
<span>Ingredients:<br>here are some things that you'd use to make this<br> isn't this amazing?<br>Yes, it is!<br>
this is getting loooooong<br>this will take me a while!<br>oh... yes it will<br>we're getting close<br>and we should be there <br>or not...<br>Im losing hope<br>and patience<br>with how long this is taking<br>I could really cry<br>
but we'll get there soon<br>safe and sound<br>free as pie<br>I dont know what I meant by that<br>
this is taking long mannnn<br>
</span>
Thank you for your help!
I have a live codepen with your original code so you can just copy and paste if you wish.
Using Jquery, we can enable and disable overflow using some simple code:
const modal = document.querySelector("#btn");
const body = document.querySelector("body");
const showModal = function (e) {
modal.classList.toggle("hidden");
if (!modal.classList.contains("hidden")) {
body.style.overflow = "hidden";
} else {
body.style.overflow = "hidden";
}
}; // just reversed for re-enabling scroll, as seen in the codepen
Currently, you have to make use of javascript and add or remove the scrollbar-properties or css-class using a hashchange event-listener for example:
window.addEventListener("hashchange", event => {
const newHash = new URL(event.newURL).hash,
el = document.getElementById(newHash.substr(1));
if (el && el.classList && el.classList.contains("overlay")) {
document.body.style.overflow = "hidden";
// or document.body.classList.add("bg-noscroll");
} else {
document.body.style.overflow = "";
// or document.body.classList.remove("bg-noscroll");
}
});
Starting from chromium 101 the support for the :has()-selector has been implemented (experimental flag only) and the current chromium 105 dev channel brings the :has()-selector enabled by default.
With the has()-selector it will be possible using:
body:has(.overlay:target) {
overflow: hidden;
}
Keep also mind, it may take some more time for other browsers to implement the has()-selector. Therefor the best would be to stick with the javascript method for a while.

Modal issue with html button updated by javascript

I am using the following javascript code to update my HTML.
document.querySelector(".long-copy").innerHTML = "Once, there was a boy who became bored when he watched over the village sheep <a class=\"word-button\">grazing</a> on the hillside.";
This updates my HTML DOM (this one) dynamically.
<div>
<p class="long-copy"></p>
</div>;
Now, in my code - the word "grazing" is a tag. When someone clicks on this "grazing" button, a modal window should open. BUT this is not working as when I run this javascript code, I get an error "Cannot read property 'addEventListener' of null". Please help.
const btn1OpenModal = document.querySelector(".word-button");
btn1OpenModal.addEventListener("click", openModal);
const openModal = function () {
modal.classList.remove("hidden");
overlay.classList.remove("hidden");
};
CSS added >>
.hidden {
display: none;
}
.word-modal-one {
position: absolute;
top: 56%;
left: 50%;
transform: translate(-50%, -50%);
width: 80%;
height: 80%;
background-color: white;
padding: 6rem;
border-radius: 10px;
box-shadow: 0 3rem 5rem rgba(0, 0, 0, 0.3);
z-index: 10;
overflow: auto;
}
.word-modal-one::-webkit-scrollbar {
display: none;
}
.overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.3);
backdrop-filter: blur(1px);
z-index: 5;
}
Instead change your <a> tag HTML to this:
var myHTML = "text text more text <a href='javascript:void(0);' onclick='openModal()'>My a element</a> more text text more text";
function openModal() {
//Open that modal
}
I added the javascript:void(0) because without that, some browsers like to use the <a> tag to redirect, because it does signify a link, and that void stops the link from executing a redirect to the same page and lets you carry on with your JS on the same page
The reason why your addEventListener fails is because the script looks for an element that doesn't exist yet, and so you can bypass that by attaching the event listener yourself onto the element

JQuery click event not working properly with an <i> element

<button type="button" class="add-to-cart"><i class="material-icons">add_shopping_cart</i>cumpara</button>
<button class="added add-to-cart"><i class="material-icons check">check</i><i class="material-icons clear">clear</i>Adaugat in cos</button>
I have these two buttons with this CSS code:
.add-to-cart
{
position: relative;
overflow: hidden;
line-height: em(48);
background: complement($rodie);
border: none;
color: $gray-100;
text-transform: uppercase;
height: em(48);
width: 100%;
font-size: em(18);
display: inline-block;
transition: all 250ms ease-out;
&.clicked
{
transform: translateX(-100%);
}
&:hover
{
background: complement(darken($rodie, 10%));
}
i
{
position: absolute;
right: 0;
top: 0;
font-size: em(18);
height: em(48);
width: em(48);
line-height: em(44);
}
}
.added
{
position: absolute;
right: -100%;
top: 90%;
z-index: 22;
background: $verde-jungla;
&:hover
{
background: $verde-jungla;
}
&.clicked
{
transform: translateX(-100%);
}
.check
{
left: 0;
}
}
.clear
{
transition: all 100ms ease-in-out;
height: em(48);
width: em(48);
right: 0;
background: desaturate(red, 30%);
&:hover
{
background: desaturate(darken(red, 10%), 30%);
}
}
I want the button to respond to a click event by transitioning the second button, which has an icon and a message (that informs the user that the product has been added to the cart) attached to it. The first transition works. When I click on the button, the other one appears as it's supposed to, but when the clear "button" (the <i> with the class of clear) is pressed, it's not working.
This is the JQuery code:
$('.add-to-cart').click(function(){
$('.add-to-cart').addClass("clicked");
});
$('.clear').click(function(){
event.preventDefault();
$('.add-to-cart').removeClass("clicked");
});
Keep in mind that if I change the selected element of the second click event, the process works just fine.
Having the .clear button inside an .add-to-cart is asking for problems.
When you click .clear, at the same time you click .add-to-cart.
You did add event.preventDefault, but you don't just want to prevent the default. You also need to prevent the event from "bubbling" up.
Also, the variable event does not exist, you need to add it as the name of the first argument.
Try:
$('.clear').click(function(event){
event.stopPropagation();// Stop bubbling up
event.preventDefault();
$('.add-to-cart').removeClass("clicked");
});
But a far better solution would be to move .clear outside of the button that has .add-to-car.
<button type="button" class="add-to-cart"><i class="material-icons">add_shopping_cart</i>cumpara</button>
<button class="added add-to-cart"><i class="material-icons check">check</i><i class="material-icons clear" style=" padding: 0 10px;">clear</i>Adaugat in cos</button>
$('.add-to-cart').click(function(){
$('.add-to-cart').addClass("clicked");
});
$('.clear').click(function(event){
event.stopPropagation();
event.preventDefault();
$('.add-to-cart').removeClass("clicked");
});

Elements with fixed position moving when body overflow is hidden

I want to open a modal layer which overtakes the body scroll. To accomplish that, when the layer is shown I'm setting the body overflow to hidden and the overflow to scroll on the modal layer. Visually, one scrollbar replaces the other.
In the background I have a top bar with fixed position and 100% wide. What happens is when the body overflow is set to hidden, the 100% width div (top bar) takes the scrollbar space and its elements move to the right.
How can I prevent those elements from moving?
I tried to calculate (javascript) the width of the scrollbar and when setting the body overflow: hidden, give a margin-right: "scrollbar width" to the top bar. That didn't work.
Also tried a dummy div at the right end of the top bar with overflow set to scroll and force it to display a scroll bar when the layer is opened. The idea was to take the space of the missing scrollbar with another scrollbar, only on the top container. That almost worked but created a 1 or 2px flickering. Not good enough.
jsFiddle here with the basic problem
var body = $('body'),
main = $('.main'),
open_modal = $('.open-modal'),
close_modal = $('.close-modal'),
modal_container = $('.modal-container'),
toggleModal = function() {
body.toggleClass('body-locked');
modal_container.toggleClass('dp-block');
};
open_modal.on('click', toggleModal);
close_modal.on('click', toggleModal);
Basically...
When the modal is opened, set the menu width to it's current width and set a window.onresize event handler which will resize the menu to the body's width.
When the modal is closed, remove the fixed width and the window.onresize handler and return the menu to it's initial state.
In the spirit of less === more I've taken the liberty of simplifying your code as much as I can.
var body = $('body');
var menu = $('#topBarFixed');
function toggleModal() {
menu.css('width', body.hasClass('locked') ? '' : menu.width());
window.onresize = body.hasClass('locked') ? '' : function () {
menu.css('width', body.width());
}
body.toggleClass('locked');
}
body.on('click', '.open-modal, .close-modal', toggleModal);
body {
padding-top: 40px;
height: 1000px;
background: lightblue;
}
body.locked {
height: 100%;
overflow: hidden;
}
.modal-container {
display: none;
overflow-y: scroll;
position: fixed;
top: 0; right: 0;
height: 100%; width: 100%;
background-color: rgba(255, 255, 255, 0.3);
z-index: 400;
}
body.locked .modal-container {
display: block !important;
}
.modal {
height: 600px;
width: 200px;
margin: 50px auto;
background: indianred;
}
#topBarFixed {
width: 100%;
background-color: lightgray;
position: fixed;
top: 0;
left: 0;
text-align:center;
display: inline-block;
z-index: 200;
}
.topBarContent {
display: inline-flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: space-between;
align-items: center;
}
.inner1 {
width:30px;
line-height: 40px;
}
.open-modal {
position: relative;
top: 400px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="topBarFixed">
<div class="topBarContent">
<div id="inner" class="inner1">div</div>
<div id="inner" class="inner1">div</div>
<div id="inner" class="inner1">div</div>
<div id="inner" class="inner1">div</div>
<div id="inner" class="inner1">div</div>
</div>
</div>
<p>Scroll down to open layer</p>
<button class="open-modal">Open layer</button>
<div class="modal-container">
<div class="modal">
<button class="close-modal">Close layer</button>
</div>
</div>
Your problem here is that topBarFixed has a 100% width. If this width was fixed you would not have this problem. The following has been tested on Chrome and Firefox:
Add this line to your toggleModal function's first line:
$(".topBarFixed").width($(".topBarFixed").width());
That will set the width to the actual width (in pixels) of the bar at that point. Then when you close the layer, set it back to 100%.
close_modal.on('click', function() { toggleModal(); $(".topBarFixed").width("100%"); });
The entire code looks like:
var body = $('body'),
main = $('.main'),
open_modal = $('.open-modal'),
close_modal = $('.close-modal'),
modal_container = $('.modal-container'),
toggleModal = function() {
$(".topBarFixed").width($(".topBarFixed").width());
body.toggleClass('body-locked');
modal_container.toggleClass('dp-block');
};
open_modal.on('click', toggleModal);
close_modal.on('click', function() { toggleModal(); $(".topBarFixed").width("100%"); });
And here is the jsFiddle: http://jsfiddle.net/wmk05t0b/5/
Edit
Optionally, you could just come up with a fixed width, and that will do the trick:
.topBarFixed
{
width:715px; /*changed from 100%*/
height: 40px;
background-color: lightgray;
position: fixed;
top: 0;
left: 0;
text-align:center;
display: inline-block;
z-index: 200;
}
Some errors in your code: id is only one. Use classes if you want to apply the same style to more elements.
<div class="topBarContent">
<div class="inner1">div</div>
<div class="inner1">div</div>
<div class="inner1">div</div>
<div class="inner1">div</div>
<div class="inner1">div</div>
</div>
Anyways, that's not what caused your problem. First of all, your body's overflow should be enough: don't add an overflowY to your .modal-container unless you want to prevent the background page from scrolling while modal is open. Second, fix the modal itself, and center it using the centered CSS trick (left:50%, margin-left:-half-of-your-width).
CSS:
.body-locked {
overflow:scroll;
}
.modal-container {
overflow:hidden;
position:fixed;
display: none;
top: 0; right: 0;
height: 100%; width: 100%;
background-color: rgba(255, 255, 255, 0.3);
z-index: 400;
}
.modal {
position: fixed;
height: 600px;
width: 200px;
margin: 50px auto 50px -100px;
background: indianred;
left:50%;
}
/*Reset your body, you never know*/
body {
margin:0;
padding:0
}
Hope it helps.

IE requires double click with custom button

I have a script that is dived as:
HTML:
<div id="wrapper">
<div id="container">
<div id="button">Click me!</div>
<form>
<input type="file" />
</form>
</div>
<div id="notice">File is uploaded!</div>
</div>
JavaScript(JQuery 2):
$(document).ready(function () {
$("input").on("change", function () {
$("div#notice").fadeIn();
//$("form").submit(); //If you want it to submit on your site uncomment this
});
});
CSS:
div#wrapper {
background-color: #ccc;
position: absolute;
width: 300px;
height: 200px;
}
div#wrapper > form > input {
color: rgba(0, 0, 0, 0);
zoom: 1;
filter: alpha(opacity=0);
opacity: 0;
color: rgba(0, 0, 0, 0);
}
div#container {
width: 200px;
height: 20px;
overflow: hidden;
}
div#button, input {
position: absolute;
top: 0px;
left: 0px;
cursor: pointer;
}
div#button {
z-index: 1;
background-color: #AAA;
}
input {
z-index: 2;
background-color: rgba(0, 0, 0, 0);
opacity: 0;
alpha: filter(opacity=0);
font-size: 25px;
color: rgba(0,0,0,0);
filter: alpha(opacity=0);
zoom: 1;
}
div#notice
{
background-color: green;
display: none;
position: absolute;
bottom: 0px;
left: 0px;
}
Note: This issue was there before blur was put to hide the flashing icon in IE.
In Chrome and Firefox the button only requires a single click. In IE 10 it requires a double click, which I don't want. I am trying to think of a way to make it single click.
The only thing I've tried so far is to .render("click") on the input, but that didn't work.
JSFiddle: http://jsfiddle.net/plowdawg/mk77W/
I had the same problem and found different approach. I just made that button be as big as I need with font-size on it. Then person simply can't click on text section.
<div class="divFileUpload">
<input class="fileUpload" type="file" />
</div>
and css:
.divFileUpload {
background-color: #F60;
border-radius: 5px;
height: 50px;
margin-left: auto;
margin-right: auto;
overflow: hidden;
position: relative;
width: 50%
}
.fileUpload {
cursor: pointer;
font-size: 10000px; /* This is the main part. */
height: 100%;
opacity: 0;
position: absolute;
right: 0;
top: 0;
width: 100%
}
To follow up on what SDLion said....
This might be what you see
But really on top of that there is a file upload control that has been made transparent.
Clicking on the browse button brings up the file upload dialog with one click.
In IE You have to double click the text box to the left of it if you want to see the file upload dialog.
Increase the font size of the file input to fill the button image
While #bastos.sergio is right about it happening in the text section there is a way to get around this if you are comfortable using JavaScript.
You will need:
A wrapper div tag
An inner dev tag
Some sort of form input
JQuery (tested on 2.1)
Steps:
Create the "wrapper" div
Create an inner "button " div
Place the form element underneath the inner "button" div
Set the "wrapper" and "inner" divs to the same size
Set overflow:hidden on the wrapper
Create a JQuery script for the "inner" div setting the on click function
In the "inner" function click function call .click() on the input
Seems to work for me in IE 10.
$(document).ready(
function()
{
$("#open_dialog").on("click",function()
{
$("input").click();
});
$("input").on("change",function()
{
alert($("input"));
$("#notice").html("uploading");
});
});
#open_dialog
{
position: relative;
width: 200px;
height: 50px;
color: white;
font-family: "Arial";
font-size: 14pt;
text-align: center;
top: 25px;
margin-top: -.5em;
z-index: 1;
}
#wrapper
{
width: 200px;
height: 50px;
overflow: hidden;
cursor: pointer;
border-radius: 10px;
background: green;
z-index: 0;
}
input
{
margin-top: 100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="wrapper">
<div id="open_dialog">Click Me</div>
<input type="file" />
</div>
<div id="notice">Nothing to upload</div>
The double click is happening on the text portion of the file upload, like #TravisPessetto stated.
Since it's not possible to hide/remove the text portion out of the file input control, I recommend that you put a regular button over the file input.
See here for more details.
I found another more simple solution, just trigger the event "click" on mousedown for this element only:
$("input").mousedown(function() {
$(this).trigger('click');
})
in order to avoid problems on other browsers, apply this solution to IE only:
if ($.browser.msie && parseInt($.browser.version, 10) > 8) {
$("#your_file_input").mousedown(function(event) {
if (event.which == 1) {
$(this).trigger('click');
}
})
}
here's your jfiddle modified, check it on IE 9-10:
http://jsfiddle.net/7Lq3k/
Edit: example modified in order to limit the event handling for left click only
(see: How to distinguish between left and right mouse click with jQuery for details)
I mixed various solutions to get this one that works for me (on every browser). It's written using LESS nesting.
HTML
<!--/* Upload input */-->
<div class="input-file">
Select image
<input type="file" />
</div>
LESS CSS
/*
* Input "file" type Styling
* Based on http://goo.gl/07sCBA
* and http://stackoverflow.com/a/21092148/1252920
*/
.input-file {
position: relative;
overflow: hidden;
margin: 10px;
input[type="file"] {
opacity: 0;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: 0;
padding: 0;
cursor: pointer;
width: 100%;
height: 100%;
font-size: 10000px;
}
// For Chrome
input[type=file]::-webkit-file-upload-button {
cursor: pointer;
}
}

Categories