Svelte flickering when adding new elements to scroll - javascript

REPL: https://svelte.dev/repl/bf3db76cdd294b3882398bf4952f820f?version=3.55.0
Code:
App.svelte
<script>
import { onMount } from 'svelte';
import InfiniteScroll from './InfiniteScroll.svelte';
// if the api (like in this example) just have a simple numeric pagination
let page = 1;
// but most likely, you'll have to store a token to fetch the next page
let nextUrl = '';
// store all the data here.
let data = [];
// store the new batch of data here.
let newBatch = [];
async function fetchData() {
const response = await fetch(`https://picsum.photos/v2/list?page=${page}&limit=2`);
newBatch = await response.json();
console.log(newBatch);
};
onMount(()=> {
// load first batch onMount
fetchData();
})
$: data = [
...data,
...newBatch
];
</script>
<div class="parent">
{#each data as d (d.id)}
<div class="children">
<img class="cover" src={d.download_url} alt="download"/>
</div>
{/each}
<InfiniteScroll
hasMore={newBatch.length}
threshold={100}
on:loadMore={() => {page++; fetchData()}}
/>
</div>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.parent {
max-height: 100vh;
overflow-y: auto;
scroll-snap-type: y mandatory;
margin: 0 auto;
}
.children {
position: relative;
background-color: black;
min-height: 100vh;
width: 100%;
color: white;
display: flex;
justify-content: center;
align-items: center;
font-weight: 600;
font-size: 64px;
scroll-snap-align: center;
border-bottom: 2px solid #fff;
}
.cover {
position: absolute;
left: 0;
top: 0;
background-size: cover;
max-height: 100vh;
}
</style>
InfiniteScroll.svelte
<script>
import { onMount, onDestroy, createEventDispatcher } from "svelte";
export let threshold = 0;
export let horizontal = false;
export let elementScroll;
export let hasMore = true;
const dispatch = createEventDispatcher();
let isLoadMore = false;
let component;
$: {
if (component || elementScroll) {
const element = elementScroll ? elementScroll : component.parentNode;
element.addEventListener("scroll", onScroll);
element.addEventListener("resize", onScroll);
}
}
const onScroll = e => {
const element = e.target;
const offset = horizontal
? e.target.scrollWidth - e.target.clientWidth - e.target.scrollLeft
: e.target.scrollHeight - e.target.clientHeight - e.target.scrollTop;
if (offset <= threshold) {
if (!isLoadMore && hasMore) {
dispatch("loadMore");
}
isLoadMore = true;
} else {
isLoadMore = false;
}
};
onDestroy(() => {
if (component || elementScroll) {
const element = elementScroll ? elementScroll : component.parentNode;
element.removeEventListener("scroll", null);
element.removeEventListener("resize", null);
}
});
</script>
<div bind:this={component} style="width:0px" />
When we scroll to the bottom of the list, the screen flickers and displays the previous element for a moment of time.
Encountering this trouble when trying to implement infinite scrolling, please check the repl. Thanks!

From #H.B.
InfiniteScroll should not be a component by the way. This is something
that actions are made for.
Answer REPL: https://svelte.dev/repl/cb9b9e77febb496c9f9af118dd077cb6?version=3.55.0
Used svelte-inview library and inview action.
Code
App.svelte
<script>
import { onMount } from 'svelte';
import { inview } from 'svelte-inview';
// if the api (like in this example) just have a simple numeric pagination
let page = 0;
// but most likely, you'll have to store a token to fetch the next page
let nextUrl = '';
// store all the data here.
let data = [];
// store the new batch of data here.
let newBatch = [];
// no more data
let noMoreData = false;
async function fetchData() {
const response = await fetch(`https://picsum.photos/v2/list?page=${page}&limit=2`);
newBatch = await response.json();
console.log(newBatch);
};
onMount(()=> {
// load first batch onMount
// fetchData();
})
let visible = true;
$: data = [
...data,
...newBatch
];
</script>
<div class="parent">
{#each data as d (d.id)}
<div class="children">
<span class="center">{d.id}</span>
</div>
{/each}
<div use:inview={{}} style="height: 10px;" on:change={(e) => {
if (e.detail.inView && !noMoreData) {
page++;
fetchData();
}
}}></div>
</div>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.parent {
max-height: 100vh;
overflow-y: scroll;
scroll-snap-type: y mandatory;
margin: 0 auto;
}
.children {
position: relative;
background-color: black;
min-height: 100vh;
width: 100%;
color: white;
display: flex;
justify-content: center;
align-items: center;
font-weight: 600;
font-size: 64px;
scroll-snap-align: center;
border-bottom: 2px solid #fff;
}
.cover {
position: absolute;
left: 0;
top: 0;
background-size: cover;
max-height: 100vh;
}
</style>

Related

How do I add multiple instances of Javascript Audio Player in static HTML site?

I am working on a static portfolio site and have styled some Javascript audio players.
The site is live here with the first audio player working almost exactly as desired (except the progress bar displays at the top of the div, I'd like it at the bottom). A photo is attached of the desired visual outcome.
I need five total audio players. How can I achieve this?
Current Javascript:
const audioPlayer = document.querySelector(".audio-player");
const audio = new Audio(
"https://jsomerset.uk/images/victory.mp3"
);
console.dir(audio);
audio.addEventListener(
"loadeddata",
() => {
audioPlayer.querySelector(".time .length").textContent = getTimeCodeFromNum(
audio.duration
);
audio.volume = .75;
},
false
);
const timeline = audioPlayer.querySelector(".timeline");
timeline.addEventListener("click", e => {
const timelineWidth = window.getComputedStyle(timeline).width;
const timeToSeek = e.offsetX / parseInt(timelineWidth) * audio.duration;
audio.currentTime = timeToSeek;
}, false);
setInterval(() => {
const progressBar = audioPlayer.querySelector(".progress");
progressBar.style.width = audio.currentTime / audio.duration * 100 + "%";
audioPlayer.querySelector(".time .current").textContent = getTimeCodeFromNum(
audio.currentTime
);
}, 500);
const playBtn = audioPlayer.querySelector(".controls .toggle-play");
playBtn.addEventListener(
"click",
() => {
if (audio.paused) {
playBtn.classList.remove("play");
playBtn.classList.add("pause");
audio.play();
} else {
playBtn.classList.remove("pause");
playBtn.classList.add("play");
audio.pause();
}
},
false
);
You code can't run properly, since you're selecting non existent elements.
Check you dev tools console for errors.
E.g. you're trying to display the current time in an element with the class time – but yout html does not contain such an element.
Besides, you haven't defined the method getTimeCodeFromNum().
See the cleaned up code – not usable blocks are commented out :
const audioPlayer = document
.querySelectorAll(".audio-player")
.forEach((audioPlayer) => {
const audio = new Audio(audioPlayer.dataset.src);
//console.dir(audio);
/*
audio.addEventListener(
"loadeddata",
() => {
audioPlayer.querySelector(
".time .length"
).textContent = getTimeCodeFromNum(audio.duration);
audio.volume = 0.75;
},
false
);
*/
const timeline = audioPlayer.querySelector(".timeline");
timeline.addEventListener(
"click",
(e) => {
const timelineWidth = window.getComputedStyle(timeline).width;
const timeToSeek =
(e.offsetX / parseInt(timelineWidth)) * audio.duration;
audio.currentTime = timeToSeek;
},
false
);
setInterval(() => {
const progressBar = audioPlayer.querySelector(".progress");
progressBar.style.width =
(audio.currentTime / audio.duration) * 100 + "%";
/*
audioPlayer.querySelector(
".time .current"
).textContent = getTimeCodeFromNum(audio.currentTime);
*/
}, 500);
const playBtn = audioPlayer.querySelector(".controls .toggle-play");
playBtn.addEventListener(
"click",
() => {
if (audio.paused) {
playBtn.classList.remove("play");
playBtn.classList.add("pause");
audio.play();
} else {
playBtn.classList.remove("pause");
playBtn.classList.add("play");
audio.pause();
}
},
false
);
/*
audioPlayer
.querySelector(".volume-button")
.addEventListener("click", () => {
const volumeEl = audioPlayer.querySelector(".volume-container .volume");
audio.muted = !audio.muted;
if (audio.muted) {
volumeEl.classList.remove("icono-volumeMedium");
volumeEl.classList.add("icono-volumeMute");
} else {
volumeEl.classList.add("icono-volumeMedium");
volumeEl.classList.remove("icono-volumeMute");
}
});
*/
});
body {
background: #000
}
.audio-player {
display: grid;
grid-template-rows: 6px auto;
overflow: hidden;
height: 200px;
width: 100vw;
color: #efefef;
}
.timeline {
background: none;
width: 100%;
position: relative;
cursor: pointer;
height: 5px;
}
.progress {
background: #efefef;
width: 0%;
height: 5px;
transition: 0.25s;
-webkit-transition: 0.25s;
}
.controls {
display: flex;
align-items: center;
justify-content: center;
width: 100px;
}
.controls * {
display: flex;
justify-content: center;
align-items: center;
}
.play {
cursor: pointer;
position: relative;
left: 0;
height: 0;
width: 0;
border: 7px solid #0000;
border-left: 13px solid white;
}
.pause {
height: 15px;
width: 20px;
cursor: pointer;
position: absolute;
margin-left: 15px;
}
.pause:before {
position: absolute;
top: 0;
left: 0px;
background: white;
content: "";
height: 15px;
width: 3px;
}
.pause:after {
position: absolute;
top: 0;
right: 9px;
background: white;
content: "";
height: 15px;
width: 3px;
}
<div class="audio-player a-one font" data-src="https://jsomerset.uk/images/swain.mp3">
<div class="timeline">
<div class="progress" style="width: 0%;"></div>
</div>
<div class="name">Action</div>
<div class="controls">
<div class="play-container">
<div class="toggle-play play">
</div>
</div>
</div>
</div>
<div class="audio-player a-two font" data-src="https://jsomerset.uk/images/victory.mp3">
<div class="timeline">
<div class="progress"></div>
</div>
<div class="name">Victory Song</div>
<div class="controls">
<div class="play-container">
<div class="toggle-play play">
</div>
</div>
</div>
</div>
Use document.querySelectorAll, then loop over the selection. You can store the mp3 URL for each div inside a data-src attribute:
<div class="audio-player" data-src="https://jsomerset.uk/images/victory.mp3">...</div>
<div class="audio-player" data-src="https://jsomerset.uk/images/anotherFile.mp3">...</div>
<div class="audio-player" data-src="https://jsomerset.uk/images/etc.mp3">...</div>
document.querySelectorAll(".audio-player").forEach(audioPlayer => {
const audio = new Audio(audioPlayer.dataset.src);
// rest of your code
});

Page does not show correct layout with small screen

The layout starts-off fine when displayed in 751px or greater but does not work fine when it starts-off in 750px or less. I thought this code below in my javascript would work but it doesn't.
// does not seem to work when page is loaded
window.addEventListener("load", () => {
window.innerWidth <= 750 ? columnLayout() : rowLayout();
});
const colorPickerEl = document.getElementById("color-picker");
const colorSchemeContainerEl = document.getElementById(
"color-scheme-container"
);
const colorModeEl = document.getElementById("color-mode");
const headerEl = document.getElementById("header");
// display default scheme
displayColorScheme(colorPickerEl.value.slice(1), "monochrome");
/*-------------
Event Listeners
---------------*/
// listen for when a new scheme is requested
document.getElementById("get-scheme-btn").addEventListener("click", () => {
displayColorScheme(colorPickerEl.value.slice(1));
});
// listen for when a randomized scheme is requested
document
.getElementById("randomize-scheme-btn")
.addEventListener("click", () => {
displayColorScheme(generateRandomColor());
});
// does not seem to work when page is loaded
window.addEventListener("load", () => {
window.innerWidth <= 750 ? columnLayout() : rowLayout();
});
window
.matchMedia("screen and (max-width: 750px)")
.addEventListener("change", (event) => {
if (event.matches) {
columnLayout();
}
});
window
.matchMedia("screen and (min-width: 751px)")
.addEventListener("change", (event) => {
if (event.matches) {
rowLayout();
}
});
function columnLayout() {
document.getElementById(
"spacer"
).style.height = `${headerEl.offsetHeight}px`;
const colorBars = document.getElementsByClassName("color-bar");
let barHeight =
(colorSchemeContainerEl.offsetHeight - headerEl.offsetHeight) / 5;
for (const bar of colorBars) {
console.log(bar);
bar.style.height = `${barHeight}px`;
}
}
function rowLayout() {
console.log("row");
const colorBars = document.getElementsByClassName("color-bar");
for (const bar of colorBars) {
bar.style.height = `${colorSchemeContainerEl.offsetHeight}px`;
}
}
// display color scheme based on user-picked color (or randomized color) and mode
function displayColorScheme(seed) {
const mode = colorModeEl.value;
// fetch the scheme using an api
fetch(`https://www.thecolorapi.com/scheme?hex=${seed}&mode=${mode}`)
// convert the data from json
.then((response) => response.json())
// manipulate the data
.then((data) => {
let html = "";
for (const color of data.colors) {
const totalRGBValue = color.rgb.r + color.rgb.g + color.rgb.b;
// 127 + 127 + 127 (the middle threshold)
const midRGBValue = 381;
const textColor =
totalRGBValue <= midRGBValue ? "white" : "black";
html += `
<div class="color-bar" style="background-color:${color.hex.value};"><p class= "text-color-bar" style="color:${textColor};">${color.hex.clean}<p></div>
`;
}
let spacer = `
<div id="spacer"></div>
`;
colorSchemeContainerEl.innerHTML = spacer + html;
});
}
// generate a random color in hex format
function generateRandomColor() {
const characters = "0123456789ABCDEF";
const maxLength = 6;
let color = "";
for (let i = 0; i < maxLength; i++) {
color += characters.charAt(
Math.floor(Math.random() * characters.length)
);
}
colorPickerEl.value = "#" + color;
return color;
}
html,
body {
margin: 0;
padding: 0;
font-family: sans-serif;
}
form {
display: flex;
justify-content: space-evenly;
}
header {
padding: 30px 0;
background-color: transparent;
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 99;
background-color: white;
box-shadow: 0 6px 10px -4px #222;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
}
input[type="button"],
select {
padding: 0 10px;
font-size: 1.05rem;
}
#color-picker {
height: 3.5em;
width: 10%;
}
p.colorName {
border: 1.5px solid rgb(70, 70, 70);
border-radius: 5px;
padding: 10px;
}
select {
width: 30%;
text-align: center;
}
.color-bar {
display: flex;
justify-content: center;
align-items: center;
}
.text-color-bar {
margin: 0;
font-size: 1.1rem;
letter-spacing: 0.1rem;
}
#color-scheme-container {
height: 100vh;
}
#media screen and (max-width: 750px) {
#color-scheme-container {
flex-direction: column;
}
#spacer {
width: 100%;
/* height: calc(60px + 3.5em - 9px); */
}
.color-bar {
width: 100%;
/* height: 17.94%; */
}
}
#media screen and (min-width: 751px) {
#color-scheme-container {
width: 100%;
display: flex;
position: relative;
}
.color-bar {
width: 20%;
}
}
<header id="header">
<form id="color-form">
<input type="color" id="color-picker" value="#008080" />
<select name="mode" id="color-mode">
<option value="monochrome">Monochrome</option>
<option value="monochrome-dark">Monochrome Dark</option>
<option value="monochrome-light">Monochrome Light</option>
<option value="analogic">Analogic</option>
<option value="complement">Complement</option>
<option value="analogic-complement">
Analogic Complement
</option>
<option value="triad">Triad</option>
<option value="quad">Quad</option>
</select>
<input id="get-scheme-btn" type="button" value="Get Color Scheme" />
<input id="randomize-scheme-btn" type="button" value="Radomize Scheme" />
</form>
</header>
<main>
<div id="color-scheme-container"></div>
</main>
Please note that I can't get the height of my header element and assign it to my spacer div, in CSS. I have to go through my JS and set the height there. Any help is greatly appreciated.
What am I doing wrong? Why isn't the addeventlistener "load" code working?
Here's a link where you can try out the issue:
https://massuhcolorschemegenerator.netlify.app/
I found the answer! All I needed to do was put my code:
window.innerWidth <= 750 ? columnLayout() : rowLayout();
at the end of my displayColorScheme function.
It worked perfectly.

