Reputation: 31
I wrote a little vanilla JavaScript program, but I want to know if there is a possibility to write this simpler? With ES6+? But only with vanila JavaScript, no jQuery or other libraries / frameworks. Thanks in advance for the suggested solutions.
const red = document.getElementById('circleOne');
const green = document.getElementById('circleTwo');
const blue = document.getElementById('circleThree');
red.addEventListener("mouseover", () => {
red.style.backgroundColor = "red";
});
red.addEventListener("mouseout", () => {
red.style.backgroundColor = "white";
});
green.addEventListener("mouseover", () => {
green.style.backgroundColor = "green";
red.style.backgroundColor = "green";
});
green.addEventListener("mouseout", () => {
green.style.backgroundColor = "white";
red.style.backgroundColor = "white";
});
blue.addEventListener("mouseover", () => {
green.style.backgroundColor = "blue";
red.style.backgroundColor = "blue";
blue.style.backgroundColor = "blue";
});
blue.addEventListener("mouseout", () => {
green.style.backgroundColor = "white";
red.style.backgroundColor = "white";
blue.style.backgroundColor = "white";
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
section {
margin: 100px auto 0 auto;
max-width: 700px;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
section .circle {
width: 200px;
height: 200px;
border-radius: 50%;
border: 1px solid #333;
margin: 0 auto;
cursor: pointer;
}
<section>
<div id="circleOne" class="circle"></div>
<div id="circleTwo" class="circle"></div>
<div id="circleThree" class="circle"></div>
</section>
Thanks in advance for the suggested solutions.
Upvotes: 2
Views: 111
Reputation: 21609
You could define a function which takes the dom element, the colour and additionally the other dom elements to change the colour of, and use that to define the two handlers.
const red = document.getElementById('circleOne');
const green = document.getElementById('circleTwo');
const blue = document.getElementById('circleThree');
function addCircleListeners(elem, col, ...others) {
const allElems = [elem, ...others];
elem.addEventListener("mouseover", () => {
allElems.forEach(e => e.style.backgroundColor = col);
});
elem.addEventListener("mouseout", () => {
allElems.forEach(e => e.style.backgroundColor = "white");
});
}
addCircleListeners(red, "red");
addCircleListeners(green, "green", red);
addCircleListeners(blue, "blue", red, green);
Upvotes: 1
Reputation: 8660
You can use a function composition approach. This is done by looking over what you're trying to accomplish within your code, point by point, and then builds up functions to facilitate each minor task until the larger task is accomplished.
This approach breaks things down into two distinct parts: Setup and Execution.
Setup is where you design all your functions and variables, Execution is putting it all to work.
// SETUP:
const [ red, green, blue ] = document.querySelectorAll( ".circle" ),
bg = n => c => n.style.backgroundColor = c,
colorMatch = ( cs ) => ( fn, i ) => fn( cs[ i ] ? cs[ i ] : cs[ cs.length - 1 ] ),
bgs = ( ...ns ) => ( ...cs ) => () => ns.map( bg ).map( colorMatch( cs ) ),
ev = n => t => fn => n.addEventListener( t, () => fn() ),
mout = n => fn => ev( n )( "mouseout" )( fn ),
mover = n => fn => ev( n )( "mouseover" )( fn ),
hover = ( n, {over, out} ) => ( mover( n )( over ), mout( n )( out ) );
// EXECUTION:
hover( red, {
over: bgs( red )( "red" ),
out: bgs( red )( "white" )
} );
hover( green, {
over: bgs( red, green )( "green" ),
out: bgs( red, green )( "white" )
} );
hover( blue, {
over: bgs( red, green, blue )( "blue" ),
out: bgs( red, green, blue )( "white" )
} );
const [ red, green, blue ] = document.querySelectorAll( ".circle" ),
bg = n => c => n.style.backgroundColor = c,
colorMatch = ( cs ) => ( fn, i ) => fn( cs[ i ] ? cs[ i ] : cs[ cs.length - 1 ] ),
bgs = ( ...ns ) => ( ...cs ) => () => ns.map( bg ).map( colorMatch( cs ) ),
ev = n => t => fn => n.addEventListener( t, () => fn() ),
mout = n => fn => ev( n )( "mouseout" )( fn ),
mover = n => fn => ev( n )( "mouseover" )( fn ),
hover = ( n, {over, out} ) => ( mover( n )( over ), mout( n )( out ) );
hover( red, {
over: bgs( red )( "red" ),
out: bgs( red )( "white" )
} );
hover( green, {
over: bgs( red, green )( "green" ),
out: bgs( red, green )( "white" )
} );
hover( blue, {
over: bgs( red, green, blue )( "blue" ),
out: bgs( red, green, blue )( "white" )
} );
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
section{
margin: 100px auto 0 auto;
max-width: 700px;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
section .circle{
width: 200px;
height: 200px;
border-radius: 50%;
border: 1px solid #333;
margin: 0 auto;
cursor: pointer;
}
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="main.css">
<script defer src="main.js"></script>
</head>
<body>
<section>
<div id="circleOne" class="circle"></div>
<div id="circleTwo" class="circle"></div>
<div id="circleThree" class="circle"></div>
</section>
</body>
</html>
Two points of note -
Upvotes: 0
Reputation: 370759
Here's one option: make a function which accepts an array of elements and sets each of their backgrounds, and add a single mouseout listener to the container which sets all to white. No need for IDs, you can put each circle into a variable quickly with querySelectorAll
and destructuring:
const bgcolorAll = (arr, color) => arr.forEach(elm => elm.style.backgroundColor = color);
const section = document.querySelector('section');
section.addEventListener('mouseout', () => {
bgcolorAll([red, green, blue], 'white');
});
const [red, green, blue] = document.querySelectorAll('section > div');
red.addEventListener("mouseover", () => {
bgcolorAll([red], 'red');
});
green.addEventListener("mouseover", () => {
bgcolorAll([red, green], 'green');
});
blue.addEventListener("mouseover", () => {
bgcolorAll([red, green, blue], 'blue');
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
section {
margin: 100px auto 0 auto;
max-width: 700px;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
section .circle {
width: 200px;
height: 200px;
border-radius: 50%;
border: 1px solid #333;
margin: 0 auto;
cursor: pointer;
}
<section>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
</section>
Or, even more DRY, don't select the individual circles at all, and instead use an array:
const bgcolorAll = (arr, color) => arr.forEach(elm => elm.style.backgroundColor = color);
const section = document.querySelector('section');
const circles = [...document.querySelectorAll('section > div')];
section.addEventListener('mouseout', () => {
bgcolorAll(circles, 'white');
});
const colors = ['red', 'green', 'blue'];
section.addEventListener('mouseover', ({ target }) => {
if (target.matches('.circle')) {
const index = circles.indexOf(target)
bgcolorAll(
circles.slice(0, index + 1),
colors[index]
);
}
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
section {
margin: 100px auto 0 auto;
max-width: 700px;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
section .circle {
width: 200px;
height: 200px;
border-radius: 50%;
border: 1px solid #333;
margin: 0 auto;
cursor: pointer;
}
<section>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
</section>
You can also achieve this with CSS only:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
section {
margin: 100px auto 0 auto;
max-width: 700px;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-areas: 'red green blue';
}
section .circle {
width: 200px;
height: 200px;
border-radius: 50%;
border: 1px solid #333;
margin: 0 auto;
cursor: pointer;
}
.circle:nth-child(1) {
grid-area: blue;
}
.circle:nth-child(1):hover,
.circle:nth-child(1):hover ~ .circle:nth-child(2),
.circle:nth-child(1):hover ~ .circle:nth-child(3) {
background-color: blue;
}
.circle:nth-child(2) {
grid-area: green;
}
.circle:nth-child(2):hover,
.circle:nth-child(2):hover ~ .circle:nth-child(3) {
background-color: green;
}
.circle:nth-child(3) {
grid-area: red;
}
.circle:nth-child(3):hover {
background-color: red;
}
<section>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
</section>
Upvotes: 6
Reputation: 1
This is the simplest, DRY, code I could come up with at the spur of the moment
const circles = [...document.querySelectorAll('div.circle')];
circles.forEach(el => {
el.addEventListener('mouseover', function(e) { // use function not arrow so this is current element
const color = this.dataset.color;
circles.some(x => {
x.style.backgroundColor = color;
return x === this;
});
});
el.addEventListener('mouseout', function(e) { // use function not arrow so this is current element
circles.some(x => {
x.style.backgroundColor = 'white';
return x === this;
});
});
})
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
section {
margin: 100px auto 0 auto;
max-width: 700px;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
section .circle {
width: 200px;
height: 200px;
border-radius: 50%;
border: 1px solid #333;
margin: 0 auto;
cursor: pointer;
}
<section>
<div id="circleOne" class="circle" data-color="red"></div>
<div id="circleTwo" class="circle" data-color="green"></div>
<div id="circleThree" class="circle" data-color="blue"></div>
</section>
Upvotes: 2