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.
Related
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
});
So basically this is Day 3 (other days, I pretty much did nothing to complete the game) of making a game from HTML5. So I'm making a moves system right now, and I guess I'm doing well? (mainly because I'm not sure if I provided the user with too many moves...) But the thing about it is that, I'm kind of having ANOTHER styling issue.
As you can see in the image: I've CLEARLY set dimensions up for the headerDisplay class/id, but NO, it goes out of the div's dimensions and even goes on the grid. I'm also aiming for the time and moves text to be stuck right on top of the grid, similarly to how the word bank is stuck to the bottom of the grid.
I was also aiming for a button that says refresh right under the word bank, however no matter what I tried, the button would just be right the score text, which looks like this:
When I am aiming for this:
Code:
<div class="content" id="content">
<div class="headerDisplay" id="headerDisplay">
</div>
<div class="gameArea" id="gameArea">
</div>
<div class="wordBank" id="wordBank">
</div>
<div class="bottomMenu" id="bottomMenu">
</div>
</div>
::before,
::after {
box-sizing: border-box;
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
.content {
display: grid;
grid-template-rows: repeat(3, max-content);
margin-block: 1em;
margin-inline: auto;
width: 512px;
}
.bottomMenu {
font-size: 24px;
text-align: right;
}
.wordBank {
border: 2.5px solid #000;
border-radius: 5px;
display: flex;
font-size: 1.6em;
min-height: 3em;
justify-content: space-between;
padding: 0.25em;
}
.wordBank span:nth-child(even) {
align-self: end;
}
.gameArea {
font-size: 0;
justify-self: center;
max-width: 100%;
}
.cell {
border: 1px solid black;
width: 50px;
font-size: 1rem;
height: 50px;
display: inline-block;
}
.headerDisplay {
width: 100%;
height: 76.8px;
text-align: right;
font-size: 1.6em;
}
let score = 0;
const headerDisplay = document.getElementById("headerDisplay")
const bottomMenu = document.getElementById("bottomMenu");
const wordBank = document.getElementById("wordBank")
const gameArea = document.getElementById("gameArea")
const rows = document.getElementsByClassName("gridRow");
const cells = document.getElementsByClassName("cell");
const words = [ // snippet
"ability",
"able",
"about",
"above",
"abroad",
"absence",
"absent",
"absolute",
"accept",
"accident",
"accord",
"account",
"accuse",
"accustom",
"ache",
"across",
"act"
]
let selectedWords = [];
bottomMenu.innerHTML = "<p>Score: " + score;
bottomMenu.innerHTML += "<button>Refresh"
while (selectedWords.length < 5) {
const selectedWord = words[Math.floor(Math.random() * words.length)];
if (selectedWord.length <= 9) {
wordBank.innerHTML += "<span>" + selectedWord + "</span>"
selectedWords.push(selectedWord);
}
}
let longestWord = selectedWords.reduce((a, b) => a.length < b.length ? b : a, "")
let charCount = longestWord.length
var moves = charCount * 5
headerDisplay.innerHTML += "<p>Time: "
headerDisplay.innerHTML += "<p>Moves: " + moves
function makeRows(rowNum) {
for (let r = 0; r < rowNum; r++) {
let row = document.createElement("div");
gameArea.appendChild(row).className = "gridRow";
}
}
function makeColumns(cellNum) {
for (let i = 0; i < rows.length; i++) {
for (let j = 0; j < cellNum; j++) {
let newCell = document.createElement("div");
rows[j].appendChild(newCell).className = "cell";
}
}
}
function defaultGrid() {
makeRows(charCount);
makeColumns(charCount);
}
defaultGrid();
To fix header you need to set its height to fit content, so it will be over your grid even if you change it later:
.headerDisplay {
width: 100%;
height: content-fit; /* previous: 76.8px */
text-align: right;
font-size: 1.6em;
}
And to fix bottom menu you need to add flexbox:
.bottomMenu {
font-size: 24px;
text-align: right;
display: flex; /* new */
flex-direction: row-reverse; /* new */
justify-content: space-between; /* new */
align-items: center; /* new */
}
For the button, you could try this:
button {
position: relative;
right: 400px;
bottom: 50px;
transform: scale(2,2)
}
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;
}
I am working on range slider for my website. I am stuck in to make it possible that on clicking specific box all previous steps gets applied with class ".previous" and all steps next to current one get applied with ".next" class, which have different styling for previous steps and next steps, according to the data-label value of that specific div. Lets take an example for a simple scenario if I click on step 7 then step 5 and 6 gets red, and step 8 and 9 gets grey, if I click on step 9 all previous gets applied with ".previous" class. In simple words all steps having number greater than current active step get applied with ".next" class and all those having number less then current active gets applied with ".previous" class .Attached is the source code, Any suggestion or help would be appreciated.
const rangeSlider = document.querySelector('#price_slider');
rangeSlider.addEventListener("input", rangeScript);
const customProgress = document.querySelector('#customProgress');
for (let i = 0; i < rangeSlider.max - rangeSlider.min; i++) {
const step = document.createElement('div');
step.classList.add('step');
step.setAttribute('data-label', +rangeSlider.min + i + 1);
step.addEventListener('click', (e) => {
let val = e.target.dataset.label;
document.querySelector('#price_slider').value = val
rangeScript({
target: rangeSlider
})
})
customProgress.appendChild(step);
}
customProgress.querySelector(`.step[data-label="${rangeSlider.value}"]`)
.classList.add('current')
function rangeScript(e) {
const target = document.getElementById('progress');
let newValue = parseInt(e.target.value);
const currentStep = customProgress.querySelector(`.step.current`);
if (currentStep) {
currentStep.classList.remove('current');
}
nextStep = customProgress.querySelector(`.step[data-label="${newValue}"]`);
if (nextStep) {
nextStep.classList.add('current')
}
}
#customProgress {
display: flex;
width: 100%;
height: 25px;
margin-top: 44px;
}
.step {
position: relative;
background: #f5f5f5;
height: 40px;
width: 100%;
border-radius: 4px 0 0 4px;
border-right: 1px solid white;
}
.step::after {
content: attr(data-label);
position: absolute;
top: -1.5em;
right: -0.25em;
}
.step ~ .current,
.step.current {
background: #a6983e;
}
.previous {
background: red;
}
.next {
background: #f5f5f5;
}
<div id="customProgress"></div>
<div id="progress" style="width: 100%">
<input id="price_slider" type="range" min="4" max="9" value="" style="display:none" />
</div>
You can do something like this
const rangeSlider = document.querySelector('#price_slider');
rangeSlider.addEventListener("input", rangeScript);
const customProgress = document.querySelector('#customProgress');
for (let i = 0; i < rangeSlider.max - rangeSlider.min; i++) {
const step = document.createElement('div');
step.classList.add('step');
step.setAttribute('data-label', +rangeSlider.min + i + 1);
step.addEventListener('click', (e) => {
let val = e.target.dataset.label;
document.querySelector('#price_slider').value = val
rangeScript({
target: rangeSlider
})
})
customProgress.appendChild(step);
}
customProgress.querySelector(`.step[data-label="${rangeSlider.value}"]`)
.classList.add('current')
function rangeScript(e) {
const target = document.getElementById('progress');
let newValue = parseInt(e.target.value);
const currentStep = customProgress.querySelector(`.step.current`);
if (currentStep) {
currentStep.classList.remove('current');
}
nextStep = customProgress.querySelector(`.step[data-label="${newValue}"]`);
if (nextStep) {
nextStep.classList.add('current')
}
}
#customProgress {
display: flex;
width: 100%;
height: 25px;
margin-top: 44px;
}
.step {
position: relative;
background: #f5f5f5;
height: 40px;
width: 100%;
border-radius: 4px 0 0 4px;
border-right: 1px solid white;
}
.step::after {
content: attr(data-label);
position: absolute;
top: -1.5em;
right: -0.25em;
}
.step ~ .current,
.step.current {
background: #a6983e;
}
.step {
background: green;
}
.current ~ div {
background: red;
}
<div id="customProgress"></div>
<div id="progress" style="width: 100%">
<input id="price_slider" type="range" min="4" max="9" value="" style="display:none" />
</div>
The title says it all. To see the issue, copy this code to the following online compiler: https://www.w3schools.com/php/phptryit.asp?filename=tryphp_compiler
<!DOCTYPE HTML>
<html>
<style>
/*MAIN*/
* {
margin: 0;
padding: 0;
user-select: none;
overflow: hidden;
}
body {
background-color: #FF0000;
margin: 0;
padding: 0;
}
/*ELEMENTS*/
div {
width: 100vw;
height: 100vh;
float: left;
margin-left: 0vw;
}
h1 {
font-family: verdana;
font-size: 5vh;
text-transform: uppercase;
}
h1.white {
color: #F4F4F4;
}
</style>
<body>
<div id = "main" style = "width: auto; margin-left: 0vw;">
<div id = "home" class = "container" style = 'background-color: #000000;'>
<h1 class = "white">click arrow to see how the next page doesn't appear until after the transition is complete</h1>
<!--ARROW BUTTON-->
<p id = 'arrowButton' style = 'color: #FFFFFF; position: absolute; height: 10vh; width: auto; margin: 45vh 0 0 75vw; font-size: 3vh;' onMouseDown = 'NextButtonClick();'>--></p>
</div>
<div id = "welcome" class = "container" style = 'background-color: #FFFFFF;'>
<h1 style = 'margin: 47.5vh 0 0 50vw'>welcome to my portfolio</h1>
</div>
</div>
<script>
var mainDiv, welcomeDiv;
var transitionSeconds = 0.5;
var isTransitioning = false;
function NextButtonClick() {
if(!isTransitioning) {
isTransitioning = true;
i = 0;
thisInterval = setInterval(function() {
mainDiv.style.marginLeft = (100 / i) - 101 + "vw";
i++;
if(i == 100) {
clearInterval(thisInterval);
mainDiv.style.marginLeft = "-100vw";
isTransitioning = false;
}
}, transitionSeconds);
}
}
window.onload = function() {
mainDiv = document.getElementById("main");
welcomeDiv = document.getElementById("welcome");
var arrowButton = document.getElementById("arrowButton");
var arrowButtonX, arrowButtonY;
var arrowButtonGlowDistance = 100;
arrowButtonX = arrowButton.getBoundingClientRect().left + arrowButton.getBoundingClientRect().width/2;//center
arrowButtonY = arrowButton.getBoundingClientRect().top + arrowButton.getBoundingClientRect().height/2;//center
document.onmousemove = function(e) {
x = e.clientX; y = e.clientY;
};
};
</script>
</body>
</html>
The background is red on purpose so that you can see how, even though the "welcome" div should be rendered over top the background, it is not being rendered until the very last second after the transition is completed and 100% of the element is on the screen.
I am stumped, and I'm not sure why this is since HTML usually doesn't seem to behave this way. Even when I highlight the element in Inspect Element, the Inspector doesn't show me where the element is on the screen until the final moment when it is rendered.
Any help would be greatly appreciated, and I look forward to hearing your feedback!
The problem here is that your DIVs are placed under each other and while one is moving horizontally, the next div is still underneath of it until first one is completely out of the way (just like Jenga game in reverse).
To solve this, you can try add display: flex, to place them horizontally instead:
var mainDiv, welcomeDiv;
var transitionSeconds = 0.5;
var isTransitioning = false;
function NextButtonClick() {
if (!isTransitioning) {
isTransitioning = true;
i = 0;
thisInterval = setInterval(function() {
mainDiv.style.marginLeft = (100 / i) - 101 + "vw";
i++;
if (i == 100) {
clearInterval(thisInterval);
mainDiv.style.marginLeft = "-100vw";
isTransitioning = false;
}
}, transitionSeconds);
}
}
window.onload = function() {
mainDiv = document.getElementById("main");
welcomeDiv = document.getElementById("welcome");
var arrowButton = document.getElementById("arrowButton");
var arrowButtonX, arrowButtonY;
var arrowButtonGlowDistance = 100;
arrowButtonX = arrowButton.getBoundingClientRect().left + arrowButton.getBoundingClientRect().width / 2; //center
arrowButtonY = arrowButton.getBoundingClientRect().top + arrowButton.getBoundingClientRect().height / 2; //center
document.onmousemove = function(e) {
x = e.clientX;
y = e.clientY;
};
};
* {
margin: 0;
padding: 0;
user-select: none;
overflow: hidden;
}
body {
background-color: #FF0000;
margin: 0;
padding: 0;
}
/*ELEMENTS*/
div {
width: 100vw;
height: 100vh;
float: left;
margin-left: 0vw;
display: flex; /* added */
}
h1 {
font-family: verdana;
font-size: 5vh;
text-transform: uppercase;
}
h1.white {
color: #F4F4F4;
}
<div id="main" style="width: auto; margin-left: 0vw;">
<div id="home" class="container" style='background-color: #000000;'>
<h1 class="white">click arrow to see how the next page doesn't appear until after the transition is complete</h1>
<!--ARROW BUTTON-->
<p id='arrowButton' style='color: #FFFFFF; position: absolute; height: 10vh; width: auto; margin: 45vh 0 0 75vw; font-size: 3vh;' onMouseDown='NextButtonClick();'>--></p>
</div>
<div id="welcome" class="container" style='background-color: #FFFFFF;'>
<h1 style='margin: 47.5vh 0 0 50vw'>welcome to my portfolio</h1>
</div>
</div>