CSS - Preventing element from being push upwards during DOM manipulation

I have a todo list built with vanilla JavaScript.
As you'd expect, when you click the add button new todos appear on the DOM.
The problem is that the todo list gets pushed upwards each time you add a new todo and eventually it overwrites the navbar and leaves the viewport.
I've tried adding all the possible CSS position properties to the title but the list still keeps moving regardless
For the code and visuals - https://codepen.io/greevesh/pen/gOxNEPy
This is the element I want to prevent from moving -
<div class="d-flex justify-content-center mb-3" style="position: sticky;">
<img class="logo" src="/img/planning.svg" alt="tasktracker-logo">
</div>
You could allow the list to grow but stop moving up when reaching the top by adding margin: auto to your sub-container. See this answer for more details
.sub-container {
margin: auto;
display: block;
}
That is because you make the .container align-items: center;. Please remove that and add padding-top or margin-top as you want. Putting align-items: center; is a bad practice for element that has dynamic height.
const toDoList = document.getElementById("toDoList");
const title = document.getElementById("title");
const toDoContainer = document.getElementById("toDoContainer");
const allCheckboxes = toDoContainer.getElementsByClassName("checkbox");
const allXBtns = toDoContainer.getElementsByClassName("X");
const saveBtn = document.getElementById("save");
const clearBtn = document.getElementById("clear");
const saveMsg = document.getElementById("saveMsg");
const saveTitle = () => {
localStorage.setItem(activeEmail.innerHTML + " Title", JSON.stringify(title.value));
};
function createToDo() {
const createdToDoContainer = document.createElement("div");
createdToDoContainer.id = 'toDo' + new Date().getTime(); // unique ID
const createdCheckbox = document.createElement("INPUT");
createdCheckbox.setAttribute("type", "checkbox");
const createdToDo = document.createElement("INPUT");
const createdXBtn = document.createElement("SPAN");
createdToDoContainer.appendChild(createdCheckbox);
createdToDoContainer.appendChild(createdToDo);
createdToDoContainer.appendChild(createdXBtn);
createdToDoContainer.classList.add("toDoInnerContainer");
createdCheckbox.classList.add("checkbox");
createdToDo.classList.add("input");
createdXBtn.classList.add("X");
createdXBtn.innerHTML = "X";
toDoContainer.appendChild(createdToDoContainer);
}
let checkedToDos = [];
// delete button functionality
toDoContainer.addEventListener("click", (e) => {
const tgt = e.target;
if (tgt.classList.contains("X")) {
const parent = tgt.parentElement;
parent.remove();
const toDoValue = parent.querySelector(".input").value;
if (parent.querySelector(".checkbox").checked) {
if (checkedToDos.includes(toDoValue)) {
checkedToDos = checkedToDos.filter(val => val !== toDoValue);
}
}
}
});
const saveToDos = () => {
// change the global todo object (and make it an array)
toDos = [...document.querySelectorAll(".input")].map(toDo => {
const checked = toDo.parentNode.querySelector(".checkbox").checked;
const id = toDo.closest("div").id;
const val = toDo.value;
if (toDo.parentNode.querySelector(".checkbox").checked == true) {
checkedToDos.push(val);
}
return { id, val, checked }
});
localStorage.setItem((activeEmail.innerHTML), JSON.stringify(toDos));
};
// changes todo styling depending on checkbox state (checked or not checked)
toDoContainer.addEventListener("change", (e) => {
const tgt = e.target;
const chk = tgt.checked;
const toDo = tgt.parentNode.querySelector('.input');
toDo.style.textDecoration = chk ? "line-through" : "none";
toDo.style.opacity = chk && toDo.value !== "" ? "50%" : "100%";
});
document.getElementById("add").addEventListener("click", createToDo);
saveBtn.addEventListener("click", () => {
saveTitle();
saveToDos();
saveMsg.innerHTML = "Your tasks have been saved.";
});
// makes sure save message disappears once user clicks elsewhere
window.addEventListener("click", (e) => {
const tgt = e.target;
if (saveMsg.innerHTML !== "" && saveBtn !== tgt) {
saveMsg.innerHTML = "";
}
})
const allToDos = toDoContainer.getElementsByClassName("input");
const clearToDosAndTitle = () => {
title.value = "";
checkedToDos.splice(0, checkedToDos.length);
[...document.getElementsByClassName("toDoInnerContainer")].map(toDo => {
// remove all todos but leave at least one empty one on the DOM
// the length of the checkbox collection matters most because it's the first part of the todo to be loaded onto the DOM
while (toDo.lastChild && allCheckboxes.length > 1) {
toDo.lastChild.remove();
}
// empties the only todo that was left on the DOM after clearance
if (allToDos[0].value !== "") {
allToDos[0].value = "";
}
});
}
clearBtn.addEventListener("click", () => {
clearToDosAndTitle();
});
const loadEmptyToDoInputs = () => {
const user = JSON.parse(localStorage.getItem(activeEmail.innerHTML));
if (user && user.length > 1) {
// using a while loop instead of a foreach in this case prevents an unrequested duplicate todo being added to the DOM
while (allToDos.length != user.length) {
createToDo();
}
}
}
const loadToDos = () => {
loadEmptyToDoInputs();
loadToDoTextValues();
loadCheckedToDoStyling();
loadDefaultEmptyToDo();
}
const loadCheckedToDoStyling = () => {
const user = JSON.parse(localStorage.getItem(activeEmail.innerHTML));
user.forEach(value => {
let checkedValues = [];
if (value.checked && toDoTextValues.includes(value.val)) {
checkedValues.push(value.val);
}
toDos = [...document.getElementsByClassName("input")].map(toDo => {
if (checkedValues.includes(toDo.value)) {
box = toDo.parentElement.firstChild;
box.checked = true;
toDo.style.textDecoration = "line-through";
toDo.style.opacity = "50%";
}
}
)});
}
const loadTitle = () => {
if (localStorage.getItem(activeEmail.innerHTML + " Title")) {
title.value = JSON.parse(localStorage.getItem(activeEmail.innerHTML + " Title"));
}
}
const loadDefaultEmptyToDo = () => {
// if there are no checkboxes, there are no todos, so load an empty one by default
if (allCheckboxes.length == 0) {
createToDo();
}
}
let toDoTextValues = [];
const loadToDoTextValues = () => {
const user = JSON.parse(localStorage.getItem(activeEmail.innerHTML));
if (user) {
user.forEach(value => {
toDoTextValues.push(value.val);
});
toDos = [...document.getElementsByClassName("input")].map(toDo => {
for (let value = 0; value < toDoTextValues.length - user.length + 1; value++) {
toDo.value = toDoTextValues[value];
}
toDoTextValues.length++;
});
}}
#title {
border: none;
font-size: 45px;
text-align: center;
margin-top: 20px;
}
#title::placeholder {
text-align: center;
}
#title:focus {
text-align: center;
outline: none;
}
#title:focus::placeholder {
visibility: hidden;
}
input[type="checkbox"] {
height: 20px;
width: 20px;;
}
.input {
border-top: none;
border-right: none;
border-left: none;
margin: 0 0 25px 30px;
font-size: 30px;
display: block;
}
.input:focus {
outline: none;
}
.X {
background-color: #E60E0E;
color: white;
border-radius: 50%;
height: 30px;
width: 30px;
font-size: 25px;
font-family: 'Helvetica', 'Arial', sans-serif;
display: flex;
justify-content: center;
align-items: center;
margin: -85px 100px 0 0;
float: right;
}
.X:hover {
cursor: pointer;
background-color: #d81313;
}
#toDoBtnContainer {
margin-top: 35px;
}
#add {
color: #fff;
font-weight: 400;
background-color: #27a348;
}
#add:hover {
background-color: #21af47;
}
#save {
color: #fff;
font-weight: 400;
background-color: #04992b;
}
#save:hover {
background-color: #099b30;
}
#clear {
color: #fff;
font-weight: 400;
background-color: #cc2121;
}
#clear:hover {
background-color: #d10f0f;
}
#saveMsg {
margin-top: 15px;
color: #04992b;
font-weight: 600;
}
.container {
display: flex;
justify-content: center;
height: 100vh;
padding-top: 120px;
}
.sub-container {
display: block;
}
.logo {
height: 75px;
width: 75px;
}
.headings {
text-align: center;
margin-bottom: 30px;
}
.headings h4 {
font-weight: 300;
}
button {
height: 35px;
font-size: 20px;
}
nav {
display: flex;
padding: 20px;
background-color: blue;
}
#signOut {
margin: 0 20px 0 auto;
color: #fff;
font-weight: 400;
background-color: #E22929;
}
#signOut:hover {
background-color: #db2626;
}
#activeEmail {
margin-right: 100px;
font-weight: 500;
}
<nav class="hide">
<button type="button" class="btn btn-lg pt-0 hide" id="signOut">Sign out</button>
<p class="mt-1 hide" id="activeEmail"></p>
</nav>
<div class="container">
<div class="sub-container">
<div class="d-flex justify-content-center mb-3" style="position: sticky;">
<img class="logo" src="/img/planning.svg" alt="tasktracker-logo">
</div>
<div class="headings">
<h1>TaskTracker</h1>
<h4>Leave no task untracked.</h4>
</div>
<div class="hide" id="toDoList">
<h2><input id="title" type="text" placeholder="Add Title"></h2>
<div id="toDoContainer">
</div>
<div id="toDoBtnContainer">
<button type="button" class="btn btn-lg pt-0" id="add">Add</button>
<button type="button" class="btn btn-lg pt-0" id="save">Save</button>
<button type="button" class="btn btn-lg pt-0" id="clear">Clear everything</button>
</div>
<p id="saveMsg"></p>
</div>
</div>
</div>
Your .container is a flex with fixed height. When todo-lists overflow, the flexbox tried to keep them all centered within the container.
An easy fix would be changing to min-height: 100vh. This way the flex can grow when the lists are added.
.container {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}

