GPT injecting between content of infinite scroll - javascript

I have created a simple boilerplate to test GPT injection using infinite scroll however when the unit loads its at the bottom of the page, but I need it to load at the bottom of the page and then more content load below it.
I can't see what mistake i have made and the changes i need to make to remedy this. All help is great and thankful.
I want it to show 7,8,9 ad 10,11,12
the code snippet doesn't work correctly so for example use link:
Live test page example
const cardContainer = document.getElementById("card-container");
const cardCountElem = document.getElementById("card-count");
const cardTotalElem = document.getElementById("card-total");
const loader = document.getElementById("loader");
const cardLimit = 99;
const cardIncrease = 9;
const pageCount = Math.ceil(cardLimit / cardIncrease);
let currentPage = 1;
cardTotalElem.innerHTML = cardLimit;
var throttleTimer;
const throttle = (callback, time) => {
if (throttleTimer) return;
throttleTimer = true;
setTimeout(() => {
callback();
throttleTimer = false;
}, time);
};
const getRandomColor = () => {
const h = Math.floor(Math.random() * 360);
return `hsl(${h}deg, 90%, 85%)`;
};
const createCard = (index) => {
const card = document.createElement("div");
card.className = "card";
card.innerHTML = index;
card.style.backgroundColor = getRandomColor();
cardContainer.appendChild(card);
};
const addCards = (pageIndex) => {
currentPage = pageIndex;
const startRange = (pageIndex - 1) * cardIncrease;
const endRange =
currentPage == pageCount ? cardLimit : pageIndex * cardIncrease;
cardCountElem.innerHTML = endRange;
for (let i = startRange + 1; i <= endRange; i++) {
createCard(i);
}
};
const handleInfiniteScroll = () => {
throttle(() => {
const endOfPage =
window.innerHeight + window.pageYOffset >= document.body.offsetHeight;
if (endOfPage) {
moreContent();
addCards(currentPage + 1);
}
if (currentPage === pageCount) {
removeInfiniteScroll();
}
}, 1000);
};
const removeInfiniteScroll = () => {
loader.remove();
window.removeEventListener("scroll", handleInfiniteScroll);
};
window.onload = function () {
addCards(currentPage);
};
window.addEventListener("scroll", handleInfiniteScroll);
body {
font-family: "Roboto", sans-serif;
}
#card-container {
display: flex;
flex-wrap: wrap;
}
.card {
height: 55vh;
width: calc((100% / 3) - 16px);
margin: 8px;
border-radius: 3px;
transition: all 200ms ease-in-out;
display: flex;
align-items: center;
justify-content: center;
}
.card:hover {
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
.card-actions {
margin: 8px;
padding: 16px 0;
display: flex;
justify-content: space-between;
align-items: center;
}
#loader {
display: flex;
}
.skeleton-card {
height: 55vh;
width: calc((100% / 3) - 16px);
margin: 8px;
border-radius: 3px;
transition: all 200ms ease-in-out;
position: relative;
background-color: #eaeaea;
}
.skeleton-card::after {
content: "";
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
transform: translateX(-100%);
background-image: linear-gradient(90deg, rgba(255, 255, 255, 0) 0, rgba(255, 255, 255, 0.2) 20%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0));
animation: load 1s infinite;
}
#keyframes load {
100% {
transform: translateX(100%);
}
}
#media screen and (prefers-reduced-motion: reduce) {
.skeleton-card::after {
animation: none;
}
}
<script src="https://securepubads.g.doubleclick.net/tag/js/gpt.js"></script>
<script src="https://naughty-swanson.78-24-89177.plesk.page/Matrix/test_matrix.js"></script>
<div id="leaderboard">
<script>
// Call display() to register the slot as ready and fetch an ad.
googletag.cmd.push(function() {
googletag.display('leaderboard');
});
</script>
</div>
<div id="card-container">
</div>
<div>
</div>
<div id="loader">
<div class="skeleton-card"></div>
<div class="skeleton-card"></div>
<div class="skeleton-card"></div>
</div>
<div class="card-actions">
<span>Showing
<span id="card-count"></span> of
<span id="card-total"></span> cards
</span>
</div>

Related

How to set a duration for requestAnimationFrame

requestAnimationFrame is good solution to creating javascript based animations. But i can not set a duration for this function. I want to play animations for a certain time. I tried some fps solutions but these are not smooth.
How can i fill this water in x seconds?
const water = document.querySelector('.water')
let scale = 0
const fillGlass = () => {
scale += 0.01
water.style.transform = `scaleY(${scale})`
if (scale <= 1) {
requestAnimationFrame(fillGlass)
}
}
requestAnimationFrame(fillGlass)
.glass {
margin: 100px;
width: 150px;
height: 250px;
border: 3px solid #000;
border-top: 0;
border-bottom-width: 12px
}
.water {
height: 100%;
background-color: #BBDEFB;
transform: scaleY(0);
transform-origin: bottom center;
}
<div class="glass">
<div class="water"></div>
</div>
Simply, you can control it with animation-duration. If you want to add dynamic value, I have just added a parameter to pass value to CSS
.glass {
margin: 100px;
width: 150px;
height: 250px;
border: 3px solid #000;
border-top: 0;
border-bottom-width: 12px
}
.water {
height: 100%;
background-color: #BBDEFB;
transform-origin: bottom center;
animation-name: animation;
animation-duration: var(--value);
}
#keyframes animation {
from { transform:scaleY(0) }
to { transform:scaleY(1) }
}
<div class="glass">
<div class="water" style='--value:20s'></div>
</div>
you can change --value in HTML, it will affect on CSS
As per your need, in JS
const water = document.querySelector('.water')
let scale = 0
let time = 2000 // 20 second * 100
const myInterval = setInterval(()=> {
scale += 1/time
water.style.transform = `scaleY(${scale})`
if (scale >= 1) {
clearInterval(myInterval);
}
}, 10);
.glass {
margin: 100px;
width: 150px;
height: 250px;
border: 3px solid #000;
border-top: 0;
border-bottom-width: 12px
}
.water {
height: 100%;
background-color: #BBDEFB;
transform: scaleY(0);
transform-origin: bottom center;
}
<div class="glass">
<div class="water"></div>
</div>
More Functions
const start = document.querySelector('.start')
const stop = document.querySelector('.stop')
const fill = document.querySelector('.fill')
const empty = document.querySelector('.empty')
const water = document.querySelector('.water')
let scale = 0
let time = 2000 // 20 second * 100
let myInterval;
start.addEventListener('click', () => {
myInterval = setInterval(() => {
scale += 1 / time
water.style.transform = `scaleY(${scale})`
if (scale >= 1) {
clearInterval(myInterval);
}
}, 10);
})
stop.addEventListener('click', () => {
clearInterval(myInterval);
})
fill.addEventListener('click', () => {
scale = 1
water.style.transform = `scaleY(${scale})`
})
empty.addEventListener('click', () => {
scale = 0
water.style.transform = `scaleY(${scale})`
})
.glass {
margin: 100px;
width: 150px;
height: 250px;
border: 3px solid #000;
border-top: 0;
border-bottom-width: 12px
}
.water {
height: 100%;
background-color: #BBDEFB;
transform: scaleY(0);
transform-origin: bottom center;
}
<button type="button" class="start">Start</button>
<button type="button" class="stop">Stop</button>
<button type="button" class="fill">Instant Fill</button>
<button type="button" class="empty">Instant Empty</button>
<div class="glass">
<div class="water"></div>
</div>
Defining simple animation like this is always better with pure CSS, also in this case, it gives you more control over the animation itself.
.water {
height: 100%;
background-color: #BBDEFB;
transform-origin: bottom center;
animation-name: animation;
animation-duration: 1s;
}
#keyframes animation {
from { transform:scaleY(0) }
to { transform:scaleY(1) }
}
For animation and smoothness you can divide the scale value by duration you want
const water = document.querySelector('.water')
let scale = 0
let time = 5000 //in seconds 1000=1sec
let duration = 100/time;
const fillGlass = () => {
scale += duration;
if(scale > 1){
scale = 1;
}
water.style.transform = `scaleY(${scale})`
if (scale <= 1) {
requestAnimationFrame(fillGlass)
}
}
requestAnimationFrame(fillGlass)

