I have made a simple to-do list, which works as intended; however, I am experiencing an issue where the background, which is a random gradient calculated and applied to the of my html on load through JS, sometimes does not apply at all. If you reload the codepen page (linked below) a few times you should see the issue, where instead of a gradient background there is no background at all. It sometimes takes 15+ reloads before it appears but it always inevitably does.
The code works as intended for the most part, but I would like to eliminate this issue altogether.
// Get elements
const inputBox = document.getElementById("input-box");
const listContainer = document.getElementById("list-container");
// Function to add a task
function addTask() {
if (inputBox.value === '') {
alert("You need to write something you silly goose.");
} else {
const li = document.createElement("li");
li.innerHTML = inputBox.value;
const span = document.createElement("span");
span.innerHTML = "u00d7";
li.appendChild(span);
listContainer.appendChild(li);
}
inputBox.value = "";
saveData();
}
// Event listener for listContainer
listContainer.addEventListener("click", function (e) {
if (e.target.tagName === "LI") {
e.target.classList.toggle("checked");
saveData();
} else if (e.target.tagName === "SPAN") {
e.target.parentElement.remove();
saveData();
}
});
// Event listener for Enter key in inputBox
inputBox.addEventListener("keypress", function (event) {
if (event.key === "Enter") {
event.preventDefault();
addTask();
}
});
// Function to save data to local storage
function saveData() {
localStorage.setItem("data", listContainer.innerHTML);
}
// Function to show tasks from local storage
function showTask() {
listContainer.innerHTML = localStorage.getItem("data");
}
document.addEventListener("DOMContentLoaded", function () {
requestAnimationFrame(function () {
backgroundColor();
});
});
const calculateContrast = (color1, color2) => {
const luminance1 = calculateLuminance(color1);
const luminance2 = calculateLuminance(color2);
const lighterLuminance = Math.max(luminance1, luminance2);
const darkerLuminance = Math.min(luminance1, luminance2);
return (lighterLuminance + 0.05) / (darkerLuminance + 0.05);
};
const calculateLuminance = (color) => {
const rgb = parseInt(color, 16);
const r = (rgb >> 16) / 255;
const g = ((rgb >> 8) & 0xff) / 255;
const b = (rgb & 0xff) / 255;
const gammaCorrect = (value) => value <= 0.03928 ? value / 12.92 : Math.pow((value + 0.055) / 1.055, 2.4);
const sRGB = gammaCorrect(r) * 0.2126 + gammaCorrect(g) * 0.7152 + gammaCorrect(b) * 0.0722;
return sRGB;
};
function backgroundColor() {
const getRandomColor = () => Math.floor(Math.random() * 0xffffff).toString(16);
const calculateAndSetBackground = () => {
let color1, color2;
do {
color1 = getRandomColor();
color2 = getRandomColor();
} while (calculateContrast(color1, color2) < 4.5);
document.body.style.background = `linear-gradient(to left top, #${color1}, #${color2})`;
};
// Ensure that showTask function is complete before setting the background
showTask();
// Call calculateAndSetBackground after showTask is complete
calculateAndSetBackground();
}
I have gone through the syntax and couldn’t find any errors. I thought it might be a timing issue so I have tried modifing my JS code to ensure that the background is set only after the contrast calculation is complete but it doesn’t seem to help.
I tried timing fixes using:
-
a combination of the ‘window.onload’ event and ‘setTimeout’ to give the browser some extra time to fully render the page before applying the background
-
the defer attribute on the tag of my html in conjunction with the DOMContentLoaded event and requestAnimationFrame to ensure that the script is executed after the HTML has been parsed:
document.addEventListener("DOMContentLoaded", function () {
requestAnimationFrame(function () {
backgroundColor();
});
});
- Separating the background calculation and setting into a new function (calculateAndSetBackground). Also ensuring that the showTask function is called before setting the background, addressing any potential timing issues.
None of which have worked, making me think that it may not be a timing issue but some other issue I can’t place, though I can’t be sure. Possibly something to do with caching?
Here’s a link to Codepen
2
Answers
Here your problem:
Sometime
randomColor
start with zero*. Then hex code pass to css is incorrect. You need some padding function to make sure color code is 6 digit hex color.The Problem
You should have tested your colors’ output on the console!
If you added the code below…
You will see the output once the bug happens. One of the hex colors is going to be 5 or 4 digits. That is, the console will log something like
90f67
. So, the timing is the cause of your bug.Possible Solutions
You have many solutions, by the way!
First, you can do what @takid1412 suggested, add a padding function to make sure color code is 6 digit hex color.
Second, you may use HSL colors as they really work will once trying to generated random colors. I always use this strategy since you have three spaces. The first one is a 360° dice-like color value called
Hue
, and the other two areSaturation
andLightness
which are a % digit0%-100%
.Then, use the
generateRandomColor()
where needed!Minor Tip?
I noticed that your code uses multiple ways of writing functions, the
function name(){}
and the most recent and better one, theconst name = () => {}
.I can’t recommend enough how useful the arrow functions can get later, once working with modules, so I heavily recommend that you stick with at all times, unless you have a good reason not to use it!