<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>

How to persist my class change with addEventListener?

const changeNavbarButton = () => {
const navbarBox = document.querySelectorAll(".navbar-icon-box");
navbarBox.forEach((box) => {
box.addEventListener("click", (event) => {
const navbarBoxActive = document.querySelector(".navbar-icon-box-active")
navbarBoxActive.classList.remove("navbar-icon-box-active")
event.currentTarget.classList.add("navbar-icon-box-active");
console.log(event.currentTarget);
});
});
};
changeNavbarButton();
.navbar {
background-color: #161616;
width: 100%;
height: 50px;
position: fixed;
bottom: 0;
padding: 0;
display: flex;
justify-content: space-around;
z-index: 100;
}
.navbar-icon-box {
height: 100%;
display: flex;
flex: 1 1 auto;
justify-content: center;
align-items: center;
flex-direction: column;
place-items: center;
text-align: center;
}
.navbar-icon-box p {
font-size: 10px;
color: white;
}
.navbar-icon-box-active {
background-color: #1f1f1f;
}
.navbar-icon-box-active p {
color: orange;
}
<div class="navbar">
<a class="navbar-icon-box navbar-icon-box-active">
<p>Home</p>
</a>
<a class="navbar-icon-box">
<p>Events</p>
</a>
<a class="navbar-icon-box">
<p>Profile</p>
</a>
</div>
Hi everyone, I'm stuck with something I can't figure out. When I click on a navbar button, my console.log() displays this:
<a class="navbar-icon-box navbar-icon-box-active" href="/"></a>
It seems to work, however I can't see any change in my browser. When I inspect the navbar button I just clicked on, it just displays the first class navbar-icon-box without add the new one. What am I missing ?
Problem may be occuring due to variable's life cycle.
I suggest you to use the codes down below where navbarBoxActive is not used over the addEventListeners.
const changeNavbarButton = () => {
//const navbarBoxActive = document.querySelector(".navbar-icon-box-active")
const navbarBox = document.querySelectorAll(".navbar-icon-box");
navbarBox.forEach((box) => {
box.addEventListener("click", (event) => {
document.querySelector(".navbar-icon-box-active").classList.remove("navbar-icon-box-active")
event.currentTarget.classList.add("navbar-icon-box-active");
console.log(event.currentTarget);
});
});
};
changeNavbarButton();
or
const changeNavbarButton = () => {
const navbarBox = document.querySelectorAll(".navbar-icon-box");
navbarBox.forEach((box) => {
box.addEventListener("click", (event) => {
const navbarBoxActive = document.querySelector(".navbar-icon-box-active");
navbarBoxActive.classList.remove("navbar-icon-box-active")
event.currentTarget.classList.add("navbar-icon-box-active");
console.log(event.currentTarget);
});
});
};
changeNavbarButton();

Categories