I have 2 <hr> elements that are animated and move into close to each other. I want the animation to take place once the <hr> elements are in the viewport, instead of occurring right when the page is loaded. I have included my JavaScript code but this code didn't work; not sure if I'm using the right event.
'use strict';
const linebreak = document.querySelector('#hero--one');
const symbolContainer = document.querySelector('.header__container-symbol')
linebreak.addEventListener('scroll', function(e) {
e.preventDefault();
const containerCoords = symbolContainer.getBoundingClientRect();
symbolContainer.scrollIntoView({
behavior: 'smooth'
});
linebreak.classList.remove('hidden');
})
/* Lines and Diamond above title */
.header__container-symbol {
display: flex;
justify-content: center;
align-content: center;
background-color: rgba(25, 25, 25, 1);
}
.span-D {
align-self: center;
}
.hr-symbol {
width: 60rem;
display: inline-block;
height: 0.3rem;
background-color: #eee;
}
#hero--one {
margin-right: 3px;
animation: moveInLeftHr;
animation-duration: 2s;
}
#symbol {
font-size: 3rem;
}
#hero--two {
margin-left: 3px;
animation: moveInRightHr;
animation-duration: 2s;
}
#symbol {
color: #eee;
margin-right: 2rem;
margin-left: 2rem;
}
.hidden {
display: none;
}
/* End Diamon and Lines above title */
/* KeyFrames Animations */
#keyframes moveInLeftHr {
0% {
opacity: 0;
transform: translateX(-100px);
}
100% {
opacity: 1;
transform: translate(0);
}
}
#keyframes moveInRightHr {
0% {
opacity: 0;
transform: translateX(100px);
}
100% {
opacity: 1;
transform: translate(0);
}
}
<div class="header__container-symbol">
<span class="span-D"><hr class="hidden hr-symbol" id="hero--one"/></span>
<span id="symbol"> ⬙ </span>
<span class="span-D"><hr class="hidden hr-symbol" id="hero--two"/></span>
</div>
You can use the Intersection Observer API to detect when the element intersects with the viewport.
The threshold option is set to 1, meaning it will check for when the element is fully within the viewport.
A test div has been inserted at the top of the html to demonstrate the behavior.
'use strict';
const linebreak1 = document.querySelector('#hero--one');
const linebreak2 = document.querySelector('#hero--two');
const symbolContainer = document.querySelector('.header__container-symbol')
const observer = new IntersectionObserver((entries, observer) => {
if (!entries[0].isIntersecting) return;
linebreak1.classList.remove('hidden');
linebreak2.classList.remove('hidden');
}, { threshold: 1 });
observer.observe(symbolContainer);
/* Lines and Diamond above title */
.header__container-symbol {
display: flex;
justify-content: center;
align-content: center;
background-color: rgba(25, 25, 25, 1);
}
.span-D {
align-self: center;
}
.hr-symbol {
width: 60rem;
display: inline-block;
height: 0.3rem;
background-color: #eee;
}
#hero--one {
margin-right: 3px;
animation: moveInLeftHr;
animation-duration: 2s;
}
#symbol {
font-size: 3rem;
}
#hero--two {
margin-left: 3px;
animation: moveInRightHr;
animation-duration: 2s;
}
#symbol {
color: #eee;
margin-right: 2rem;
margin-left: 2rem;
}
.hidden {
display: none;
}
/* End Diamon and Lines above title */
/* KeyFrames Animations */
#keyframes moveInLeftHr {
0% {
opacity: 0;
transform: translateX(-100px);
}
100% {
opacity: 1;
transform: translate(0);
}
}
#keyframes moveInRightHr {
0% {
opacity: 0;
transform: translateX(100px);
}
100% {
opacity: 1;
transform: translate(0);
}
}
<div style="height: 500px;"></div>
<div class="header__container-symbol">
<span class="span-D"><hr class="hidden hr-symbol" id="hero--one"/></span>
<span id="symbol"> ⬙ </span>
<span class="span-D"><hr class="hidden hr-symbol" id="hero--two"/></span>
</div>
Related
I have 0 clue why my form container is not sliding out, it just doesn't move, it slides in fine, but sliding out it just doesn't work. I've tried sliding out the phoneInput itself but it just gets stuck at the end of the container. Assuming you connected everything correctly you'll notice my elements slide in, but when i click the button my intention is for them to slide out but only the heading slides out, the form container doesn't slide out, it doesn't even move.
const params = new URLSearchParams();
// remove number formatting then add +1
function formatPhoneNumber(phoneNumberString) {
var cleaned = ('' + phoneNumberString).replace(/\D/g, '');
var match = cleaned.match(/^(\d{3})(\d{3})(\d{4})$/);
if (match) {
return '+1' + match[1] + match[2] + match[3];
}
return null;
}
// login and send input data to server
function login() {
const input = document.getElementById('phoneInput')
let number = input.value
if (isNaN(parseInt(number)) || parseInt(number) === null) {
console.log('Invalid phone number');
const divText = document.getElementsByClassName('heading-text')[0];
divText.innerText = 'Invalid phone number!';
divText.style.color = 'red';
setTimeout(() => {
divText.style.color = "black";
divText.innerText = 'Please enter a valid phone number! No dashes or spaces!';
}, 3000);
return
} else {
formattedNumber = formatPhoneNumber(number)
if (formattedNumber === null || formattedNumber.length > 12) {
console.log('Invalid phone number');
const divText = document.getElementsByClassName('heading-text')[0];
divText.innerText = 'Invalid phone number!';
divText.style.color = 'red';
setTimeout(() => {
divText.style.color = "black";
divText.innerText = 'Please enter a valid phone number! No dashes or spaces!';
}, 3000);
} else {
let heading = document.getElementsByClassName('heading')[0];
let phoneForm = document.getElementsByClassName('form-container')[0];
heading.classList.add('slide-out');
phoneForm.classList.add('slide-out');
params.delete("number")
params.append('number', formattedNumber)
fetch('http://localhost:3000/login', {
method: 'POST',
body: params
})
.then(function(response) {
return response.json()
})
}
}
}
body,
html {
padding: 0%;
margin: 0%;
font-family: 'Poppins', sans-serif;
}
.heading {
display: flex;
position: relative;
flex-direction: column;
justify-content: flex-end;
align-items: center;
gap: 2em;
height: 60%;
width: 100%;
transition: opacity 0.5s;
transform: translateX(-100%);
-webkit-transform: translateX(-100%);
animation: slide-in 0.5s forwards;
-webkit-animation: slide-in 0.5s forwards;
}
.slide-out {
transform: translateX(100%);
-webkit-transform: translateX(100%);
animation: slide-out 0.5s forwards;
-webkit-animation: slide-out 0.5s forwards;
}
.heading-title {
font-size: 2em;
font-weight: 700;
}
.heading-text {
text-align: center;
}
.form-container {
display: flex;
position: relative;
justify-content: center;
align-items: center;
width: 100%;
padding-top: 1.5em;
transform: translateX(-100%);
-webkit-transform: translateX(-100%);
animation: slide-in 0.5s forwards;
-webkit-animation: slide-in 0.5s forwards;
}
#phoneForm {
display: flex;
flex-direction: column;
width: 50%;
gap: 1rem;
}
#phoneInput {
position: relative;
flex: 1;
width: 50%;
border: solid 2px #d9d9d9;
border-radius: 3px;
align-self: center;
padding: 7px;
height: 100%;
}
#phoneLabel {
padding-inline-start: 10em;
font-weight: bold;
align-self: flex-start;
}
.form-submit {
align-self: center;
background-color: #90E0EF;
border: none;
font-weight: bold;
border-radius: 0.4em;
padding: 1em 0em 1em 0em;
width: 24em;
}
svg {
color: #90E0EF;
}
#phoneInput:focus {
outline: none;
border-color: black;
}
#media screen and (max-width: 820px) {
#phoneInput {
width: 100%;
}
}
#media screen and (min-width: 100px) and (max-width: 595px) {
#phoneForm {
width: 75%;
}
}
#keyframes slide-in {
100% {
transform: translateX(0%);
}
}
#-webkit-keyframes slide-in {
100% {
-webkit-transform: translateX(0%);
}
}
#keyframes slide-out {
0% {
transform: translateX(0%);
}
100% {
transform: translateX(100%);
}
}
#-webkit-keyframes slide-out {
0% {
-webkit-transform: translateX(0%);
}
100% {
-webkit-transform: translateX(100%);
}
}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/css/login.css">
<title>Document</title>
</head>
<body>
<div class="heading">
<div class="heading-image"><svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" fill="currentColor" class="bi bi-shield-lock-fill" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 0c-.69 0-1.843.265-2.928.56-1.11.3-2.229.655-2.887.87a1.54 1.54 0 0 0-1.044 1.262c-.596 4.477.787 7.795 2.465 9.99a11.777 11.777 0 0 0 2.517 2.453c.386.273.744.482 1.048.625.28.132.581.24.829.24s.548-.108.829-.24a7.159 7.159 0 0 0 1.048-.625 11.775 11.775 0 0 0 2.517-2.453c1.678-2.195 3.061-5.513 2.465-9.99a1.541 1.541 0 0 0-1.044-1.263 62.467 62.467 0 0 0-2.887-.87C9.843.266 8.69 0 8 0zm0 5a1.5 1.5 0 0 1 .5 2.915l.385 1.99a.5.5 0 0 1-.491.595h-.788a.5.5 0 0 1-.49-.595l.384-1.99A1.5 1.5 0 0 1 8 5z"/>
</svg></div>
<div class="heading-title">Sign In To PrivDono</div>
<div class="heading-text">Please confirm your phone number. No dashes.</div>
</div>
<div class="form-container">
<form onsubmit="return false" id="phoneForm">
<input type="tel" id="phoneInput" name="phone" required>
<button onclick="login()" class="form-submit">NEXT</button>
</form>
</div>
<script src="/js/login.js" defer></script>
</body>
</html>
```
I'm making my own dropdown with CSS. You hover on it, then some options appear below it, you pick one and that's it. If there are too many options to fit there is a scroll bar. You can see the whole thing here: JSFiddle
let testData = [
"qwertyuiop",
"asdfghjkl",
"zxcvbnm",
"axdxfcbhdhvhv",
"äöäööäöääöää",
"zoinkszoinks",
"brrrrrrrrrr",
"gygygygyasdasda",
];
getTestStuff();
function getTestStuff() {
let content = document.getElementById("content");
let text = document.getElementById("text");
for (let group of testData) {
let div = document.createElement("div");
div.innerHTML = group;
div.addEventListener("click", function() {
text.innerHTML = group; // update button text
}, false);
content.appendChild(div);
}
}
let dropdown = document.getElementById("dropdown");
let arrow = document.getElementById("arrow");
let content = document.getElementById("content");
dropdown.addEventListener("mouseenter", function () {
arrow.className = "open";
content.classList.add("open");
}, false);
dropdown.addEventListener("mouseleave", function () {
arrow.className = "closed";
content.classList.remove("open");
}, false);
.dropdown-wrapper {
height: 40px;
float: left;
margin: 0px 5px;
}
.dropdown-wrapper, .dropdown-content {
width: 300px;
}
.dropdown-wrapper .header {
width: 100%;
height: 100%;
margin: 0;
background-color: #f1f1f1;
text-align: center;
border-radius: 5px;
font-size: 16px;
border: 1px solid grey;
display: table;
}
.dropdown-wrapper .header span {
text-align: center;
vertical-align: middle;
display: table-cell;
width: 100%;
}
.dropdown-wrapper .header div span {
font-size: 20px;
}
.dropdown-wrapper .header div {
margin-left: -40px;
width: 40px;
height: 40px;
float: right;
text-align: center;
display: table;
position: absolute;
}
.dropdown-content {
display: none;
float: none;
max-height: 300px;
overflow-x: hidden;
overflow-y: scroll;
position: absolute;
font-size: 16px;
background-color: #f1f1f1;
min-width: 0px;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 1;
margin-right: auto;
text-align: center;
}
.dropdown-content div {
color: black;
float: none;
padding: 12px 16px;
text-decoration: none;
display: block;
margin-right: -15px;
}
.dropdown-content div:hover {
background-color: #ddd;
}
.dropdown {
height: 100%;
width: 100%;
}
.dropdown:hover .dropdown-content {
display: block;
}
#arrow.open {
-webkit-animation-name: openArrowAnimation;
-webkit-animation-duration: 0.5s;
-webkit-animation-iteration-count: 1;
-webkit-animation-timing-function: ease;
-webkit-animation-fill-mode: forwards;
}
#arrow.closed {
-webkit-animation-name: closeArrowAnimation;
-webkit-animation-duration: 0.5s;
-webkit-animation-iteration-count: 1;
-webkit-animation-timing-function: ease;
-webkit-animation-fill-mode: forwards;
}
#-webkit-keyframes openArrowAnimation {
from {
-webkit-transform: rotate(0deg);
}
to {
-webkit-transform: rotate(180deg);
}
}
#-webkit-keyframes closeArrowAnimation {
from {
-webkit-transform: rotate(180deg);
}
to {
-webkit-transform: rotate(0deg);
}
}
#content.open {
-webkit-animation-name: openDropdownAnimation;
-webkit-animation-duration: 0.5s;
-webkit-animation-iteration-count: 1;
-webkit-animation-timing-function: ease;
-webkit-animation-fill-mode: forwards;
}
#-webkit-keyframes openDropdownAnimation {
from {
max-height: 0px;
}
to {
max-height: 300px;
}
}
<div class="dropdown-wrapper">
<div class="dropdown" id="dropdown">
<div class="header">
<span id="text">Test Thing</span>
<div id="arrow"><span>^</span></div>
</div>
<div id="content" class="dropdown-content">
</div>
</div>
</div>
The Problem:
The scrollbar appears during my animation. Which is not a problem, if it is going to stay, but when the content fits, it disappears again.
When the scroll bar is not there, the space that the scrollbar would take, does not have the right background color, when i hover over the option. Making a very ugly gap.
I know about overflow: auto; but i still use overflow: scroll; with margin-right: -16px; on the children, because my scrollbar moves the children and i only have that scrollbar sometimes.
I need some kind of CSS conditional hack to set overflow to hidden if we don't need it or use JS to change the margin-right but trying to check the parent height after adding the kids gave me this:
content.height: undefined
content.style.height: empty string
So i'm very lost with this.
I got it working with this trick on the children:
margin-right: calc(100% - 300px);
And setting the parent overflow to auto
This works because here 100% is the elements full width. Without a scroll bar, 100% is 300px, so margin is set to 0. With a scroll bar, 100% is around 290px so margin is set to around -10, so the text stays centered.
As #CBroe pointed out, i couldn't get the height of my element, or the children because it had display: none so to get around that:
let content = document.getElementById("content");
content.style.display = "block";
let height = content.offsetHeight;
content.style.display = "";
It's amazing something this dumb works so well. Now i can hide the scrollbar when i don't need it and fix the offset, that the scrollbar creates, when i do need it:
if (height < 300) {
content.style.overflowY = "hidden";
} else {
content.style.overflowY = "scroll";
for (let kid of content.children) {
kid.style.marginRight = "-16px";
}
}
This solution has the added bonus, that it works well with my animation. A problem that the CSS only solution couldn't solve.
How do you achieve VueJS-like control over transitions using vanilla JavaScript?
The goal is to make an element have display: none; when it's hidden, but maintain the ability to fade it in/out.
I managed to make it fade out by applying a class with an opacity of 0 and then listening for the transitionend event to swap the class with a hide class that sets display: none;. That part actually seems to work well...
However, when trying to do the inverse, it seems to fail. Instead of tranitioning in; it ignores the transition entirely and just appears with full opacity and never fires a transitionend event.
const btnShow = document.querySelector('#btnShow');
const btnHide = document.querySelector('#btnHide');
const div = document.querySelector('div');
btnShow.addEventListener('click', () => {
console.log('btnShow clicked');
div.classList.remove('hide');
div.classList.add('div-enter');
div.classList.replace('div-enter', 'div-enter-to');
div.addEventListener('transitionend', function() {
console.log('show transition ended');
div.classList.remove('div-enter-to');
}, { once: true });
});
btnHide.addEventListener('click', () => {
console.log('btnHide clicked');
div.classList.add('div-leave');
div.classList.replace('div-leave', 'div-leave-to');
div.addEventListener('transitionend', () => {
console.log('hide transition ended');
div.classList.replace('div-leave-to', 'hide');
}, { once: true });
});
.hide {
display: none;
opacity: 0;
}
.div-enter {
opacity: 0;
}
.div-enter-to {
opacity: 1;
}
.div-leave {
opacity: 1;
}
.div-leave-to {
opacity: 0;
}
/* Irrelevant styles */
html, body {
height: 100vh;
width: 100vw;
margin: 0;
padding: 1em 1.5em;
font-size: 100%;
}
div {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
margin: 1em 0;
padding: 3rem 0;
width: 50%;
background: #4bfa;
border-radius: 5px;
transition: all 1s linear;
}
button {
border: none;
border-radius: 2px;
}
button:hover {
cursor: pointer;
}
<button id="btnShow">show</button>
<button id="btnHide">hide</button>
<div>
fade out, then set display: none<br />
then do the reverse...
</div>
The hide class should be written like:
.hide {
visibility: hidden;
opacity: 0;
}
CSS transitions and animations allow you to animate a specific set of CSS properties. You cannot animate the display property.
Source: https://www.w3.org/TR/css-transitions-1/#animatable-properties
Use animations instead of transitions , working example of your changed code provided below.
const btnShow = document.querySelector('#btnShow');
const btnHide = document.querySelector('#btnHide');
const div = document.querySelector('div');
btnShow.addEventListener('click', () => {
div.classList.remove('hide');
div.classList.add('fade-in');
div.addEventListener('animationend', function() {
div.classList.remove('fade-in');
}, { once: true });
});
btnHide.addEventListener('click', () => {
console.log('btnHide clicked');
div.classList.add('fade-out');
div.addEventListener('animationend', () => {
div.classList.replace('fade-out', 'hide');
}, { once: true });
});
#keyframes fade-out {
0% {opacity: 1;}
100% {opacity: 0;}
}
#keyframes fade-in {
100% {opacity: 1;}
0% {opacity: 0;}
}
.hide { display: none; }
.fade-out {animation-name: fade-out; animation-duration: 1s; }
.fade-in {animation-name: fade-in; animation-duration: 1s; }
/* Irrelevant styles */
html, body {
height: 100vh;
width: 100vw;
margin: 0;
padding: 1em 1.5em;
font-size: 100%;
}
div {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
margin: 1em 0;
padding: 3rem 0;
width: 50%;
background: #4bfa;
border-radius: 5px;
transition: all 1s linear;
}
button {
border: none;
border-radius: 2px;
}
button:hover {
cursor: pointer;
}
<button id="btnShow">show</button>
<button id="btnHide">hide</button>
<div>
fade out, then set display: none<br />
then do the reverse...
</div>
I was able to get it working by wrapping the enter animations in a setTimeout(() => {}, 0);
I don't fully understand why this works, so I'd appreciate comments explaining it. I had tried putting them in a requestAnimationFrame() but that didn't seem to have the same effect.
If anyone knows a better or cleaner way to accomplish this, I'd love to see it.
const btnShow = document.querySelector('#btnShow');
const btnHide = document.querySelector('#btnHide');
const div = document.querySelector('div');
btnShow.addEventListener('click', () => {
console.log('btnShow clicked');
div.classList.replace('hide', 'div-enter');
setTimeout(() => {
div.classList.replace('div-enter', 'div-enter-to');
div.addEventListener('transitionend', function() {
console.log('show transition ended');
div.classList.remove('div-enter-to');
}, { once: true });
}, 0);
});
btnHide.addEventListener('click', () => {
console.log('btnHide clicked');
div.classList.add('div-leave');
div.classList.replace('div-leave', 'div-leave-to');
div.addEventListener('transitionend', () => {
console.log('hide transition ended');
div.classList.replace('div-leave-to', 'hide');
}, { once: true });
});
.hide {
display: none;
opacity: 0;
}
.div-enter {
opacity: 0;
}
.div-enter-to {
opacity: 1;
}
.div-leave {
opacity: 1;
}
.div-leave-to {
opacity: 0;
}
/* Irrelevant styles */
html, body {
height: 100vh;
width: 100vw;
margin: 0;
padding: .5em 1em;
font-size: 100%;
}
div {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
margin: 1em 0;
padding: 2rem 0;
width: 50%;
background: #4bfa;
border-radius: 5px;
transition: all 1s linear;
}
button {
border: none;
border-radius: 2px;
}
button:hover {
cursor: pointer;
}
<div>
fade out, then set display: none<br />
then do the reverse...
</div>
<button id="btnShow">show</button>
<button id="btnHide">hide</button>
I just got off support with ActiveCampaign and they said they couldn't provide me with code examples on how to add their modal pop-up forms to be triggered by wordpress buttons.
I found a few resources online but they are all slightly different than the functionality I'm looking for.
I already added the ActiveCampaign plugin to my wordpress site and there are two options of embedding the form within the site.
shortcode "[activeCampaign formId=1]" or
<script src="https://exampledomain.com/f/embed.php?id=1" type="text/javascript" charset="utf-8"></script>
I'm currently using the divi theme, and the buttons have sections for CSS ID's and CSS Classes.
so to summarize, I would like to be able to click a button and have the activecampaign modal form popup.
If you could show me how I can add code to the button and my site to trigger the modal popup that'd be amazing.
Let me know if you have any other information.
Thanks!
Sugesstion:
This involves DOM manipulation. create a css class called active which should be set to the form container to show. Here's an example:
var formToggle = document.getElementById("form-toggler");
var formContainer = document.querySelector(".form-container");
formToggle.addEventListener('click', function(){
// When you click the button, first check if the form is open
// so that you know if you should close or open
if(formContainer.classList.contains("active")){
// Form is currently open, because it has active as one of it's classes
// so remove active to hide it.
formContainer.classList.remove("active");
}else{
// Form is currently closed, because it does not have active as one of it's classes
// so add active to show it.
formContainer.classList.add("active");
}
});
.form-container{
width: 100%;
height: 100%;
min-height: 200px;
background-color: rgba(0,0,0,0.2);
display: none;
}
/* When form has active class, set display to block */
.form-container.active{
display: block !important;
}
<div class="form-container">
<!-- your Form Here -->
<h1>Yey, form is active!!</h1>
</div>
<button id="form-toggler">OpenForm<button>
This is just at the basic level of approaching your scenario. So you've got to work on your css to make your Modal cover the entire window and add a close button on it in case someone decides to close it.
Hey this should work for you also. Bear in mind there is some extra code you probably wont need all of it such as the animations but I will leave these in as they make the modal look a little slicker. You won't need bootstrap or any additional libraries for this code.
HTML:
<a id="gdx-lighbox-modal-unique-1" data-hover="true" type="button" class="gdx-lightbox-tooltip-open-modal lightbox-link gdx-lightbox-button" data-open="gdx-lighbox-modal-1">
Click Here
</a>
<div class="gdx-modal" id="gdx-lighbox-modal-1" data-animation="slideInOutLeft">
<div class="gdx-modal-dialog">
<header class="gdx-modal-header">
<a class="gdx-close-modal" aria-label="close modal" data-close="">✕</a>
</header>
<section class="gdx-modal-content">
//Form would go here instead of the image (image just an example)
<img src="https://gdxdesigns.com/wp-content/uploads/2020/11/little-frog.jpg"> </section>
<footer class="gdx-modal-footer"> <h3 class="gdx-modal-image-title"></h3></footer>
</div>
</div>
CSS:
/* RESET RULES
–––––––––––––––––––––––––––––––––––––––––––––––––– */
:root {
--lightgray: #efefef;
--blue: steelblue;
--white: #fff;
--black: rgba(0, 0, 0, 0.8);
--bounceEasing: cubic-bezier(0.51, 0.92, 0.24, 1.15);
}
* {
padding: 0;
margin: 0;
}
.gdx-body {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
font: 16px/1.5 sans-serif;
}
.lightbox-link, a.lightbox-link {
cursor: pointer;
margin-left: 2.5%;
}
.gdx-lightbox-tooltip-open-modal img {
width: 20px;
height: 20px;
}
.gdx-lightbox-button {
padding: 10px 20px;
background: #000;
padding: 10px 20px;
font-weight: normal;
border: 2px solid #000;
color: #94c93b;
}
.gdx-lightbox-button:hover {
background: #FFF;
color: #000;
}
/* MODAL
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.gdx-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
background: var(--black);
cursor: pointer;
visibility: hidden;
opacity: 0;
transition: all 0.35s ease-in;
z-index: 9999 !important;
}
.gdx-modal.is-visible {
visibility: visible;
opacity: 1;
}
.gdx-modal-dialog {
position: relative;
max-width: 100vw;
max-height: 100vh;
border-radius: 5px;
background: var(--white);
overflow: auto;
cursor: default;
margin-top: 5%;
}
.gdx-modal-dialog > * {
padding: 1rem;
}
.gdx-modal-header,
.gdx-modal-footer {
background: #FFF;
}
.gdx-modal-header .gdx-close-modal {
font-size: 1.5rem;
}
.gdx-modal-header a {
font-size: 2em;
}
.gdx-modal-content {
text-align: center;
}
.gdx-modal-content img {
margin: 0 !important;
}
.gdx-close-modal {
float: right;
cursor: pointer;
}
.gdx-modal p + p {
margin-top: 1rem;
}
.gdx-modal-image-title {
text-align: center;
font-size: 1em;
margin: 0;
}
/* ANIMATIONS
–––––––––––––––––––––––––––––––––––––––––––––––––– */
[data-animation] .gdx-modal-dialog {
opacity: 0;
transition: all 0.5s var(--bounceEasing);
}
[data-animation].is-visible .gdx-modal-dialog {
opacity: 1;
transition-delay: 0.2s;
}
[data-animation="slideInOutDown"] .gdx-modal-dialog {
transform: translateY(100%);
}
[data-animation="slideInOutTop"] .gdx-modal-dialog {
transform: translateY(-100%);
}
[data-animation="slideInOutLeft"] .gdx-modal-dialog {
transform: translateX(-100%);
}
[data-animation="slideInOutRight"] .gdx-modal-dialog {
transform: translateX(100%);
}
[data-animation="zoomInOut"] .gdx-modal-dialog {
transform: scale(0.2);
}
[data-animation="rotateInOutDown"] .gdx-modal-dialog {
transform-origin: top left;
transform: rotate(-1turn);
}
[data-animation="mixInAnimations"].is-visible .gdx-modal-dialog {
animation: mixInAnimations 2s 0.2s linear forwards;
}
[data-animation="slideInOutDown"].is-visible .gdx-modal-dialog,
[data-animation="slideInOutTop"].is-visible .gdx-modal-dialog,
[data-animation="slideInOutLeft"].is-visible .gdx-modal-dialog,
[data-animation="slideInOutRight"].is-visible .gdx-modal-dialog,
[data-animation="zoomInOut"].is-visible .gdx-modal-dialog,
[data-animation="rotateInOutDown"].is-visible .gdx-modal-dialog {
transform: none;
}
#keyframes mixInAnimations {
0% {
transform: translateX(-100%);
}
10% {
transform: translateX(0);
}
20% {
transform: rotate(20deg);
}
30% {
transform: rotate(-20deg);
}
40% {
transform: rotate(15deg);
}
50% {
transform: rotate(-15deg);
}
60% {
transform: rotate(10deg);
}
70% {
transform: rotate(-10deg);
}
80% {
transform: rotate(5deg);
}
90% {
transform: rotate(-5deg);
}
100% {
transform: rotate(0deg);
}
}
/* Backend Instructions
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.lightbox-instructions-heading {
font-size: 1.8em !important;
}
.lightbox-instructions strong {
font-size: 1.2em !important;
}
.gdx-lightbox-tooltip-open-modal img {
margin: -0.3em;
margin-left: 0.25em;
}
xmp {
white-space: normal;
}
.lightbox-tooltip-instructions-content xmp{
margin-bottom: 2em;
}
Javascript
const openEls = document.querySelectorAll("[data-open]");
const closeEls = document.querySelectorAll("[data-close]");
const isVisible = "is-visible";
for (const el of openEls) {
el.addEventListener("click", function() {
const modalId = this.dataset.open;
document.getElementById(modalId).classList.add(isVisible);
});
}
for (const el of closeEls) {
el.addEventListener("click", function() {
this.parentElement.parentElement.parentElement.classList.remove(isVisible);
});
}
document.addEventListener("click", e => {
if (e.target == document.querySelector(".gdx-modal.is-visible")) {
document.querySelector(".gdx-modal.is-visible").classList.remove(isVisible);
}
});
document.addEventListener("keyup", e => {
// if we press the ESC
if (e.key == "Escape" && document.querySelector(".gdx-modal.is-visible")) {
document.querySelector(".gdx-modal.is-visible").classList.remove(isVisible);
}
});
JSFiddle Example can be seen here.
If you want to download this as a Wordpress Plugin (free of course) you can do so here
If you want to see the a demo of the plugin in action with the button modal popup you see this here
I've come up with this simple code, but for some reason, it does not always work exactly the way I would like it to.
Here's what I have. There are tiles that flip on hover. There are two possible scenarios and two reactions.
If I mouse is hovered over a tile, the tile is supposed to flip 180° and stay in this possition. This happens, if mouseleave happens in longer time than 1 second. After unhover, the tile should flip back to its original state.
If mouse is hovered over a tile and unhover immediately, the tile is supposed to do a full 180° flip before returning to its original state. This happens when mouse leaves in the 1 second timeframe. In this case, the JS scripts waits for the CSS transition to end before fliping the tile back.
Now, this works fine until the second scenario takes place. Then for some reason, the tile returns to its original state after a full transition despite the mouse not leaving the tile. Why is that? Any ideas on how to overcome this issue?
Thanks.
$(function() {
var timeoutId;
$(".tile").mouseenter(function() {
$(this).addClass("flip");
if (!timeoutId) {
timeoutId = window.setTimeout(function() {
timeoutId = null;}, 1000);
}
});
$(".tile").mouseleave(function() {
if (timeoutId) {
$(this).on('transitionend webkitTransitionEnd oTransitionEnd', function () {
$(this).removeClass("flip");
window.clearTimeout(timeoutId);
timeoutId = null;
});
}
else {
$(this).removeClass("flip");
}
});
})
body {
text-align: center;
background-color: #FFFFFF;
color: #454545;
}
.flex-container {
padding: 0;
margin: 0;
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-flex-flow: row;
justify-content: space-around;
}
.tile {
background: #454545;
margin: 0px;
font-weight: bold;
flex: 1 0 auto;
height: auto;
border: 1px solid #454545;
border-radius: 10%;
}
.tile:before {
content: '';
float: left;
padding-top: 100%;
}
.tile-inner {
position: relative;
width: 100%;
height: 100%;
border-radius: 10%;
transform-style: preserve-3d;
transition: transform 4s;
}
.tile.flip .tile-inner {
transform: rotate3d(-1, 1, 0, 180deg);
transition: transform 1s;
}
.tile-front, .tile-off, .tile-semi, .tile-rsemi, .tile-on {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
border-radius: 10%;
}
.tile-front {
background-color: #1c1c1c;
background-size: 100%;
}
.tile-off {
background-color: #252525;
transform: rotate3d(-1, 1, 0, 180deg);
}
.tile-semi {
background: linear-gradient(to right bottom, #252525 50%, #dedede 50%);
transform: rotate3d(-1, 1, 0, 180deg);
}
.tile-rsemi {
background: linear-gradient(to right bottom, #dedede 50%, #252525 50%);
transform: rotate3d(-1, 1, 0, 180deg);
}
.tile-on {
background-color: #dedede;
transform: rotate3d(-1, 1, 0, 180deg);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<body>
<div class="flex-container">
<div class="tile">
<div class="tile-inner">
<div class="tile-front"></div>
<div class="tile-semi"></div>
</div>
</div>
<div class="tile">
<div class="tile-inner">
<div class="tile-front"></div>
<div class="tile-on"></div>
</div>
</div>
<div class="tile">
<div class="tile-inner">
<div class="tile-front"></div>
<div class="tile-rsemi"></div>
</div>
</div>
</div>
</body>
You are sharing one variable and using it on all of the other tiles. So the timeout for tile 1 will also be with tile 2 when you mouseover it. You should be setting the timeouts for each one individually. Basic idea with data.
$(function() {
$(".tile").mouseenter(function() {
var elem = $(this);
elem.addClass("flip");
if (!elem.data('timeoutId')) {
var tid = window.setTimeout(function() {
elem.removeData('timeoutId')
}, 1000);
elem.data('timeoutId', tid)
}
});
$(".tile").mouseleave(function() {
var elem = $(this);
var timeoutId = elem.data('timeoutId')
if (timeoutId) {
window.clearTimeout(timeoutId);
elem.removeData('timeoutId')
elem.off('transitionend webkitTransitionEnd oTransitionEnd')
elem.on('transitionend webkitTransitionEnd oTransitionEnd', function() {
elem.removeClass("flip");
});
} else {
$(this).removeClass("flip");
}
});
})
body {
text-align: center;
background-color: #FFFFFF;
color: #454545;
}
.flex-container {
padding: 0;
margin: 0;
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-flex-flow: row;
justify-content: space-around;
}
.tile {
background: #454545;
margin: 0px;
font-weight: bold;
flex: 1 0 auto;
height: auto;
border: 1px solid #454545;
border-radius: 10%;
}
.tile:before {
content: '';
float: left;
padding-top: 100%;
}
.tile-inner {
position: relative;
width: 100%;
height: 100%;
border-radius: 10%;
transform-style: preserve-3d;
transition: transform 4s;
}
.tile.flip .tile-inner {
transform: rotate3d(-1, 1, 0, 180deg);
transition: transform 1s;
}
.tile-front,
.tile-off,
.tile-semi,
.tile-rsemi,
.tile-on {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
border-radius: 10%;
}
.tile-front {
background-color: #1c1c1c;
background-size: 100%;
}
.tile-off {
background-color: #252525;
transform: rotate3d(-1, 1, 0, 180deg);
}
.tile-semi {
background: linear-gradient(to right bottom, #252525 50%, #dedede 50%);
transform: rotate3d(-1, 1, 0, 180deg);
}
.tile-rsemi {
background: linear-gradient(to right bottom, #dedede 50%, #252525 50%);
transform: rotate3d(-1, 1, 0, 180deg);
}
.tile-on {
background-color: #dedede;
transform: rotate3d(-1, 1, 0, 180deg);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<body>
<div class="flex-container">
<div class="tile">
<div class="tile-inner">
<div class="tile-front"></div>
<div class="tile-semi"></div>
</div>
</div>
<div class="tile">
<div class="tile-inner">
<div class="tile-front"></div>
<div class="tile-on"></div>
</div>
</div>
<div class="tile">
<div class="tile-inner">
<div class="tile-front"></div>
<div class="tile-rsemi"></div>
</div>
</div>
</div>
</body>
The problem is that when you run:
if (timeoutId) {
$(this).on('transitionend webkitTransitionEnd oTransitionEnd', function () {
$(this).removeClass("flip");
window.clearTimeout(timeoutId);
timeoutId = null;
});
}
The event stays in the element indefinitely. So, next time you hover the element, when it finishes transition, this event triggers again.
What you probably want is to run the event only once:
if (timeoutId) {
$(this).once('transitionend webkitTransitionEnd oTransitionEnd', function () {
$(this).removeClass("flip");
window.clearTimeout(timeoutId);
timeoutId = null;
});
}