memory js game pictures won't close

I found one game on js, and I want to add it to myself by redoing it a bit
https://jsfiddle.net/a7Lx1c98/
So, I want to replace emoji with pictures here, I do this
const emojis = ['https://i.imgur.com/GLS9S5f.jpg', 'https://i.imgur.com/IN9C2qz.jpg', 'https://i.imgur.com/Ke2ubzv.jpg', 'https://i.imgur.com/PbvJDyR.jpg', 'https://i.imgur.com/L3ysai2.jpg',
'https://i.imgur.com/1NxzhTV.jpg', 'https://i.imgur.com/aksV9O3.jpg', 'https://i.imgur.com/gYsZdE4.jpg', 'https://i.imgur.com/LXo6iW3.jpg', 'https://i.imgur.com/wYrEwNR.jpg']
const picks = pickRandom(emojis, (dimensions * dimensions) / 2)
const items = shuffle([...picks, ...picks])
const cards = `
<div class="board" style="grid-template-columns: repeat(${dimensions}, auto)">
${items.map(item => `
<div class="card">
<div class="card-front"></div>
<div class="card-back"><img src="${item}"></div>
</div>
`).join('')}
</div>
`
As a result, the pictures appear, but the game itself does not work, when you select two pictures and they are different, they do not close, but you can choose everything in a row
What could be wrong?
const selectors = {
boardContainer: document.querySelector('.board-container'),
board: document.querySelector('.board'),
moves: document.querySelector('.moves'),
timer: document.querySelector('.timer'),
start: document.querySelector('button'),
win: document.querySelector('.win')
}
const state = {
gameStarted: false,
flippedCards: 0,
totalFlips: 0,
totalTime: 0,
loop: null
}
const shuffle = array => {
const clonedArray = [...array]
for (let index = clonedArray.length - 1; index > 0; index--) {
const randomIndex = Math.floor(Math.random() * (index + 1))
const original = clonedArray[index]
clonedArray[index] = clonedArray[randomIndex]
clonedArray[randomIndex] = original
}
return clonedArray
}
const pickRandom = (array, items) => {
const clonedArray = [...array]
const randomPicks = []
for (let index = 0; index < items; index++) {
const randomIndex = Math.floor(Math.random() * clonedArray.length)
randomPicks.push(clonedArray[randomIndex])
clonedArray.splice(randomIndex, 1)
}
return randomPicks
}
const generateGame = () => {
const dimensions = selectors.board.getAttribute('data-dimension')
if (dimensions % 2 !== 0) {
throw new Error("The dimension of the board must be an even number.")
}
const emojis = ['https://i.imgur.com/GLS9S5f.jpg', 'https://i.imgur.com/IN9C2qz.jpg', 'https://i.imgur.com/Ke2ubzv.jpg', 'https://i.imgur.com/PbvJDyR.jpg', 'https://i.imgur.com/L3ysai2.jpg',
'https://i.imgur.com/1NxzhTV.jpg', 'https://i.imgur.com/aksV9O3.jpg', 'https://i.imgur.com/gYsZdE4.jpg', 'https://i.imgur.com/LXo6iW3.jpg', 'https://i.imgur.com/wYrEwNR.jpg']
const picks = pickRandom(emojis, (dimensions * dimensions) / 2)
const items = shuffle([...picks, ...picks])
const cards = `
<div class="board" style="grid-template-columns: repeat(${dimensions}, auto)">
${items.map(item => `
<div class="card">
<div class="card-front"></div>
<div class="card-back"><img src="${item}"></div>
</div>
`).join('')}
</div>
`
const parser = new DOMParser().parseFromString(cards, 'text/html')
selectors.board.replaceWith(parser.querySelector('.board'))
}
const startGame = () => {
state.gameStarted = true
selectors.start.classList.add('disabled')
state.loop = setInterval(() => {
state.totalTime++
selectors.moves.innerText = `${state.totalFlips} moves`
selectors.timer.innerText = `time: ${state.totalTime} sec`
}, 1000)
}
const flipBackCards = () => {
document.querySelectorAll('.card:not(.matched)').forEach(card => {
card.classList.remove('flipped')
})
state.flippedCards = 0
}
const flipCard = card => {
state.flippedCards++
state.totalFlips++
if (!state.gameStarted) {
startGame()
}
if (state.flippedCards <= 2) {
card.classList.add('flipped')
}
if (state.flippedCards === 2) {
const flippedCards = document.querySelectorAll('.flipped:not(.matched)')
if (flippedCards[0].innerText === flippedCards[1].innerText) {
flippedCards[0].classList.add('matched')
flippedCards[1].classList.add('matched')
}
setTimeout(() => {
flipBackCards()
}, 1000)
}
// If there are no more cards that we can flip, we won the game
if (!document.querySelectorAll('.card:not(.flipped)').length) {
setTimeout(() => {
selectors.boardContainer.classList.add('flipped')
selectors.win.innerHTML = `
<span class="win-text">
You won!<br />
with <span class="highlight">${state.totalFlips}</span> moves<br />
under <span class="highlight">${state.totalTime}</span> seconds
</span>
`
clearInterval(state.loop)
}, 1000)
}
}
const attachEventListeners = () => {
document.addEventListener('click', event => {
const eventTarget = event.target
const eventParent = eventTarget.parentElement
if (eventTarget.className.includes('card') && !eventParent.className.includes('flipped')) {
flipCard(eventParent)
} else if (eventTarget.nodeName === 'BUTTON' && !eventTarget.className.includes('disabled')) {
startGame()
}
})
}
generateGame()
attachEventListeners()
html {
width: 100%;
height: 100%;
background: linear-gradient(325deg, #6f00fc 0%,#fc7900 50%,#fcc700 100%);
font-family: Fredoka;
}
.game {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.controls {
display: flex;
gap: 20px;
margin-bottom: 20px;
}
button {
background: #282A3A;
color: #FFF;
border-radius: 5px;
padding: 10px 20px;
border: 0;
cursor: pointer;
font-family: Fredoka;
font-size: 18pt;
}
.disabled {
color: #757575;
}
.stats {
color: #FFF;
font-size: 14pt;
}
.board-container {
position: relative;
}
.board,
.win {
border-radius: 5px;
box-shadow: 0 25px 50px rgb(33 33 33 / 25%);
background: linear-gradient(135deg, #6f00fc 0%,#fc7900 50%,#fcc700 100%);
transition: transform .6s cubic-bezier(0.4, 0.0, 0.2, 1);
backface-visibility: hidden;
}
.board {
padding: 20px;
display: grid;
grid-template-columns: repeat(4, auto);
grid-gap: 20px;
}
.board-container.flipped .board {
transform: rotateY(180deg) rotateZ(50deg);
}
.board-container.flipped .win {
transform: rotateY(0) rotateZ(0);
}
.card {
position: relative;
width: 100px;
height: 100px;
cursor: pointer;
}
.card-front,
.card-back {
position: absolute;
border-radius: 5px;
width: 100%;
height: 100%;
background: #282A3A;
transition: transform .6s cubic-bezier(0.4, 0.0, 0.2, 1);
backface-visibility: hidden;
}
.card-back {
transform: rotateY(180deg) rotateZ(50deg);
font-size: 28pt;
user-select: none;
text-align: center;
line-height: 100px;
background: #FDF8E6;
}
.card.flipped .card-front {
transform: rotateY(180deg) rotateZ(50deg);
}
.card.flipped .card-back {
transform: rotateY(0) rotateZ(0);
}
.win {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
text-align: center;
background: #FDF8E6;
transform: rotateY(180deg) rotateZ(50deg);
}
.win-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 21pt;
color: #282A3A;
}
.highlight {
color: #6f00fc;
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}
<!DOCTYPE html>
<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">
<title>🧠 Memory Game in JavaScript</title>
<link rel="stylesheet" href="assets/styles.css" />
<script src="assets/game.js" defer></script>
</head>
<body>
<div class="game">
<div class="controls">
<button>Start</button>
<div class="stats">
<div class="moves">0 moves</div>
<div class="timer">time: 0 sec</div>
</div>
</div>
<div class="board-container">
<div class="board" data-dimension="4"></div>
<div class="win">You won!</div>
</div>
</div>
</body>
</html>
Because every pair is a match:
if (flippedCards[0].innerText === flippedCards[1].innerText)
Your elements have no text, so innerText is always an empty string. So any two cards, regardless of their images, match.
Probably the quickest solution is to compare the HTML instead:
if (flippedCards[0].innerHTML === flippedCards[1].innerHTML)
Assuming the rest of the HTML is always the same, the only different should be the src on the <img> element(s).
As an added exercise, you could also look into being more explicit in that comparison. Perhaps give each element a data-* property and compare those instead of relying on the text or HTML. Or perhaps specifically read the src property and compare those values instead of comparing the entire contents of the element(s).
The issue is comparing the innerText of the two cards. There is no inner text, so you can compare the card back image URLs.
const selected = [...flippedCards].map(card => card.querySelector('.card-back img').src);
if (allEqual(selected)) {
flippedCards[0].classList.add('matched')
flippedCards[1].classList.add('matched')
}
I also added an allEqual function to make sure all the items match:
const allEqual = arr => arr.every(v => v === arr[0]);
Working example
const allEqual = arr => arr.every(v => v === arr[0]);
const selectors = {
boardContainer: document.querySelector('.board-container'),
board: document.querySelector('.board'),
moves: document.querySelector('.moves'),
timer: document.querySelector('.timer'),
start: document.querySelector('button'),
win: document.querySelector('.win')
};
const state = {
gameStarted: false,
flippedCards: 0,
totalFlips: 0,
totalTime: 0,
loop: null
};
const shuffle = array => {
const clonedArray = [...array];
for (let index = clonedArray.length - 1; index > 0; index--) {
const randomIndex = Math.floor(Math.random() * (index + 1));
const original = clonedArray[index];
clonedArray[index] = clonedArray[randomIndex];
clonedArray[randomIndex] = original;
}
return clonedArray;
}
const pickRandom = (array, items) => {
const clonedArray = [...array];
const randomPicks = [];
for (let index = 0; index < items; index++) {
const randomIndex = Math.floor(Math.random() * clonedArray.length);
randomPicks.push(clonedArray[randomIndex]);
clonedArray.splice(randomIndex, 1);
}
return randomPicks;
}
const generateGame = () => {
const dimensions = selectors.board.getAttribute('data-dimension');
if (dimensions % 2 !== 0) {
throw new Error("The dimension of the board must be an even number.");
}
const emojis = [
'https://i.imgur.com/GLS9S5f.jpg',
'https://i.imgur.com/IN9C2qz.jpg',
'https://i.imgur.com/Ke2ubzv.jpg',
'https://i.imgur.com/PbvJDyR.jpg',
'https://i.imgur.com/L3ysai2.jpg',
'https://i.imgur.com/1NxzhTV.jpg',
'https://i.imgur.com/aksV9O3.jpg',
'https://i.imgur.com/gYsZdE4.jpg',
'https://i.imgur.com/LXo6iW3.jpg',
'https://i.imgur.com/wYrEwNR.jpg'
];
const picks = pickRandom(emojis, (dimensions * dimensions) / 2);
const items = shuffle([...picks, ...picks]);
const cards = `
<div class="board" style="grid-template-columns: repeat(${dimensions}, auto)">
${items.map(item => `
<div class="card">
<div class="card-front"></div>
<div class="card-back"><img src="${item}"></div>
</div>
`).join('')}
</div>
`;
const parser = new DOMParser().parseFromString(cards, 'text/html');
selectors.board.replaceWith(parser.querySelector('.board'));
}
const startGame = () => {
state.gameStarted = true
selectors.start.classList.add('disabled');
state.loop = setInterval(() => {
state.totalTime++;
selectors.moves.innerText = `${state.totalFlips} moves`;
selectors.timer.innerText = `time: ${state.totalTime} sec`;
}, 1000);
}
const flipBackCards = () => {
document.querySelectorAll('.card:not(.matched)').forEach(card => {
card.classList.remove('flipped');
})
state.flippedCards = 0;
}
const flipCard = card => {
state.flippedCards++;
state.totalFlips++;
if (!state.gameStarted) {
startGame();
}
if (state.flippedCards <= 2) {
card.classList.add('flipped');
}
if (state.flippedCards === 2) {
const flippedCards = document.querySelectorAll('.flipped:not(.matched)')
const selected = [...flippedCards].map(card => card.querySelector('.card-back img').src);
if (allEqual(selected)) {
flippedCards[0].classList.add('matched')
flippedCards[1].classList.add('matched')
}
setTimeout(() => {
flipBackCards();
}, 1000);
}
// If there are no more cards that we can flip, we won the game
if (!document.querySelectorAll('.card:not(.flipped)').length) {
setTimeout(() => {
selectors.boardContainer.classList.add('flipped');
selectors.win.innerHTML = `
<span class="win-text">
You won!<br />
with <span class="highlight">${state.totalFlips}</span> moves<br />
under <span class="highlight">${state.totalTime}</span> seconds
</span>
`;
clearInterval(state.loop);
}, 1000);
}
}
const attachEventListeners = () => {
document.addEventListener('click', event => {
const eventTarget = event.target;
const eventParent = eventTarget.parentElement;
if (eventTarget.className.includes('card') && !eventParent.className.includes('flipped')) {
flipCard(eventParent);
} else if (eventTarget.nodeName === 'BUTTON' && !eventTarget.className.includes('disabled')) {
startGame();
}
})
}
generateGame();
attachEventListeners();
html {
width: 100%;
height: 100%;
background: linear-gradient(325deg, #6f00fc 0%, #fc7900 50%, #fcc700 100%);
font-family: Fredoka;
}
.game {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.controls {
display: flex;
gap: 20px;
margin-bottom: 20px;
}
button {
background: #282A3A;
color: #FFF;
border-radius: 5px;
padding: 10px 20px;
border: 0;
cursor: pointer;
font-family: Fredoka;
font-size: 18pt;
}
.disabled {
color: #757575;
}
.stats {
color: #FFF;
font-size: 14pt;
}
.board-container {
position: relative;
}
.board,
.win {
border-radius: 5px;
box-shadow: 0 25px 50px rgb(33 33 33 / 25%);
background: linear-gradient(135deg, #6f00fc 0%, #fc7900 50%, #fcc700 100%);
transition: transform .6s cubic-bezier(0.4, 0.0, 0.2, 1);
backface-visibility: hidden;
}
.board {
padding: 20px;
display: grid;
grid-template-columns: repeat(4, auto);
grid-gap: 20px;
}
.board-container.flipped .board {
transform: rotateY(180deg) rotateZ(50deg);
}
.board-container.flipped .win {
transform: rotateY(0) rotateZ(0);
}
.card {
position: relative;
width: 100px;
height: 100px;
cursor: pointer;
}
.card-front,
.card-back {
position: absolute;
border-radius: 5px;
width: 100%;
height: 100%;
background: #282A3A;
transition: transform .6s cubic-bezier(0.4, 0.0, 0.2, 1);
backface-visibility: hidden;
}
.card-back {
transform: rotateY(180deg) rotateZ(50deg);
font-size: 28pt;
user-select: none;
text-align: center;
line-height: 100px;
background: #FDF8E6;
}
.card.flipped .card-front {
transform: rotateY(180deg) rotateZ(50deg);
}
.card.flipped .card-back {
transform: rotateY(0) rotateZ(0);
}
.win {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
text-align: center;
background: #FDF8E6;
transform: rotateY(180deg) rotateZ(50deg);
}
.win-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 21pt;
color: #282A3A;
}
.highlight {
color: #6f00fc;
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}
<div class="game">
<div class="controls">
<button>Start</button>
<div class="stats">
<div class="moves">0 moves</div>
<div class="timer">time: 0 sec</div>
</div>
</div>
<div class="board-container">
<div class="board" data-dimension="4"></div>
<div class="win">You won!</div>
</div>
</div>

<a class='gotoLine' href='#230:45'>230:45</a> Uncaught TypeError :

"230:45 Uncaught TypeError: Cannot read properties of undefined (reading 'shouldStopExecution')"
I got the following error can you fix it. I am trying to add HTML, CSS, and javascript on the same page.
I found this code in codepen i am trying to solve the issue but it's not working anymore...
But javascript not working here...
<div class="wrapper">
<div class="poll-box">
<div class="poll-container">
<div class="poll-question">Do You Love to Play FreeFire?</div>
<div class="poll-panel row mt-30">
<div class="btn poll-panel-btn" aria-role="button" data-result="0" data-vote="0"> <span>Yes</span></div>
<div class="btn poll-panel-btn" aria-role="button" data-result="0" data-vote="1"> <span>No</span></div>
</div>
</div>
</div>
</div>
<style>* {
box-sizing: border-box;
}
body {
background: #1b1b1b;
margin: 0;
}
.wrapper {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.mt-30 {
margin: 30px 0 0 0;
}
.row, .column {
display: flex;
}
.row {
flex-direction: row;
}
.column {
flex-direction: column;
}
.btn:not([disabled]) {
cursor: pointer;
}
.poll-box {
background: linear-gradient(#803af7, #500cc4);
border-radius: 3px;
box-shadow: 0px 5px 11px -7px #000;
text-align: center;
}
.poll-container {
padding: 25px 30px;
position: relative;
}
.poll-question {
width: max-content;
max-width: 700px;
color: #FFF;
font-family: "Poppins", sans-serif;
}
.poll-panel.poll-voted {
overflow: hidden;
border-radius: 50px;
}
.poll-panel.poll-voted .poll-panel-btn.--user-choice {
background: #FFF;
color: #000;
}
.poll-panel.poll-voted .poll-panel-btn.--user-choice:hover {
color: #000;
background: #FFF;
}
.poll-panel.poll-voted .poll-panel-btn {
background: #676464;
color: #FFF;
border-radius: 0;
margin: 0;
border: 0;
position: relative;
}
.poll-panel.poll-voted .poll-panel-btn:hover {
color: #FFF;
background: #676464;
}
.poll-panel.poll-voted .poll-panel-btn:after {
content: attr(data-result);
font-size: 9px;
display: block;
opacity: 0.5;
animation: slideWithFade 0.2s ease;
}
.poll-panel.poll-voted .poll-panel-btn:active {
transform: inherit;
}
.poll-panel.poll-voted .poll-panel-btn span {
display: block;
}
.poll-panel {
width: 100%;
}
.poll-panel-btn {
padding: 7px 10px;
font-family: "Roboto", sans-serif;
font-size: 0.7rem;
width: 100%;
border-radius: 50px;
border: 1px solid #FFF;
margin: 0 20px;
color: #FFF;
transition: 0.15s cubic-bezier(0.17, 0.67, 0.79, 1.24);
}
.poll-panel-btn:hover {
background: #FFF;
color: #000;
}
.poll-panel-btn:active {
transform: scale(0.95);
}
#keyframes slideWithFade {
0% {
opacity: 0;
transform: translateY(10px);
}
100% {
opacity: 1;
}
}</style>
<script>// POLL PLUGIN
class poll {
constructor(question, answers, options) {
const defaultOptions = {};
this.options = Object.assign({}, defaultOptions, options);
this.history = [];
this.possibleAnswers = answers;
}
clear() {
this.history = [];
}
get results() {
let numberOfVotes = this.history.length,
votesResults = [];
Object.keys(this.possibleAnswers).forEach(answerId => {
let answerIdCounter = 0;
let voters = [];
this.history.forEach(vote => {
if (answerId == vote.id) {
answerIdCounter++;
voters.push(vote.name);
}
});
let percentOfAllVotes = answerIdCounter / numberOfVotes * 100;
let formatedPercent = isNaN(percentOfAllVotes) ?
0 :
parseFloat(percentOfAllVotes).
toFixed(3).
slice(0, -1);
votesResults.push({
votes: answerIdCounter,
voters: voters,
percent: formatedPercent });
});
return votesResults;
}
vote(answerId, name = "Anonymouse") {
if (this.possibleAnswers[answerId]) {
let getCurrentDate = new Date().toLocaleString();
this.history.push({ id: answerId, name: name, date: getCurrentDate });
return true;
} else throw new Error("Incorrect answer's id");
}}
// Plugin: https://codepen.io/badurski/pen/RJvJQZ
const q1 = new poll("Will Poland win the footboal match?", {
0: { title: "Yes" },
1: { title: "No" } });
// Add some randome votes
for (let i = 0; i < 20; i++) {if (window.CP.shouldStopExecution(0)) break;
q1.vote(Math.floor(Math.random() * (1 - 0 + 1)) + 0);
}
// Poll interface script
window.CP.exitedLoop(0);let pollButtons = document.querySelectorAll('.poll-panel-btn'),
pollPanel = document.querySelector('.poll-panel');
pollButtons.forEach(button => {
button.onclick = () => {
if (button.getAttribute('disabled') != 'disabled') {
q1.vote(button.dataset.vote);
pollPanel.classList.add('poll-voted');
button.classList.add('--user-choice');
pollButtons.forEach(b => {
b.setAttribute('disabled', 'disabled');
let percent = q1.results[b.dataset.vote].percent + '%';
b.style.width = percent;
b.dataset.result = percent;
});
}
};
});</script>
In codepen everything is separated but in your HTML you should put the scripts before the divs.
Something like:
<script>
//blabla.yourscripts - you got it
</script>
<div class="wrapper">
<div class="poll-box">
<div class="poll-container">
<div class="poll-question">Do You Love to Play FreeFire?</div>
<div class="poll-panel row mt-30">
<div class="btn poll-panel-btn" aria-role="button" data-result="0" data-vote="0"> <span>Yes</span></div>
<div class="btn poll-panel-btn" aria-role="button" data-result="0" data-vote="1"> <span>No</span></div>
</div>
</div>
</div>
</div>
So just put the scripts before the stuff reading them.
You have included CP which is the CodePan component.
Please remove window.CP lines, then it will work:
window.CP.exitedLoop(0);
if (window.CP.shouldStopExecution(0)) break;
<script>
// POLL PLUGIN
class poll {
constructor(question, answers, options) {
const defaultOptions = {};
this.options = Object.assign({}, defaultOptions, options);
this.history = [];
this.possibleAnswers = answers;
}
clear() {
this.history = [];
}
get results() {
let numberOfVotes = this.history.length,
votesResults = [];
Object.keys(this.possibleAnswers).forEach(answerId => {
let answerIdCounter = 0;
let voters = [];
this.history.forEach(vote => {
if (answerId == vote.id) {
answerIdCounter++;
voters.push(vote.name);
}
});
let percentOfAllVotes = answerIdCounter / numberOfVotes * 100;
let formatedPercent = isNaN(percentOfAllVotes) ?
0 :
parseFloat(percentOfAllVotes).
toFixed(3).
slice(0, -1);
votesResults.push({
votes: answerIdCounter,
voters: voters,
percent: formatedPercent });
});
return votesResults;
}
vote(answerId, name = "Anonymouse") {
if (this.possibleAnswers[answerId]) {
let getCurrentDate = new Date().toLocaleString();
this.history.push({ id: answerId, name: name, date: getCurrentDate });
return true;
} else throw new Error("Incorrect answer's id");
}}
// Plugin: https://codepen.io/badurski/pen/RJvJQZ
const q1 = new poll("Will Poland win the footboal match?", {
0: { title: "Yes" },
1: { title: "No" } });
// Add some randome votes
for (let i = 0; i < 20; i++) {
q1.vote(Math.floor(Math.random() * (1 - 0 + 1)) + 0);
}
// Poll interface script
let pollButtons = document.querySelectorAll('.poll-panel-btn'),
pollPanel = document.querySelector('.poll-panel');
pollButtons.forEach(button => {
button.onclick = () => {
if (button.getAttribute('disabled') != 'disabled') {
q1.vote(button.dataset.vote);
pollPanel.classList.add('poll-voted');
button.classList.add('--user-choice');
pollButtons.forEach(b => {
b.setAttribute('disabled', 'disabled');
let percent = q1.results[b.dataset.vote].percent + '%';
b.style.width = percent;
b.dataset.result = percent;
});
}
};
});
</script>

flip scroll when scrolling

We need advice on how to do what would happen with normal scrolling of the page (with a wheel, or in a mob with a finger) after the block has been screwed up to the top of the screen, it began to scroll horizontally and after the edge of the block is reached, the standard scroll continues, respectively, if the scroll goes up, then that's it in reverse order. (There may be several such blocks on a page)
* {
box-sizing: border-box;
margin: 0;
}
body {
overflow-x: hidden;
}
.simple {
height: 100vh;
background: #1f69c0;
border-bottom: 2px solid #777;
}
.simple2 {
height: 400px;
background: #EEAA07;
border-bottom: 2px solid #777;
}
.simple3 {
height: 400px;
background: #07eed9;
border-bottom: 2px solid #777;
}
.outer {
height: 150px;
background: #7b8e39;
overflow-y: hidden;
overflow-x: scroll;
}
.inner {
height: 100%;
margin: 0 100px;
display: flex;
}
.cube {
min-width: 200px;
height: 100px;
background: #f6f6f6;
margin: 10px;
display: flex;
align-items: center;
justify-content: center;
}
<div class="simple"></div>
<div class="outer">
<div class="inner">
<div class="cube">1</div>
<div class="cube">2</div>
<div class="cube">3</div>
<div class="cube">4</div>
<div class="cube">5</div>
<div class="cube">6</div>
<div class="cube">7</div>
<div class="cube">8</div>
<div class="cube">9</div>
<div class="cube">10</div>
</div>
</div>
<div class="simple2"></div>
<div class="simple3"></div>
Here is an approximate structure, until block 2 has scrolled to the end, block 3 should be visible and after 2 has scrolled horizontally to the end, the standard scroll will continue
PS Here is an example (https://horizontalscrolling.wpdemos.net/horizontal-scrolling/) of how scrolling should work, although there can be more than 1 block on one screen
This technique needs to use javascript called "GSAP" which is a high-performance javascript animation library.
I found a similar animation that you were looking for.
Hopefully, this will help :)
//https://codepen.io/osublake/pen/e72106811a34efcccff91a03568cc790.js?v=3
class SmoothScroll {
constructor(options) {
this.endThreshold = 0.05;
this.requestId = null;
this.maxDepth = 10;
this.viewHeight = 0;
this.halfViewHeight = 0;
this.maxDistance = 0;
this.viewWidth = 0;
this.halfViewWidth = 0;
this.maxDistanceWidth = 0;
this.scrollHeight = 0;
this.endScroll = 0;
this.returnCurrentScroll = 0;
this.currentScroll = 0;
this.scrollTransform = 0;
this.horizontalScroll = 0;
this.resizeRequest = 1;
this.scrollRequest = 0;
this.scrollItems = [];
this.lastTime = -1;
this.maxElapsedMS = 100;
this.targetFPMS = 0.06;
// this.scrollBody = options.scrollBody;
// this.scrollSpacer = options.scrollSpacer;
this.target = options.target;
this.scrollEase = options.scrollEase != null ? options.scrollEase : 0.1;
this.maxOffset = options.maxOffset != null ? options.maxOffset : 500;
this.horizontalScrollWrapper = options.horizontalScrollWrapper;
this.horizontalScrollTarget = options.horizontalScrollTarget;
this._horziontalSetHeihgt();
this.childElements = this._childElements();
this.rectHorStart = this.horizontalScrollWrapper.getBoundingClientRect();
this.horzItemStart = {
top: this.rectHorStart.top,
bottom: this.rectHorStart.bottom,
height: this.rectHorStart.height
}
this.addItems();
window.addEventListener("resize", this._onResize);
window.addEventListener("scroll", this._onScroll);
//this.scrollBody.addEventListener("scroll", this._onScroll);
this._update();
}
_childElements = (event) => {
const childElementsNode = this.target.querySelectorAll("*[data-color]");
return childElementsNode;
}
_horizonstalScrollRect = (event) => {
const horzintalRect = this.horizontalScrollTarget.getBoundingClientRect();
return horzintalRect;
}
_lastScrollRect = (event) => {
const lastScrollRect = this.horizontalScrollTarget.lastElementChild.getBoundingClientRect();
return lastScrollRect;
}
_horziontalSetHeihgt = (event) => {
let horScrHeight = 0;
if (
this.horizontalScrollTarget !== null &&
this.horizontalScrollWrapper !== null
) {
const lastScrollRect = this._lastScrollRect();
horScrHeight = this.horizontalScrollTarget.scrollWidth - lastScrollRect.width + this._horizonstalScrollRect().height;
this.horizontalScrollWrapper.style.height = horScrHeight + "px";
}
}
_onResize = (event) => {
this.resizeRequest++;
if (!this.requestId) {
this.lastTime = performance.now();
this.requestId = requestAnimationFrame(this._update);
}
};
_onScroll = (event) => {
this.scrollRequest++;
if (!this.requestId) {
this.lastTime = performance.now();
this.requestId = requestAnimationFrame(this._update);
}
};
_horizonstalScroll = (scrollY,dt) => {
if (this.horizontalScrollWrapper !== null) {
const rectHor = this.horizontalScrollWrapper.getBoundingClientRect();
const lastScrollRect = this._lastScrollRect();
const itemHor = {
target: this.horizontalScrollTarget,
targetRect: this._horizonstalScrollRect(),
top: rectHor.top,
bottom: rectHor.bottom + scrollY,
topScroll: rectHor.top + scrollY,
horizonstalMove: 0,
};
itemHor.horizonstalMove += this.currentScroll - this.horzItemStart.top;
if(scrollY >= this.horzItemStart.top && scrollY <= this.horzItemStart.bottom - itemHor.targetRect.height){
itemHor.target.style.position = 'fixed';
itemHor.target.style.transform = `translate3d(-${itemHor.horizonstalMove}px,0px,0px)`;
//this._paralaxHorizontal(dt);
if(lastScrollRect.x <= (lastScrollRect.width/2)){
this.scrollTransform = this.horzItemStart.bottom - itemHor.targetRect.height;
itemHor.target.style.top = this.horzItemStart.bottom - itemHor.targetRect.height+'px';
}else {
this.scrollTransform = this.horzItemStart.top;
itemHor.target.style.top = this.horzItemStart.top+'px';
}
}
}
};
_changeColorBody = (event) => {
if(this.childElements.length > 0){
this.childElements.forEach(child => {
const wrapper = document.querySelector('.change_color_page');
const childRect = child.getBoundingClientRect();
const childAttr = child.getAttribute('data-color');
if(childRect.y <= this.halfViewHeight && childRect.bottom >= this.halfViewHeight){
if(childAttr == "off_white"){
if(!document.body.classList.contains('white')){
document.body.classList.add('white');
}
if(!wrapper.classList.contains('white')){
wrapper.classList.add('white');
}
}else if(childAttr == "dark"){
if(document.body.classList.contains('white')){
document.body.classList.remove('white');
}
if(wrapper.classList.contains('white')){
wrapper.classList.remove('white');
}
}
}
});
}
}
_update = (currentTime = performance.now()) => {
let elapsedMS = currentTime - this.lastTime;
if (elapsedMS > this.maxElapsedMS) {
elapsedMS = this.maxElapsedMS;
}
const deltaTime = elapsedMS * this.targetFPMS;
const dt = 1 - Math.pow(1 - this.scrollEase, deltaTime);
const resized = this.resizeRequest > 0;
const scrollY = window.pageYOffset;
//const scrollY = this.scrollBody.scrollTop;
if (resized) {
this._horziontalSetHeihgt();
const height = this.target.clientHeight;
document.body.style.height = height + "px";
//this.scrollSpacer.style.height = height + "px";
this.scrollHeight = height;
this.viewHeight = window.innerHeight;
this.halfViewHeight = this.viewHeight / 2;
this.maxDistance = this.viewHeight * 2;
this.resizeRequest = 0;
this.viewWidth = window.innerWidth;
this.halfViewWidth = this.viewWidth / 2;
this.maxDistanceWidth = this.viewWidth * 2;
}
this.endScroll = scrollY;
// this.scrollTransform += (scrollY - this.scrollTransform) * this.scrollEase;
this.scrollTransform += (scrollY - this.scrollTransform) * dt;
this.currentScroll += (scrollY - this.currentScroll) * dt;
if (Math.abs(scrollY - this.currentScroll) < this.endThreshold || resized) {
this.currentScroll = scrollY;
this.scrollRequest = 0;
}
if (
Math.abs(scrollY - this.scrollTransform) < this.endThreshold ||
resized
) {
this.scrollTransform = scrollY;
this.scrollRequest = 0;
}
///change color section
this._changeColorBody();
///horizontal scroll
this._horizonstalScroll(this.currentScroll,dt);
// const scrollOrigin = scrollY + this.halfViewHeight;
const scrollOrigin = this.currentScroll + this.viewHeight;
this.target.style.transform = `translate3d(0px,-${this.scrollTransform}px,0px)`;
//items
for (let i = 0; i < this.scrollItems.length; i++) {
const item = this.scrollItems[i];
const distance = scrollOrigin - item.top;
const offsetRatio = distance / this.maxDistance;
item.endOffset = Math.round(
this.maxOffset * item.depthRatio * offsetRatio
);
if (Math.abs(item.endOffset - item.currentOffset < this.endThreshold)) {
item.currentOffset = item.endOffset;
} else {
// item.currentOffset += (item.endOffset - item.currentOffset) * this.scrollEase;
item.currentOffset += (item.endOffset - item.currentOffset) * dt;
}
if(item.direction == "y"){
item.target.style.transform = `translate3d(0px,${item.currentOffset}px,0px)`;
}else if(item.direction == "x"){
item.target.style.transform = `translate3d(${item.currentOffset}px,0px,0px)`;
}
}
this.lastTime = currentTime;
this.requestId =
this.scrollRequest > 0 ? requestAnimationFrame(this._update) : null;
};
addItems() {
this.scrollItems = [];
const elements = document.querySelectorAll("*[data-depth]");
for (let i = 0; i < elements.length; i++) {
const element = elements[i];
const depth = +element.getAttribute("data-depth");
const direction_item = element.getAttribute("data-direction");
const rect_item = element.getBoundingClientRect();
const item = {
rect: rect_item,
target: element,
top: rect_item.top + window.pageYOffset,
//top: rect_item.top + this.scrollBody.scrollTop,
depth: depth,
depthRatio: depth / this.maxDepth,
currentOffset: 0,
endOffset: 0,
direction: direction_item
};
this.scrollItems.push(item);
}
return this;
}
currentScrollReturn() {
return this.currentScroll;
}
}
document.documentElement.style.setProperty(
"--scrollbar-size",
getScrollbarSize() + "px"
);
var scroller = new SmoothScroll({
// scrollBody: document.querySelector(".scroll-content"),
// scrollSpacer: document.querySelector(".spacer"),
target: document.querySelector(".scroll-container"), // element container to scroll
scrollEase: 0.05,
horizontalScrollWrapper: document.querySelector(".horizontal-scroll-wrapper"),
horizontalScrollTarget: document.querySelector(".horizontal-scroll")
});
function getScrollbarSize() {
var div = document.createElement("div");
div.classList.add("scrollbar-test");
document.body.appendChild(div);
var size = div.offsetWidth - div.scrollWidth;
document.body.removeChild(div);
return size;
}
$white: #fbe8ee;
$black: #0a0a0a;
:root {
--scrollbar-size: 0px;
}
*, :after, :before {
box-sizing: border-box;
}
body {
}
.viewport {
overflow: hidden;
position: fixed;
height: 100%;
width: 100%;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
body {
overflow-x: hidden;
overflow-y: scroll;
padding: 0;
margin: 0;
font-family: "Courier New", Courier, monospace;
*:not(.change_color_page) {
color: $white;
transition: color 0.5s ease-in-out, border-color 0.5s ease;
border-color: $white;
}
&.white {
*:not(.change_color_page) {
color: $black;
transition: color 0.5s ease-in-out, border-color 0.5s ease;
border-color: $black;
}
}
}
.change_color_page {
position: fixed;
display: block;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: $black;
transition: background-color 0.5s ease;
backface-visibility: hidden;
transform-style: preserve-3d;
&.white {
background-color: $white;
}
}
.header {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.5s ease-in-out;
background-color: $white;
&.white {
background-color: $black;
}
}
.scrollbar-test {
position: absolute;
visibility: hidden;
overflow: scroll;
width: 100px;
height: 100px;
top: -99999px;
left: -99999px;
pointer-events: none;
user-select: none;
}
.fixed-content {
position: absolute;
display: block;
top: 0;
left: 0;
right: var(--scrollbar-size, 0px);
bottom: 0;
z-index: 2;
pointer-events: none;
}
.scroll-container {
position: absolute;
overflow: hidden;
z-index: 10;
backface-visibility: hidden;
transform-style: preserve-3d;
width: 100%;
}
.content {
overflow: hidden;
position: relative;
width: 100%;
}
.spacer {
background: transparent;
}
.single-item {
flex: 0 0 100vw;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
font-size: 70px;
&.left {
justify-content: flex-start;
}
p {
width: 300px;
height: 300px;
display: flex;
align-items: center;
justify-content: center;
border-width: 2px;
border-style: solid;
}
}
.horizontal-scroll-wrapper {
position: relative;
}
.horizontal-scroll {
display: flex;
}
.horizontal-scroll .single-item {
flex: 0 0 100vw;
p {
width: 600px;
height: 600px;
max-width: 80%;
max-height: 80%;
}
}
<div class="change_color_page"></div>
<header></header>
<div class="viewport">
<div class="scroll-container">
<div class="content">
<div class="single-item active" data-color="off_white">
<p>1</p>
</div>
<div class="single-item" data-color="dark">
<p data-depth="-7" data-direction="y">2</p>
</div>
<div class="single-item" data-color="off_white">
<p class="item_to_move">3</p>
</div>
<div class="single-item" data-color="dark">
<p data-depth="-3" data-direction="y">4</p>
</div>
<div class="single-item" data-color="off_white">
<p data-depth="-3" data-direction="y">5</p>
</div>
<div class="horizontal-scroll-wrapper" data-color="dark">
<div class="horizontal-scroll">
<div class="single-item">
<p><span data-depth-hor="-3" data-direction="left">6</span></p>
</div>
<div class="single-item">
<p><span data-depth-hor="-3" data-direction="left">7</span></p>
</div>
<div class="single-item">
<p><span data-depth-hor="-3" data-direction="left">8</span></p>
</div>
<div class="single-item">
<p><span data-depth-hor="-3" data-direction="left">9</span></p>
</div>
<div class="single-item">
<p><span data-depth-hor="-3" data-direction="left">10</span></p>
</div>
</div>
</div>
<div class="single-item" data-color="off_white">
<p data-depth="-3" data-direction="y">11</p>
</div>
<div class="single-item" data-color="dark">
<p data-depth="-3" data-direction="y">12</p>
</div>
<div class="single-item left" data-color="off_white">
<p data-depth="15" data-direction="x">13</p>
</div>
</div>
</div>
</div>
source code from: https://codepen.io/duty47/pen/vYYEgam

Why blocked pointer events are fired while timed-out function with no delay is running?

Why after clicking the load button, both buttons load and dummy covered by the loader's overlay still register clicks?
Sometimes when clicking the load button, the loader is not even displayed.
Buttons correctly don't register clicks
if we for example display the loader from the start by commenting the line 5 loader.hide();
add some timeout delay (but I don't want that)
Example (best to run in Full Page mode):
const iterations = 1e3;
const multiplier = 1e9;
const loader = $('.css-loader-fullscreen');
const dummyBtn = $('#dummy');
const loadBtn = $('#load');
loader.hide();
dummyBtn.on('click', () => console.log('dummy clicked'));
loadBtn.on('click', jsHeavyTask);
function calculatePrimes(iterations, multiplier) {
var primes = [];
for (var i = 0; i < iterations; i++) {
var candidate = i * (multiplier * Math.random());
var isPrime = true;
for (var c = 2; c <= Math.sqrt(candidate); ++c) {
if (candidate % c === 0) {
// not prime
isPrime = false;
break;
}
}
if (isPrime) {
primes.push(candidate);
}
}
return primes;
}
function jsHeavyTask(){
console.log('heavy function started');
loader.show();
setTimeout(() => {
const start = performance.now();
calculatePrimes(iterations, multiplier);
const end = performance.now();
loader.hide();
console.log('heavy function ended in '+ (end - start).toFixed() +' ms');
});
}
input {width: 150px}
.css-loader-background {
display: flex;
align-items: center;
justify-content: center;
background-color: white;
border-radius: 10px;
font-size: 12px;
}
.css-loader-fullscreen {
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 100000;
position: fixed;
background-color: rgba(0, 0, 0, 0.4);
display: flex;
justify-content: center;
align-items: center;
}
.css-loader-fullscreen .css-loader-background {
width: 100px;
height: 100px;
}
.css-loader-animation {
width: 40px;
height: 40px;
border-radius: 50%;
border: 8px solid transparent;
border-top-color: purple;
border-bottom-color: purple;
text-indent: -9999em;
animation: spinner 0.8s ease infinite;
transform: translateZ(0);
}
#-webkit-keyframes spinner {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div class="css-loader-fullscreen">
<div class="css-loader-background">
<div class="css-loader-animation"></div>
</div>
</div>
<input id="load" type="button" value="load">
<input id="dummy" type="button" value="dummy">
Run times of jsHeavyTask() are different on every machine. For me it's around 5s. You can change iterations and multiplier constants to modify the run time.
There is lot more of weird I observed related to this no delay timed-out calc-heavy function, especially in Webkit, but first I am curious about this one.
as first I would try to call the heavy function differently:
loadBtn.on('click', () => {
loader.show();
jsHeavyTask
});
If that doesn't do the trick, I would try different approach with the show/hide method and use opacity with ponter-events combination for better performance and disabling passing through the clicks.
JavaScript
const loader = $('.css-loader-fullscreen');
const dummyBtn = $('#dummy');
const loadBtn = $('#load');
const content = $('#content');
const cssHidden = 'css-hidden';
const cssLoading = 'css-loading';
loader.addClass(cssHidden);
dummyBtn.on('click', () => console.log('dummy clicked'));
loadBtn.on('click', () => {
content.addClass(cssLoading);
loader.removeClass(cssHidden);
jsHeavyTask
});
function jsHeavyTask(){
console.log('heavy function started');
setTimeout(() => {
for ( var i = 0; i < 2e7; i++){
Math.sqrt(Date.now());
}
loader.addClass(cssHidden);
content.removeClass(cssLoading);
console.log('heavy function ended');
});
}
CSS (only changes)
.css-loader-fullscreen {
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 100000;
position: fixed;
background-color: rgba(0, 0, 0, 0.4);
display: flex;
justify-content: center;
align-items: center;
opacity: 99.99999;
pointer-events: auto;
}
.css-hidden {
opacity: 0.000001;
pointer-events: none;
}
.css-loading {
pointer-events: none;
}
HTML
<div class="css-loader-fullscreen">
<div class="css-loader-background">
<div class="css-loader-animation"></div>
</div>
</div>
<div id="content">
<input id="load" type="button" value="load">
<input id="dummy" type="button" value="dummy">
</div>
Sometimes when clicking the load button, the loader is not even displayed.
Obviously because of this setTimeout function
setTimeout(() => {
for (var i = 0; i < 2e7; i++) {
Math.sqrt(Date.now());
}
console.log('heavy function ended');
});
The setTimeout makes your code, kind of async, so the inside runs smoothly and with the provided delay. Here, you have almost no delay, it's just a simple timeout that runs instantly, the functions inside as there's little to no delay value.
On the other hand, the for loop has dynamic run times.
var t0 = performance.now();
for (var i = 0; i < 2e7; i++) {
Math.sqrt(Date.now());
}
var t1 = performance.now();
console.log("Took " + (t1 - t0) + " milliseconds.");
So sometimes it runs fast enough and that means that your loader.hide(); runs instantly and hides your overlay and sometimes not so you see the loader.
Why after clicking the load button, both buttons covered by the loader's overlay still register clicks?
I don't know what you mean there but if you mean that you can click as the overlay displays then no, try debugging by not allowing the loaders to go and then click the other button, you'll notice that the click is not registered.
const loader = $('.css-loader-fullscreen');
const dummyBtn = $('#dummy');
const loadBtn = $('#load');
loader.hide();
dummyBtn.on('click', () => console.log('dummy clicked'));
loadBtn.on('click', jsHeavyTask);
function jsHeavyTask(){
console.log('heavy function started');
loader.show();
setTimeout(() => {
for ( var i = 0; i < 2e6; i++){
Math.sqrt(Date.now());
}
// loader.hide();
console.log('heavy function ended');
});
}
input {width: 150px}
.css-loader-background {
display: flex;
align-items: center;
justify-content: center;
background-color: white;
border-radius: 10px;
font-size: 12px;
}
.css-loader-fullscreen {
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 100000;
position: fixed;
background-color: rgba(0, 0, 0, 0.4);
display: flex;
justify-content: center;
align-items: center;
}
.css-loader-fullscreen .css-loader-background {
width: 100px;
height: 100px;
}
.css-loader-animation {
width: 40px;
height: 40px;
border-radius: 50%;
border: 8px solid transparent;
border-top-color: purple;
border-bottom-color: purple;
text-indent: -9999em;
animation: spinner 0.8s ease infinite;
transform: translateZ(0);
}
#-webkit-keyframes spinner {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div class="css-loader-fullscreen">
<div class="css-loader-background">
<div class="css-loader-animation"></div>
</div>
</div>
<input id="load" type="button" value="load">
<input id="dummy" type="button" value="dummy">
NOTE: Some of your stated behaviors are inconsistent with the given observations in the questions, I suggest you adequately validate them.
It simply seems that the browser is memorizing all the clicks while the JS calculation is running. But those clicks are not applied to the painted layout existing before AND during the heavy JS calculation. They are applied to the repainted layout which includes changes introduced while the JS calculation was running. So no loader overlay to catch those clicks.
I would expect the clicks to be applied to the original existing layout, when the loader overlay is still displayed.
edit: adding a correct solution below without a need to add some delay
const iterations = 1e3;
const multiplier = 1e9;
const loader = $('.css-loader-fullscreen');
const dummyBtn = $('#dummy');
const loadBtn = $('#load');
loader.hide();
dummyBtn.on('click', () => console.log('dummy clicked'));
loadBtn.on('click', jsHeavyTask);
function calculatePrimes(iterations, multiplier) {
var primes = [];
for (var i = 0; i < iterations; i++) {
var candidate = i * (multiplier * Math.random());
var isPrime = true;
for (var c = 2; c <= Math.sqrt(candidate); ++c) {
if (candidate % c === 0) {
// not prime
isPrime = false;
break;
}
}
if (isPrime) {
primes.push(candidate);
}
}
return primes;
}
function renderLayoutAndRun(f){
window.requestAnimationFrame(() => {
window.requestAnimationFrame(f);
});
};
function jsHeavyTask(){
loader.show();
renderLayoutAndRun(() => {
console.log('heavy function started');
const start = performance.now();
calculatePrimes(iterations, multiplier);
const end = performance.now();
renderLayoutAndRun(() => loader.hide());
console.log('heavy function ended in '+ (end - start).toFixed() +' ms');
});
}
input {width: 150px}
.css-loader-background {
display: flex;
align-items: center;
justify-content: center;
background-color: white;
border-radius: 10px;
font-size: 12px;
}
.css-loader-fullscreen {
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 100000;
position: fixed;
background-color: rgba(0, 0, 0, 0.4);
display: flex;
justify-content: center;
align-items: center;
}
.css-loader-fullscreen .css-loader-background {
width: 100px;
height: 100px;
}
.css-loader-animation {
width: 40px;
height: 40px;
border-radius: 50%;
border: 8px solid transparent;
border-top-color: purple;
border-bottom-color: purple;
text-indent: -9999em;
animation: spinner 0.8s ease infinite;
transform: translateZ(0);
}
#-webkit-keyframes spinner {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div class="css-loader-fullscreen">
<div class="css-loader-background">
<div class="css-loader-animation"></div>
</div>
</div>
<input id="load" type="button" value="load">
<input id="dummy" type="button" value="dummy">

Categories