This question already has answers here:
Javascript set button active
(5 answers)
Closed 1 year ago.
Im making a simon says game,
i use 4 colored buttons that works perfectly.
the process is simple- the program present the user with the colors.
the repeat the sequence by clicking the buttons.
I wish to let the program activate the button the same way a user would, with the appropriate activate effect.
.but:active{ // <-- how do i initiate this with js?
box-shadow: 0 5px #666;
transform: translateY(4px);
You mean
document.querySelector(".but").click()
or
const buts = document.querySelectorAll(".but");
const cnt = 0;
setInterval(function() {
if (cnt>= 0) cnt = 0;
but[cnt].click();
cnt++;
},2000)
let but = document.querySelector('.but');
but.addEventListener('click', () => {
but.classList.add('active');
let activeStatus = document.querySelector('.active');
activeStatus.style = `
box-shadow: 0 5px #666;
transform: translateY(4px);
`;
});
Related
I have a simple JS scroll event that when an element gets to within 50px of the top of the window the header animates and changes colour, which is done by using getBoundingClientRect().top < 50 on a trigger element. This functionality is only on the home page of the site.
Is there anyway of having it so when a user visits another URL/page on the site, and then comes back to this page via the browsers back arrow, that the previous animation state is still applied? If the page reloads and starts at the top again it doesn't matter, but if you click back to the page that uses this code, the menu transition happens even if you return to part of the page that was past the trigger point. I don't want to force the page to the top each time because this page is going to have downloadable and searchable info on, so that it would be real pain to be sent back to the top of that page each time.
I've given a working example below and via the CodePen link, the problem is of course on CodePen and StackOverflow when you go to a different URL and then click back to URL in question it actually reloads the page from scratch again, which doesn't happen as standard browser behaviour on day-to-day websites.
Codepen: https://codepen.io/anna_paul/pen/bGvPWRj
In that back end I'm using PHP, and I do have access to this is there needs to be a server side solution.
Any ideas or suggestions appreciated.
Note: On the actual site this scroll event is invoked via a debounce function, but I have removed this for code simplicity.
let triggerElement = document.getElementById('trigger-element'),
header = document.getElementById('h')
let menuChange = function() {
if(triggerElement.getBoundingClientRect().top < 50) {
header.style.background = 'black'
header.style.transition = '1s'
} else {
header.style.background = 'red'
header.style.transition = '.15s'
}
}
window.addEventListener('scroll', menuChange)
body {
margin: 0;
height: 200vh;
}
#h {
display: flex;
justify-content: center;
background: red;
color: #fff;
position: fixed;
top: 0;
width: 100%;
}
#trigger-element {
margin-top: 150px;
padding: 1rem;
background:blue;
color: #fff;
}
<header id="h">
<p>HEADER CONTENT</p>
</header>
<div id="trigger-element">Trigger Element</div>
I recommend using localStorage for this particular use case, because it can easily be implemented alongside your current method:
const triggerElement = document.getElementById('trigger-element');
const header = document.getElementById('h');
const animationTriggered = localStorage.getItem('animationTriggered') === 'true';
let initialLoad = true;
const menuChange = function() {
if (animationTriggered && initialLoad) {
header.style.background = 'black';
} else if (triggerElement.getBoundingClientRect().top < 50) {
header.style.background = 'black';
header.style.transition = '1s';
localStorage.setItem('animationTriggered', 'true');
} else {
header.style.background = 'red';
header.style.transition = '.15s';
localStorage.setItem('animationTriggered', 'false');
}
initialLoad = false;
}
window.addEventListener('scroll', menuChange);
This will remember the previous state and apply the black background color if the animation was previously triggered. This adds a small amount of overhead, but in a real-world scenario it should not have any noticeable impact on the performance of the application.
I am coding a simple navigation bar for a project that has four sections, and I made it interactive enough to have a specific color when hovering/clicking on a section and then it returns back to its original color after clicking.
But what if I want the selected section to still be colored/highlighted when a user is viewing it?
So if the hovering color is coded blue, i want the section in the Navbar to still be blue when a user has selected it, and then changes when a user selects another section. Here's my code so far.
// The mouse hover functiona and commands. Here we specificy the color of the buttons/mouse
// when the user clicks on them, there's a color for hovering/clicking
// and a color for leaving the button
function mouseOver () {
let anchor = document.getElementsByTagName('a');
for (i = 0; i < anchor.length; i++) {
anchor[i].addEventListener('mouseover', function handleMouseOver() {
event.target.style.backgroundColor = "#72a6ca";
event.target.style.color = "#fff";
})
//the color returns to its normal state after clicking away
anchor[i].addEventListener('mouseout', function handleMouseOut() {
event.target.style.backgroundColor = "rgb(220, 220, 220)";
event.target.style.color = "black";
})
}
}
and here is my navbar display code
function navBarStyle () {
let anchor = document.getElementsByTagName('a');
let styles = `
display: flex;
flex-direction: row;
align-items: stretch;
color: #000;
text-decoration: none;
margin: 0 0.5em 0 0.5em;
padding: 0.5em;
background-color: rgb(220, 220, 220);
font-size: large;
transform:translateX(-0.5em);
`;
for (i = 0; i < anchor.length; i++) {
anchor[i].setAttribute('style', styles);
} }
if i was vague enough i am sorry, but any help would be appreciated to put me on the right track
Firstly, a note for your current implementation. It works and it is pretty well coded. But for this thing browsers offer native functionality using the :hover selector and it would be better to use than to reinvent it.
I don't have your HTMl but you would most likely need to add a class to each 'a' tag in the nav, something like this:
<nav>
Link 1
Link 2
</nav>
and then you would need a style tag in the head (or better, external css)
<head>
...
<style>
.nav-link {
background-color: 72a6ca;
color: #fff;
}
.nav-link:hover {
background-color: rgb(220, 220, 220);
color: black;
}
</style>
</head>
As for the current section, your best bet would be to use https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
See here for an example: Intersection observer API scroll aware navigation
or this codepen: https://codepen.io/mishunov/pen/opeRdL
Using IntersectionObserver you can detect when the user scrolls in/out of the section. You can toggle another class on and off of the related nav-link then. For example - say you toggle the .current class, your style could look like this to style both cases (hovering and currently scrolled) in 1 place:
.nav-link:hover,
.nav-link.current {
background-color: rgb(220, 220, 220);
color: black;
}
You can make a class named active like this
.active {
backgroundColor: #72a6ca;
color: #fff;
}
and assign it to each anchor that's clicked(or hovered), simultaneously remove .active from the other anchors
let anchors = document.getElementsByTagName('a');
for (let anchor of anchors) {
anchor.addEventListener('mouseover', function handleMouseOver() {
const target = event.currentTarget;
if (target.classList.contains('active')) {
target.classList.remove('active')
} else {
[...anchors].forEach((anchor) => anchor.classList.remove('active'))
target.classList.add('active')
}
})
}
If you want to give the class active to the anchors in viewPort use this code:
const anchors = document.getElementsByTagName('a');
const isInViewport = el => {
const rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <=
(window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
};
const run = () =>
anchors.forEach(item => {
if (isInViewport(item)) {
item.classList.add('active');
}
});
// Events
window.addEventListener('load', run);
window.addEventListener('resize', run);
window.addEventListener('scroll', run);
In this example, after the button is clicked, I first want to transform the box and then show an alert. But here, it first show the alert and then do the transform
const clickme = document.getElementById('clickme');
const box = document.getElementById('box');
clickme.addEventListener("click", () => {
box.style.transform = `rotate(90deg)`;
const value = parseFloat(box.style.transform.split('rotate(')[1]);
if(value >= 90) {
alert('Hello Rotate')
}
})
#box {
width: 100px;
height: 100px;
background: tomato;
transition: .200s ease;
}
<div id="box">
</div>
<button id="clickme">
rotate
</button>
That's the limit for transform for you.
To achieve best performance, a reliable result and actually use the right tools for the right purpose i'd suggest learning CSS animations with keyframes.
It's not that hard and it will allow you to unload some of the tasks from the main thread to the gpu(better SEO and performance).
Else there are plenty of libraries that you could use:
For example: https://animejs.com/
In your particular case, the problem is not of the transform property or not having keyframes as Filas' answer pointed out, but instead the problem lies in the nature of Javascript.
const clickme = document.getElementById('clickme');
const box = document.getElementById('box');
clickme.addEventListener("click", () => {
box.style.transform = `rotate(90deg)`;
const value = parseFloat(box.style.transform.split('rotate(')[1]);
if(value >= 90) {
alert('Hello Rotate')
}
})
The browser pauses the rendering of the HTML, which is responsible for box.style.transform = 'rotate(90deg)';, and instead prioritizes showing the alert(). Here's a thread that talks about it more.
I assume that in your actual code, you would be using some other function, instead of the alert(). In that case, your code should run as you intended, with the transform occurring first, followed by the subsequent commands.
const clickme = document.getElementById('clickme');
const box = document.getElementById('box');
clickme.addEventListener("click", () => {
box.style.transform = 'rotate(90deg)';
const value = parseFloat(box.style.transform.split('rotate(')[1]);
if(value >= 90) {
box.style.backgroundColor="green"; //code that isn't an alert()
}
})
#box {
width: 100px;
height: 100px;
background: tomato;
transition: .200s ease;
}
<div id="box">
</div>
<button id="clickme">
rotate
</button>
Combine this with a transitionend event, or a setTimeout() function, if you want to sequence the changes in a way that the individual changes/steps are clearly visible to the user.
I have this piece of code for drumkit project for playing audio and add an transition effect to the pressed button. Try here drumkitProject
CSS
.key {
border: .4rem solid black;
border-radius: 10%;
margin:1rem;
font-size: 1.5rem;
padding: 1rem .5rem;
transition: all .07s;
width: 10rem;
text-align: center;
color: white;
background: rgba(0,0,0,0.4);
text-shadow: 0 0 .5rem black;
}
.playing {
transform: scale(1.1);
border-color: #ffc600;
box-shadow: 0 0 1rem #ffc600;
}
Javascript
function playSound(e) {
const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
const pressedKey = document.querySelector(
`.key[data-key="${e.keyCode}"]`
);
if (!audio) return;
pressedKey.classList.add("playing");
audio.currentTime = 0;
audio.play();
}
function stopSound(e) {
if (e.propertyName !== "transform") return;
this.classList.remove("playing");
}
window.addEventListener("keydown", playSound);
const keys = document.querySelectorAll(".key");
for (let index = 0; index < keys.length; index++) {
keys[index].addEventListener("transitionend", stopSound);
}
When I keep the button pressed the transition effect gets permanently added to the button and the button does not return back to normal. Why is that happening when I have removed the class as soon as the transition gets over.
Code:https://github.com/heysujal/drumkit2
I found out that the effect is permanently added when the transitionend event is blocked from firing (like I said in the comments).
To fix this issue, you can just add a setTimeout in playSound() function, to remove the class, after certain duration.
function playSound(e) {
const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
const pressedKey = document.querySelector(
`.key[data-key="${e.keyCode}"]`
);
if (!audio) return;
pressedKey.classList.add("playing");
audio.currentTime = 0;
audio.play();
setTimeout(() => pressedKey.classList.remove("playing"), 150);
}
Try it out on fiddle.
It seems to be a bug in the way how transitions are being handled for expensive properties like transition or box-shadow. I managed to reproduce this behavior in Chrome, Firefox and Safari, but looks like Chrome always does it, while Firefox and Safari are more random.
An easy fix for you would be to replace transform in if (e.propertyName !== "transform") return; with a cheaper property, like border-top-color:
if (e.propertyName !== "border-top-color") return;
But I'm doubtful if you want to rely on the keydown repeat in your app, since you have no control over repeat interval. You can consider disabling the repeating sounds entirely:
function playSound(e) {
if (e.repeat) return;
// ...
}
Or, if you'd like to keep the repeating, I'd suggest implementing a custom timer for that, giving you the control over how long you want to wait before the next hit.
function first(Objs) {
var imgid= Objs;
secondMethod(imgid);
}
function secondmethod(imgid) {
var boxid=imgid.id;
var color = getBackgroundColor(imgid.status);//am passing different colors for every refreshing(i.e. #D03C78 or #B8B8B8) values from DataBase
if($('#boxcontent'+boxid).is(':visible')) {
}else{
var boxText = document.createElement("div");
boxText.id='boxcontent'+boxid;
boxText.style.cssText = "white-space:nowrap;text-align:center;border:2px solid ;background-color:"+color+";opacity:0.9;filter:alpha(opacity=90);-moz-border-radius: 5px;border-radius: 5px;color:black !important;";
return boxText;
}
$('#boxcontent'+boxid).css({'background-color':color});
}
this box content is displaying on map.
this box background color not getting in IE,but works in FireFox,
this first function refreshing every 10 seconds with different objts.(for every refreshing changing color).
in IE not working(i.e. color not changing),please help for this problem.
check is it refreshing in every 10 seconds.
you said that for every 10 seconds will get different objects.
IE may get problem with resending data(when refreshing) i.e caching problem.
var color = getBackgroundColor(imgid.status);//am passing different colors for every refreshing(i.e. #D03C78 or #B8B8B8) values from DataBase
alert(imgid.id+" : color is : "+color)
lets check this one in Firefox and IE.
its hard to tell, why IE not setting the color with the information you gave...
might be also a css error or missing commas etc.
But ill try to help you out..
You have a strange mix in JS and JQuery, so i tried to merge this to jQuery, as it
proviedes some X-Browser stuff for IE..
Also i made a class, because its more clean like this.
//Some definition
var Objs = {
id : 'test',
status : "red"
};
//Call
first(Objs);
function first(Objs) {
var imgid= Objs;
secondMethod(imgid);
}
function secondMethod(imgid) {
var boxid = imgid.id;
var color = imgid.status;
if($('#boxcontent' + boxid).length < 1) { //No existing box, make new
var boxText = $("<div></div>")
.attr("id", "boxcontent"+boxid)
.addClass("basciStyleNoColor")
.css({
'background-color' : color,
opacity : 0.9 //<<-- Jquery sets filter for IE...
});
$("body").append(boxText);//<<-Insert here, or retrun and insert later...
return boxText;
}
}
CSS:
.basciStyleNoColor {
white-space:nowrap;
text-align:center;
border:2px solid ;
-moz-border-radius: 5px;
border-radius: 5px;
color:black !important;
}
here the Fiddle.net
hope it helps some how...