I'm new to jquery and javascript. I have a website with multiple unique modal/popups that are triggered via a CSS :target selector, which sets them to display: block. I'm trying to use jquery to hide a separate element among other things.
The logic is:
If a modal is visible, then hide element. Else show element. I'm currently using popstate in my jquery. This is because the modals can close if the user presses their browser back button. So I don't want to use any click functions. Everything seems to be working fine, except the if/else statements that detect the visibility of the modals seem to behave differently between Firefox and Chrome? When it hides the element in Firefox, it shows it Chrome. When it hides it in Chrome, it shows it in Firefox. Why the opposite behavior? What am I doing wrong?
$(window).on('popstate', function(event) {
if ($('.modal').is(':visible')) {
console.log("Modal ON");
$('#wrapper').css('overflow-y', 'hidden');
$('#extra_element').css('display', 'none');
} else {
console.log("Modal OFF");
$('#wrapper').css('overflow-y', 'scroll');
$('#extra_element').css('display', 'block');
}
});
.modal {
display: none;
width: 100px;
height: 100px;
background-color: red;
margin: 0 auto;
}
.modal:target {
display: block;
}
#extra_element {
width: 100vw;
height: 20vh;
background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<div id="extra_element">This element should hide when modal is open</div>
<div>
<a href="#content">Click Me To Open Modal</a>
<div id="content" >I'm a Modal. Press browser back button to close
</div>
</div>
Alternate jQuery:
Setting the length to 1 works in Firefox and works oppositely in Chrome.
Setting the length to 0 works in Chrome and works oppositely in Firefox.
$(window).on('popstate', function(event) {
if ( $('.modal-overlay:visible').length === 1 ) {
console.log("Modal ON");
$('#wrapper').css('overflow-y', 'hidden');
$('#extra_element').css('display', 'none');
}
else {
console.log("Modal OFF");
$('#wrapper').css('overflow-y', 'scroll');
$('#extra_element').css('display', 'block');
}
});
Is there another way to do this correctly?
CodePudding user response:
This would be a Chrome bug, according to the specs, at the step 10 of the History traversal algorithm the UA should call the "scroll to fragment" algorithm which is responsible for updating the document's target element (:target) and only at the step 18.1 it should fire the popstate event.
Chrome does fire the event before it updates the document's target element, and thus your CSS :target selector doesn't match yet.
I did open an issue so they get this in-line with the standards, but for the time being you can workaround that by waiting just a task after the event fired:
$(window).on('popstate', async function(event) {
await new Promise(res => setTimeout(() => res()));
if ($('.modal').is(':visible')) {
console.log("Modal ON");
$('#wrapper').css('overflow-y', 'hidden');
$('#extra_element').css('display', 'none');
} else {
console.log("Modal OFF");
$('#wrapper').css('overflow-y', 'scroll');
$('#extra_element').css('display', 'block');
}
});
.modal {
display: none;
width: 100px;
height: 100px;
background-color: red;
margin: 0 auto;
}
.modal:target {
display: block;
}
#extra_element {
width: 100vw;
height: 20vh;
background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<div id="extra_element">This element should hide when modal is open</div>
<div>
<a href="#content">Click Me To Open Modal</a>
<div id="content" >Press browser back button to close
</div>
</div>
