I'm facing a problem with wiggling bottom border pseudo css element, pointer events set to none fixes it but I have decide to go with javascript and create a span on mouse enter event and remove it on mouse leave event. with set time method, I created an acceptable animation like feeling but now back to the same problem WIGGLING. So is there an equivalent code in javascript that mimics pointer events set to none?
<a class="top-link">
<span class="title"> Title </span>
</a>
.border-bottom {
border-bottom: solid 3px #ddd;
content: "";
width: 100%;
position: absolute;
top: 100%;
z-index: 1;
left: 0;
transform: translateY(4px);
transition: ease-in 0.2s;
opacity: 0;
}
document.querySelectorAll(".top-link").forEach((el) => {
el.addEventListener("mouseenter", function () {
el.insertAdjacentHTML("beforeend", '<span class="border-bottom"></span>');
el.childNodes.forEach((e) => {
if (e.className == "border-bottom") {
setTimeout(() => {
e.style.transform = "translateY(0px)";
e.style.opacity = "1";
}, 50);
}
});
});
el.addEventListener("mouseleave", function () {
el.childNodes.forEach((e) => {
if (e.className == "border-bottom") {
setTimeout(() => {
e.style.transform = "translateY(4px)";
}, 25);
setTimeout(() => {
e.style.opacity = "0.4";
}, 50);
setTimeout(() => {
e.style.opacity = "0";
e.remove();
}, 250);
}
});
});
});
Related
I've got a border that i would like to reveal when in the viewport and then stay as the complete line. i've used the following css. Does anyone know some lightweight JS to activate the animation?
.draw-line {
border-left:1px solid rgb(255,0,0);
animation: draw-line 5s;
Animation-fill-mode: forwards
}
#keyframes draw-line {
0% {
height:0
}
50% {
border-left:1px solid rgb(89,0,255)
}
100% {
height:100vh;
border-left:1px solid rgb(255,0,0)
}
}
You can use the Insertion Observer API. This snippet will add the class draw-line to all the viewport-divs when the viewport-div is in the viewport, and removes them when out of the viewport.
if (!!window.IntersectionObserver) {
let viewportDivs = [...document.querySelectorAll(".viewport-div")];
viewportDivs.forEach((div) => {
insertionObserverFunction(div);
});
}
function insertionObserverFunction(div) {
let observer = new IntersectionObserver(
(entries, observer) => {
entries.forEach((entry) => {
if (entry.intersectionRatio != 0) {
div.classList.add('draw-line')
} else {
div.classList.remove('draw-line')
}
});
},
{ threshold: 0.51 }
);
observer.observe(div);
}
User the threshold property to adjust when to add the class.
const b = document.querySelector('.start-button');
const d = document.querySelector('.draw-line');
b.addEventListener('click', function() {
b.disabled = true;
d.style.animation = 'draw-line 5s';
});
d.addEventListener('animationend', function() {
d.style.animation = 'unset';
b.disabled = false;
});
body {
margin: 0;
}
.draw-line {
border-left: 10px solid rgb(255, 0, 0);
Animation-fill-mode: forwards
}
#keyframes draw-line {
0% {
height: 0
}
50% {
border-left: 10px solid rgb(89, 0, 255)
}
100% {
height: 100vh;
border-left: 10px solid rgb(255, 0, 0)
}
}
.start-button {
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
padding: 1em 2em;
}
<div class="draw-line"></div>
<button class="start-button">Start</button>
I'm assuming you are using Vanilla JS?
Could this work?
function isInViewport(element) {
const rect = element.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}
This function takes in a DOM element and checks if the element is visible in the viewport. It could be paired with an eventlistener for scroll, if it returns true, add .draw-line to the classlist. If it returns false, remove from classlist.
Check this out
Edit:
Maybe I misunderstood what you wanted. If you want elements to play an animation just once and stay like that when scrolled into view.
Then this works
window.addEventListener('scroll', reveal);
function reveal() {
var reveals = document.querySelectorAll('.reveal');
for(const reveal of reveals) {
var revealTop = reveal.getBoundingClientRect().top;
var revealPoint = 150;
if(revealTop < viewport.height - revealPoint) {
reveal.classList.add('draw-line');
}
}
}
What you do here is add .reveal to every element that you want to animate. You dont need to define the styles for .reveal. It just works as some kind of guiding light on what elements you want to trigger.
RevealPoint represents the Y pixels from top.
When reveal comes into view, the animation class gets added to it and stays that way.
Is it possible to a piece of javascript on mobile/tablet devices only? I'd like to do something a bit better than display: none and it makes sense to stop the script from running if it's not required?
Basically I have a custom cursor effect, that is only required when it follows the cursor on desktop with a mouse/trackpad.
This is the script I have:
var cursor = document.querySelector('.cursor-outer');
var cursorinner = document.querySelector('.cursor');
var a = document.querySelectorAll('a');
var moveCursor = true;
var radiusOfCursor = parseInt(getComputedStyle(cursor).getPropertyValue('width')) / 2; // radiusOfCursor = (width_of_cursor / 2).
document.addEventListener('mousemove', function (e) {
var x = e.clientX;
var y = e.clientY;
cursorinner.style.left = x + 'px';
cursorinner.style.top = y + 'px';
if (!moveCursor) return;
cursor.style.marginLeft = `calc(${e.clientX}px - ${radiusOfCursor}px)`;
cursor.style.marginTop = `calc(${e.clientY}px - ${radiusOfCursor}px)`;
moveCursor = false;
setTimeout(() => {
moveCursor = true;
}, 32) // The wait time. I chose 95 because it seems to work just fine for me.
});
/* Centre pointer after stopping */
function mouseMoveEnd() {
cursor.style.marginLeft = `calc(${cursorinner.style.left} - ${radiusOfCursor}px)`;
cursor.style.marginTop = `calc(${cursorinner.style.top} - ${radiusOfCursor}px)`;
}
var x;
document.addEventListener('mousemove', function() {
if (x) clearTimeout(x);
x = setTimeout(mouseMoveEnd, 10);
}, false);
/* End */
document.addEventListener('mousedown', function() {
cursor.classList.add('click');
cursorinner.classList.add('cursorinnerhover');
});
document.addEventListener('mouseup', function() {
cursor.classList.remove('click');
cursorinner.classList.remove('cursorinnerhover');
});
a.forEach(item => {
item.addEventListener('mouseover', () => {
cursor.classList.add('hover');
});
item.addEventListener('mouseleave', () => {
cursor.classList.remove('hover');
});
});
a.forEach((item) => {
const interaction = item.dataset.interaction;
item.addEventListener("mouseover", () => {
cursorinner.classList.add(interaction);
});
item.addEventListener("mouseleave", () => {
cursorinner.classList.remove(interaction);
});
});
* {
cursor: none;
}
.cursor-outer {
border: 2px solid black;
border-radius: 100%;
box-sizing: border-box;
height: 32px;
pointer-events: none;
position: fixed;
top: 16px;
left: 16px;
transform: translate(-50%, -50%);
transition: height .12s ease-out, margin .12s ease-out, opacity .12s ease-out, width .12s ease-out;
width: 32px;
z-index: 100;
}
.cursor {
background-color: black;
border-radius: 100%;
height: 4px;
opacity: 1;
position: fixed;
transform: translate(-50%, -50%);
pointer-events: none;
transition: height .12s, opacity .12s, width .12s;
width: 4px;
z-index: 100;
}
<div class="cursor-outer"></div>
<div class="cursor"></div>
Thanks in advance!
Option #1
You can use something similar to this to determine if a device is touch-enabled:
isTouchDevice = () => {
return ( 'ontouchstart' in window ) ||
( navigator.maxTouchPoints > 0 ) ||
( navigator.msMaxTouchPoints > 0 );
};
This is adapted from Patrick H. Lauke's Detecting touch article on Mozilla.
Then just: if (isTouchDevice()) { /* Do touch screen stuff */}
Option #2
But maybe a pure CSS approach could work better in your situation, like:
#media (hover: none) {
.cursor {
pointer-events: none;
}
}
Option #3
If you don't mind using a third-party library, then Modernizr is really great for detecting things like this in the user's environment. Specifically, Modernizr.pointerevents will confirm if touchscreen is being used.
I want to toggle between showing and hiding two divs on a mouseenter and click event. However, if the click event happens in the same position as the mouseenter area the functions go into a loop, illustrated in the gif below.
How can I delay the showOverlay function for a few seconds but maintain the mouseenter event working normally like in the gif below (i.e no delay):
Any help would be greatly appreciated?
var hover = document.querySelector('#hover');
var overlay = document.querySelector('#overlay');
var primary = document.querySelector('#primary');
var overlayShowing = false;
var clicked = false
function showOverlay() {
overlayShowing = true
overlay.classList.add('display');
setTimeout(function(){
overlay.classList.add('active');
}, 100);
primary.classList.add('nactive');
}
function hideOverlay() {
overlayShowing = false
overlay.classList.remove('active');
primary.classList.remove('hidden');
setTimeout(function(){
primary.classList.remove('nactive');
}, 100);
}
primary.addEventListener('transitionend', function() {
if(overlayShowing) {
primary.classList.add('hidden');
}
})
overlay.addEventListener('transitionend', function() {
if(!overlayShowing) {
overlay.classList.remove('display');
}
});
hover.addEventListener('mouseenter', function(event) {
showOverlay();
});
overlay.addEventListener("click", function() {
hideOverlay();
});
#primary {
opacity: 1;
display: block;
transition: 0.5s ease
}
#primary.hidden {
display: none !important;
}
#primary.nactive {
opacity: 0;
}
#hover {
font-size: 150px;
}
#overlay {
position: fixed;
height: 100vh;
width: 100vw;
top: 0;
left: 0;
background-color: black;
opacity: 0;
display: none;
transition: 0.5s ease
}
#overlay.active {
opacity: 0.8;
}
#overlay.display {
display: block;
}
<div id="primary">
<div id="hover">HOVER</div>
</div>
<div id="overlay"></div>
Just use a timeout to delay the showing:
var showTimeout;
function showOverlay() {
if(showTimeout) return;
showTimeout = setTimeout(() => {
overlayShowing = true;
overlay.classList.add('display');
setTimeout(function(){
overlay.classList.add('active');
}, 100);
primary.classList.add('nactive');
}, 500);
}
function hideOverlay() {
if(showTimeout) {
clearTimeout(showTimeout);
showTimeout = null;
}
overlayShowing = false
overlay.classList.remove('active');
primary.classList.remove('hidden');
setTimeout(function(){
primary.classList.remove('nactive');
}, 100);
}
This codepen shows my problem: http://codepen.io/PiotrBerebecki/pen/pNvpdG
When the user clicks on the big button the css opacity is reduced to 0. Since I've applied the following rule: transition: opacity 0.5s ease-in-out; the fade out animation is smooth.
I would like to achieve the same smooth transition when the next button fades in.
However for some reason the next button appears suddenly without any transition.
Would you know what causes the issue and how to fix it?
console.clear();
(function() {
// Data for the app
const model = {
buttons: ['tomato', 'blue'],
currentButton: -1
};
// Logic for the app
const controller = {
init: function() {
view.init();
},
getButtonName: function() {
model.currentButton = (model.currentButton + 1) % model.buttons.length;
return model.buttons[model.currentButton];
}
};
// View for the app
const view = {
init: function() {
this.root = document.getElementById('root');
this.showNext();
},
animationDelay: 500,
showNext: function() {
// Get next button name
const buttonName = controller.getButtonName();
// Create button DOM element
const buttonElement = document.createElement('div');
buttonElement.className = 'button';
buttonElement.id = buttonName;
buttonElement.textContent = buttonName;
buttonElement.style.opacity = 0;
// Add event listender for the button
buttonElement.addEventListener('click', event => {
// Reduce opacity
buttonElement.style.opacity = 0;
// Remove the button from DOM
setTimeout(() => {
this.root.removeChild(buttonElement);
}, this.animationDelay + 10);
// Start the function to show next button
setTimeout(() => {
this.showNext();
}, this.animationDelay + 20);
});
// Add button to DOM
this.root.appendChild(buttonElement);
// Show button by increasing opacity
buttonElement.style.opacity = 1;
}
};
// Start the app
controller.init();
}());
#tomato {
background: tomato;
}
#blue {
background: DeepSkyBlue;
}
.button {
transition: opacity 0.5s ease-in-out;
width: 100%;
height: 50vh;
border: solid 3px black;
cursor: pointer;
}
<div id="root"></div>
This should work , Code pen link: http://codepen.io/saa93/pen/gLbvmQ
You would need to add this instead of directly setting opacity to 1
// Show button by increasing opacity
buttonElement.style.opacity = 0;
setTimeout(() => {
buttonElement.style.opacity = 1;
}, this.animationDelay + 20);
Add a class (in the Snippet is .active) add the following:
CSS
.button {
opacity: 0;
transition: opacity 0.5s ease-in-out;
width: 100%;
height: 50vh;
border: solid 3px black;
cursor: pointer;
}
.button.active {
opacity: 1;
transition: opacity 0.5s ease-in-out;
}
JavaScript
...
// Reduce opacity
buttonElement.classList.toggle('active');
buttonElement.style.opacity = 0;
...
// Show button by increasing opacity
buttonElement.classList.toggle('active');
buttonElement.style.opacity = 1;
SNIPPET
console.clear();
(function() {
// Data for the app
const model = {
buttons: ['tomato', 'blue'],
currentButton: -1
};
// Logig for the app
const controller = {
init: function() {
view.init();
},
getButtonName: function() {
model.currentButton = (model.currentButton + 1) % model.buttons.length;
return model.buttons[model.currentButton];
}
};
// View for the app
const view = {
init: function() {
this.root = document.getElementById('root');
this.showNext();
},
animationDelay: 500,
showNext: function() {
// Get next button name
const buttonName = controller.getButtonName();
// Create button DOM element
const buttonElement = document.createElement('div');
buttonElement.className = 'button';
buttonElement.id = buttonName;
buttonElement.textContent = buttonName;
buttonElement.style.opacity = 0;
// Add event listender for the button
buttonElement.addEventListener('click', event => {
// Reduce opacity
buttonElement.classList.toggle('active');
buttonElement.style.opacity = 0;
// Remove the button from DOM
setTimeout(() => {
this.root.removeChild(buttonElement);
}, this.animationDelay + 10);
// Start the function to show next button
setTimeout(() => {
this.showNext();
}, this.animationDelay + 20);
});
// Add button to DOM
this.root.appendChild(buttonElement);
// Show button by increasing opacity
buttonElement.classList.toggle('active');
buttonElement.style.opacity = 1;
}
};
// Start the app
controller.init();
}());
#tomato {
background: tomato;
}
#blue {
background: DeepSkyBlue;
}
.button {
opacity: 0;
transition: opacity 0.5s ease-in-out;
width: 100%;
height: 50vh;
border: solid 3px black;
cursor: pointer;
}
.button.active {
opacity: 1;
transition: opacity 0.5s ease-in-out;
}
<div id="root"></div>
after this.root.appendChild(buttonElement);
you should set opacity to 0 and let the browser time to render before buttonElement.style.opacity = 1;
BTW I think removing and adding the element of not a good way to do this
.button {
width: 100%;
height: 50vh;
border: solid 3px black;
cursor: pointer;
animation-name: example;
animation-duration:3.5s;
}
#keyframes example {
0% {opacity:1}
50% {opacity:0}
100% {opacity:1}
}
What U really want is to use animation like this:JSFIDDLE EXAMPLE
This way the animation does all this timing and opacity back and forth using the css only
I am trying to fade an element that is being created dynamically with JavaScript.
Here's a CSS example of what I am trying to do: https://codepen.io/deejay/pen/OJJqZaL
Javascript
...
const fadeOutWithOpacity = () => {
const opacity = totalCount >= 1 && 0.5;
return opacity;
};
const handleOpacity = () => {
setInterval(() => {
fadeOutWithOpacity();
}, 3000);
};
...
style: {
background: "#FA4379",
color: "#fff",
opacity: `${handleOpacity()}`
}
I only want to change the opacity value every 3sec
You can use Javascript to add a class to your element which will cause the opacity to decrease slowly thanks to the CSS transition property.
const spawnBox = () => {
var box = document.createElement("div");
box.className = "box";
box.onclick = (event) => {
box.classList.add("hidden");
};
document.body.appendChild(box);
};
spawnBox();
setInterval(() => {
spawnBox();
}, 1000);
.box {
width: 20px;
height: 20px;
margin: 5px;
background: red;
transition: opacity .5s;
}
.box.hidden {
opacity: 0;
}