I think that I am accidentally refreshing this every time a scroll happens, which is maybe rendering the page on every change.
I think that the issue is that the window.addeventlistener gets called the first time and never unmounts, and then the second time animator is called, it mounts and unmounts it's own event listener, leaving the first in place.
I know useEffect is rendering every time scrollPosition changes, my goal was to stop scrollPosition from changing essentially after the first scroll.
The problem here (aside from any performance issues) is that there are components that have animations, which reset every time the scrollPosition changes.
my question is: How do I stop the rendering after the first time the useEffect runs on the scrollPosition change?
function App() {
const [scrollPosition, setScrollPosition] = useState(0);
const [animating, setAnimating] = useState(false);
const handleScroll = () => {
const position = window.scrollY;
setScrollPosition(position);
};
function animator() {
if (!animating) {
window.addEventListener('scroll', handleScroll, { passive: true });
}
return () => {
window.removeEventListener('scroll', handleScroll);
};
}
useEffect(() => {
animator();
}, [scrollPosition]);
}
I have tried moving the event listener to basically every possible other position, creating new functions to handle it, and used the animating boolean to try and prevent scrollPosition from changing, but alas it has not worked for me yet.
Try using the useMemo hook. This video will explain how you can implement it in your code [https://youtu.be/THL1OPn72vo]
Related
I am using a few Intersection Observers to simply change some classes when certain elements are in the viewport. While it's working, I thought it would be best practice to unobserve these when the elements are not in the viewport, with regard to performance and memory build-up. But, inspecting JS events shows that there are only events fired when the elements are in the viewport, and honestly I'm not sure if that alone is just fine.
Here is the working code I have, but note that if I uncomment observerInfo.unobserve(infoStatement); it no longer works, which is expected, but I am not sure how to implement that unobserve.
const infoStatement = document.querySelector('.statement-container');
const bodyInfo = document.querySelector('body');
const observerInfo = new IntersectionObserver(
function callBackFunction(entries) {
const [entry] = entries;
if (entry.isIntersecting) {
bodyInfo.classList.add('background-dark');
} else {
bodyInfo.classList.remove('background-dark');
//observerInfo.unobserve(infoStatement);
}
},
{ rootMargin: "-10%", }
)
observerInfo.observe(infoStatement);
FIDDLE is here
I also tried a toggle in the if/else statement, but that only added the class and did not remove it:
if (entry.isIntersecting) {
bodyInfo.classList.toggle('background-dark');
} else {
observerInfo.unobserve(infoStatement);
}
How can I achieve this?
I was a little stuck on figuring out how to trigger different functions if the same key was pressed. For instance, if space was pressed, it would call function1. If space was pressed again, it would call function2. If space was called one more time, it will call function3.
I currently have the following code and it seems like my code never enters the last two if statements. Any tips on how to fix this? Thanks in advance!
document.addEventListener("keydown", function (e) {
if (e.code == "") {
function1();
if (e.code == "") {
function2();
}
if (e.code == "") {
function3();
}
}
JavaScript events are what make sure that they trigger a function when something happens. So, if you want to add several functions you have to make sure something happens to trigger them. So if you say you have to run several functions when a key is pressed it has no meaning if you are not binding some other function to that key. Because if the only thing you have is one key pressing event you cannot possibly think of running multiple functions each time that key is pressed. But you can tweak the code a little bit to run multiple functions too.
Here's an example of how to do that.
HTML:
<p class='p'>Hello<p>
CSS:
.red{
background:red;
}
.green{
background:green;
}
.blue{
background:blue;
}
JS:
let v=0
const p = document.querySelector('.p')
document.body.onkeydown= function (e) {
if (e.keyCode == 32) {
switch(v % 3){
case 0:
p.classList.add('red')
p.classList.remove('green')
p.classList.remove('blue')
v += 1
console.log(v)
break
case 1:
p.classList.add('green')
p.classList.remove('red')
p.classList.remove('blue')
v += 1
break
case 2:
p.classList.add('blue')
p.classList.remove('green')
p.classList.remove('red')
v += 1
break
}
}
}
Here, you can see that I am making sure that another function is running hidden to select the functionality I want.
Since your event handler will be called for every single event, you have to use some sort of debounce function. Basically a timer and some logic that keeps track of how many times the event occured in a given time frame.
Let's start with a little helper function that takes care of delaying the call of your 3 event handler functions.
const debounceKeyPress = (keyCode, ...fns) => {
let noOfOccurrences = -1; // we're starting with -1 here, so we don't need to re-calculate for use with index
let timeout;
return (ev) => {
if (ev.keyCode !== keyCode) {
return;
}
noOfOccurrences += 1;
clearTimeout(timeout);
if (fns[noOfOccurrences]) {
timeout = setTimeout(() => { fns[noOfOccurrences](ev); noOfOccurrences = -1; }, 500);
}
}
}
Now you have a general function that takes a key code and a number of functions. The last part is variable. So you can also pass 4 or 5 functions. It returns another function that you can then use as event listener.
const fn1 = (ev) => console.log(`key ${ev.keyCode} was hit once.`);
const fn2 = (ev) => console.log(`key ${ev.keyCode} was hit twice.`);
const fn3 = (ev) => console.log(`key ${ev.keyCode} was hit three times.`);
document.addEventListener('keydown', debounceKeyPress(32, fn1, fn2, fn3));
You can play with the time parameter of the setTimeojut function a bit to see how long or short it actually needs to be. It is also possible to include that as a param of your helper function debounceKeyPress to get more flexibility.
Hope that works for you.
Cheers.
A.
The sample Carto-VL animation control code doesn't appear on my animated map.
I'm creating an animation of tree population change since the last ice age in Carto VL, and I want the user to be able to change the duration and play/pause the animation. I've pasted the code from the example pages into my map.
const $progressRange = document.getElementById('js-progress-range');
const $playButton = document.getElementById('js-play-button');
const $pauseButton = document.getElementById('js-pause-button');
const $durationRange = document.getElementById('js-duration-range');
const $currentTime = document.getElementById('js-current-time');
// Listen to interaction events with the UI
$playButton.addEventListener('click', () => {
viz.variables.animation.play();
});
$pauseButton.addEventListener('click', () => {
viz.variables.animation.pause();
});
$durationRange.addEventListener('change', () => {
viz.variables.duration = parseInt($durationRange.value, 10);
});
// Update progress bar each 100 milliseconds
function updateProgress () {
$progressRange.value = viz.variables.animation.getProgressPct();
$currentTime.innerText = viz.variables.animation.getProgressValue();
}
setInterval(updateProgress, 100);
I expect the animation control box to show up on the map, but it only appears if there's an error in the code causing the map not to appear. Even then, I cannot interact with it.
I am trying to trigger a function which hides or show the images on the basis of data i have written two function one which calls the api which is in created hook and second function which renders the image . The problem is how do i call that second function after the dom is loaded , right now when i am trying to call in the first function or created it is returning me error that css cannot be changed of null.I have tried using mounted function with newtick but its still firing the render_badges function first and hence values are null inside
created:function(){
this.loadlike()
},
methods:{
loadlike:function(){
var self = this
this.$http.get('/api/user_profile').then(function (res) {
self.tasksdata = res.body
self.badges = self.tasksdata.data2
console.log(self.badges)
console.log(this.tasksdata)
console.log(this.max)
})
},
getHumanDate : function (date) {
return moment(date, 'YYYY-MM-DD hh-mm-ss').locale("en-gb").format('LL');
},
render_badges:function(){
var self = this
var counter = 0;
self.badges.map(function(e){
counter ++;
console.log(counter)
if(counter <=self.max){
document.getElementById("i").style.display = "initial";
}
else{
document.getElementById("i").style.display = "none";
}
})
},
mounted: function () {
this.$nextTick(function () {
this.render_badges();
})
}
Rather than manipulating the DOM, you should use v-if on the element itself that turns it on or off based on data. It is a different way of thinking than direct DOM manipulation learned in the jQuery days. Read more in the Vue docs about conditional rendering.
If you are wanting to trigger once the DOM is available, use mounted()
I'm looking for a really simple and lightweight code that change the body background of the website (it can go with the css also) every 10 seconds, it's supposed to be something easy with jquery and css, right?
how can i do that?
You can use JavaScript's setTimeout() function. Here is an example of how to use it.
For setTimeout(), you need 2 parameters, one being your function, and the other an integer, which is how long your timeout will last.
So for your instance, you can do:
$(function() {
var newBg = ['img1.png', 'img2.jpg', 'img3.png'];
var path="img/";
var i = 0;
var changeBg = setTimeout(function() {
$('body').css({backgroundImage : 'url(' path + newBg[i] + ')'});
if(i == newBg.length)
i=0;
else
i++;
}, 10000);
});
This creates an array of your images, specified with a path. Thus I create a variable which checks the length of your arrayList, and whether or not you need to reset the variable i.
So i hope that helps :)
custom-background.js is a lightweight jQuery plugin to allow users change the website’s default background and saves the background selected to local storage. This plugin can easily be added to any website or web app without any compromise on website's performance.
https://github.com/CybrSys/custom-background.js
You can easily do this with jQuery.
A simple javascript function to toggle between two colors every X seconds
function toggle_color(color1,color2,cycle_time,wait_time) {
setInterval(function first_color() {
$("body").animate({
backgroundColor: color1
}, 1000, function () {
setTimeout(change_color, wait_time);
});
}, cycle_time);
function change_color() {
$("body").animate({
backgroundColor: color2
}, 1000, function () {
setTimeout(function () {}, wait_time);
});
}
}
You can check Changing background color every X seconds in Javascript for working demo and explanation.