I am a beginner using tutorials to learn how to apply different colours to different parts of products.
How do I highlight multiple classes on click and change the colour of the selected boxes, instead of only one box?
For example, when selecting a box, I can select not just a single class, but multiple class, then change multiple classes as the same color?
let overlay;
document.querySelectorAll('#product-a, #product-b, #product-c').forEach(function(path) {
path.onclick = chooseProduct;
})
function chooseProduct(e) {
if (overlay) overlay.classList.remove('highlight')
overlay = e.target
overlay.classList.add('highlight')
}
var removeHighlight = function(e) {
var products = document.querySelectorAll('.product');
if (!e.target.classList.contains('product') && !e.target.classList.contains('color')) {
overlay = null;
document.querySelectorAll('.product').forEach(function(prod) {
prod.classList.remove('highlight');
});
}
}
document.onclick = removeHighlight;
var el = document.getElementsByClassName("color");
for (var i = 0; i < el.length; i++) {
el[i].onclick = changeColor;
}
function changeColor(e) {
let hex = e.target.getAttribute("data-hex");
overlay.style.fill = hex;
}
#container {
height: 200px;
width: 200px;
}
#product-svg {
position: relative;
z-index: 2;
background-size: 100%;
background-repeat: no-repeat;
background-position: 50%;
mix-blend-mode: multiply;
}
path {
fill: #CCCCCC;
}
.colors {
display: flex;
position: fixed;
bottom: 2em;
right: 2em;
z-index: 3;
}
.color {
height: 36px;
width: 36px;
margin-left: 0.5em;
border-radius: 18px;
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.3);
border: 2px solid #aaa;
cursor: pointer;
}
.highlight {
stroke-width: 10px;
stroke: #000;
}
<div id="container">
<svg id="product-svg" viewBox="0 0 744 1074">
<path id="product-a" class="product" d="M51 207.5L51 348L686 348L686 67L51 67L51 207.5Z" />
<path id="product-b" class="product" d="M51 544.5L51 685L686 685L686 404L51 404L51 544.5Z" />
<path id="product-c" class="product" d="M51 883.5L51 1024L686 1024L686 743L51 743L51 883.5Z" />
</svg>
<img id="background-image" src="boxes.jpg" alt="">
</div>
<div class="colors">
<div class="color" style="background-color: #ff0000" data-hex="#ff0000"></div>
<div class="color" style="background-color: #ffff33" data-hex="#ffff33"></div>
<div class="color" style="background-color: #3399ff" data-hex="#3399ff"></div>
</div>
You can simply create a code that add the boxes to an array as the user clicks on them. That way, the selected ones will be contained in that array. I think this solves your problem:
const overlays = [];
document.querySelectorAll('#product-a, #product-b, #product-c').forEach(function(path) {
path.onclick = chooseProduct;
})
function chooseProduct(e) {
overlays.push(e.target);
overlays.forEach((overlay) => overlay.classList.add('highlight'));
}
var removeHighlight = function(e) {
var products = document.querySelectorAll('.product');
if(!e.target.classList.contains('product') && !e.target.classList.contains('color')){
overlays.length = 0;
document.querySelectorAll('.product').forEach(function(prod){
prod.classList.remove('highlight');
});
}
}
document.onclick = removeHighlight;
var el = document.getElementsByClassName("color");
for (var i = 0; i < el.length; i++) {
el[i].onclick = changeColor;
}
function changeColor(e) {
let hex = e.target.getAttribute("data-hex");
overlays.forEach((overlay) => overlay.style.fill = hex);
}
#container {
height: 200px;
width: 200px;
}
#product-svg {
position: relative;
z-index: 2;
background-size: 100%;
background-repeat: no-repeat;
background-position: 50%;
mix-blend-mode: multiply;
}
path {
fill: #CCCCCC;
}
.colors {
display: flex;
position: fixed;
bottom: 2em;
right: 2em;
z-index: 3;
}
.color {
height: 36px;
width: 36px;
margin-left: 0.5em;
border-radius: 18px;
box-shadow: 0px 4px 10px rgba(0,0,0,0.3);
border: 2px solid #aaa;
cursor: pointer;
}
.highlight {
stroke-width: 10px;
stroke: #000;
}
<div id="container">
<svg id="product-svg" viewBox="0 0 744 1074">
<path id="product-a" class="product" d="M51 207.5L51 348L686 348L686 67L51 67L51 207.5Z" />
<path id="product-b" class="product" d="M51 544.5L51 685L686 685L686 404L51 404L51 544.5Z" />
<path id="product-c" class="product" d="M51 883.5L51 1024L686 1024L686 743L51 743L51 883.5Z" />
</svg>
<img id="background-image" src="boxes.jpg" alt="">
</div>
<div class="colors">
<div class="color" style="background-color: #ff0000" data-hex="#ff0000"></div>
<div class="color" style="background-color: #ffff33" data-hex="#ffff33"></div>
<div class="color" style="background-color: #3399ff" data-hex="#3399ff"></div>
</div>
Related
Hello I have used google dropdown in my react project and need to handle event on focus of the dropdown I couldn't make it can you help me?
As you can see in below output there is a div placeholder which i need to hide on focus. I have tried a few ways but it didn't work so I need some expert help. I am stuck in here.
code
<GooglePlacesAutocomplete
apiKey={'XXXXXXX'}
selectProps={{
value,
backspaceRemovesValue: true,
controlShouldRenderValue: true,
isClearable: true,
placeholder: strings["SELECT_LOCATION"],
onChange: async (e) => {
console.log("E : ", e);
setValue(e)
if (e) {
let location = await getLocationAction(e.value?.place_id)
let address = {
address: e.label,
location
}
getLocation && getLocation(address)
} else {
getLocation(null);
}
}
}
}
/>
Output
<div class="googleDropdown">
<div class="css-2b097c-container">
<span
aria-live="polite"
aria-atomic="false"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
></span>
<div class="css-yk16xz-control">
<div class="css-1hwfws3">
<div class="css-1wa3eu0-placeholder">Select location</div>
<div class="css-1g6gooi">
<div class="" style="display: inline-block">
<input
autocapitalize="none"
autocomplete="off"
autocorrect="off"
id="react-select-3-input"
spellcheck="false"
tabindex="0"
type="text"
aria-autocomplete="list"
value=""
style="
box-sizing: content-box;
width: 2px;
background: 0px center;
border: 0px;
font-size: inherit;
opacity: 1;
outline: 0px;
padding: 0px;
color: inherit;
"
/>
<div
style="
position: absolute;
top: 0px;
left: 0px;
visibility: hidden;
height: 0px;
overflow: scroll;
white-space: pre;
font-size: 16px;
font-family: Comfortaa, Montserrat, sans-sarif;
font-weight: 400;
font-style: normal;
letter-spacing: normal;
text-transform: none;
"
></div>
</div>
</div>
</div>
<div class="css-1wy0on6">
<span class="css-1okebmr-indicatorSeparator"></span>
<div class="css-tlfecz-indicatorContainer" aria-hidden="true">
<svg
height="20"
width="20"
viewBox="0 0 20 20"
aria-hidden="true"
focusable="false"
class="css-tj5bde-Svg"
>
<path
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
></path>
</svg>
</div>
</div>
</div>
</div>
</div>
code
How do I replace the the class highlight with an id instead, so when I click outside of the products, it doesn't highlight that area?
I included the window.onload section with what I want to do, however I don't know how to change the class highlight into id. I can't think of an easier way than changing the class and then using the window.onload.
let overlay;
document.querySelectorAll('.product').forEach(function(path) {
path.onclick = chooseProduct;
})
function chooseProduct(e) {
if (overlay) overlay.classList.remove('highlight')
overlay = e.target
overlay.classList.add('highlight')
}
//What I want to add to the highlight class using id to remove black border when click outside of the products
// window.onload = function(){
// var hide = document.getElementById('?');
// document.onclick = function(e){
// if(e.target.id !== '?'){
// hide.style.display = 'none';
// }
// };
// };
var el = document.getElementsByClassName("color");
for (var i = 0; i < el.length; i++) {
el[i].onclick = changeColor;
}
function changeColor(e) {
let hex = e.target.getAttribute("data-hex");
if (overlay) overlay.style.fill = hex;
}
body,
html {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
}
#container {
height: 200px;
width: 200px;
}
#product-svg {
position: relative;
z-index: 2;
background-size: 100%;
background-repeat: no-repeat;
background-position: 50%;
mix-blend-mode: multiply;
}
path {
fill: #CCCCCC;
}
#background-image {
position: absolute;
top: 0;
left: 0;
height: 200px;
width: 200px;
height: auto;
z-index: 1;
}
.colors {
display: flex;
position: fixed;
bottom: 2em;
right: 2em;
z-index: 3;
}
.color {
height: 36px;
width: 36px;
margin-left: 0.5em;
border-radius: 18px;
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.3);
border: 2px solid #aaa;
cursor: pointer;
}
.highlight {
stroke-width: 10px;
stroke: #000;
}
<div id="container">
<svg id="product-svg" viewBox="0 0 744 1074">
<path class="product" d="M51 207.5L51 348L686 348L686 67L51 67L51 207.5Z" />
<path class="product" d="M51 544.5L51 685L686 685L686 404L51 404L51 544.5Z" />
<path class="product" d="M51 883.5L51 1024L686 1024L686 743L51 743L51 883.5Z" />
</svg>
<img id="background-image" src="boxes.jpg" alt="">
</div>
<div class="colors">
<div class="color" style="background-color: #ff0000" data-hex="#ff0000"></div>
<div class="color" style="background-color: #ffff33" data-hex="#ffff33"></div>
<div class="color" style="background-color: #3399ff" data-hex="#3399ff"></div>
</div>
your code is working as expected, except the part which you have mentioned in the question which highlight style of the selected product is not removed when you clicked outside the product.
In order to do that, simply I have added an Event Listener (removeHighlight()) by document.onclick = removeHighlight; to the whole document on click event. So whenever you click on anywhere in the DOM, that event listener will be triggered. What I'm do inside EventListener function is removing the class highlight from all products if and only if the click event was not triggered by clicking on either products or colours. Additionally I'm setting the overlay=null in order to remove the reference of the previously selected product, then clicking on colors won't fill the previously selected product with selected color if same conditions met and until you click again on a product.
let overlay;
document.querySelectorAll('.product').forEach(function(path) {
path.onclick = chooseProduct;
})
function chooseProduct(e) {
if (overlay) overlay.classList.remove('highlight')
overlay = e.target
overlay.classList.add('highlight')
}
var removeHighlight = function(e) {
var products = document.querySelectorAll('.product');
if(!e.target.classList.contains('product') && !e.target.classList.contains('color')){
overlay = null;
document.querySelectorAll('.product').forEach(function(prod){
prod.classList.remove('highlight');
});
}
}
document.onclick = removeHighlight;
var el = document.getElementsByClassName("color");
for (var i = 0; i < el.length; i++) {
el[i].onclick = changeColor;
}
function changeColor(e) {
let hex = e.target.getAttribute("data-hex");
if (overlay) overlay.style.fill = hex;
}
body,
html {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
}
#container {
height: 200px;
width: 200px;
}
#product-svg {
position: relative;
z-index: 2;
background-size: 100%;
background-repeat: no-repeat;
background-position: 50%;
mix-blend-mode: multiply;
}
path {
fill: #CCCCCC;
}
#background-image {
position: absolute;
top: 0;
left: 0;
height: 200px;
width: 200px;
height: auto;
z-index: 1;
}
.colors {
display: flex;
position: fixed;
bottom: 2em;
right: 2em;
z-index: 3;
}
.color {
height: 36px;
width: 36px;
margin-left: 0.5em;
border-radius: 18px;
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.3);
border: 2px solid #aaa;
cursor: pointer;
}
.highlight {
stroke-width: 10px;
stroke: #000;
}
<div id="container">
<svg id="product-svg" viewBox="0 0 744 1074">
<path class="product" d="M51 207.5L51 348L686 348L686 67L51 67L51 207.5Z" />
<path class="product" d="M51 544.5L51 685L686 685L686 404L51 404L51 544.5Z" />
<path class="product" d="M51 883.5L51 1024L686 1024L686 743L51 743L51 883.5Z" />
</svg>
<img id="background-image" src="boxes.jpg" alt="">
</div>
<div class="colors">
<div class="color" style="background-color: #ff0000" data-hex="#ff0000"></div>
<div class="color" style="background-color: #ffff33" data-hex="#ffff33"></div>
<div class="color" style="background-color: #3399ff" data-hex="#3399ff"></div>
</div>
jsfiddle
When a behavior is a result of a click event occurring anywhere but the tags meant to be clicked, it's most effective to register the document Object as the click event listener and control all clicks by Event Delegation.
register the document Object as the click event listener
document.onclick = selectPath;
the click event handler is selectPath(e)
collect all .product into a NodeList
const paths = document.querySelectorAll('.product');
if the user clicked any .product remove .highlight class from all .product and then add .highlight class to the .product user clicked
if (e.target.matches('.product')) {
paths.forEach(path => path.classList.remove('highlight'));
e.target.classList.add('highlight');
but if the user clicked any .color, then get it's [data-hex] value, and fill .highlight with the hex color if it exists (re: .highlight)
else if (e.target.matches('.color')) {
let hex = e.target.dataset.hex;
let selected = document.querySelector('.highlight');
if (selected) selected.style.fill = hex;
otherwise just remove the .highlight class if it exists.
paths.forEach(path => path.classList.remove('highlight'));
document.onclick = selectPath;
function selectPath(e) {
const paths = document.querySelectorAll('.product');
if (e.target.matches('.product')) {
paths.forEach(path => path.classList.remove('highlight'));
e.target.classList.add('highlight');
} else if (e.target.matches('.color')) {
let hex = e.target.dataset.hex;
let selected = document.querySelector('.highlight');
if (selected) selected.style.fill = hex;
} else {
paths.forEach(path => path.classList.remove('highlight'));
}
};
body,
html {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
}
.container {
height: 200px;
width: 200px;
}
.svg {
position: relative;
z-index: 2;
background-size: 100%;
background-repeat: no-repeat;
background-position: 50%;
mix-blend-mode: multiply;
}
path {
fill: #CCCCCC;
}
.background {
position: absolute;
top: 0;
left: 0;
height: 200px;
width: 200px;
height: auto;
z-index: 1;
}
.circles {
position: fixed;
bottom: 2em;
right: 2em;
z-index: 3;
}
.color {
display: inline-block;
height: 36px;
width: 36px;
margin-left: 0.5em;
border-radius: 18px;
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.3);
border: 2px solid #aaa;
cursor: pointer;
}
.highlight {
stroke-width: 10px;
stroke: #000;
}
<div class="container">
<svg class="svg" viewBox="0 0 744 1074">
<path class="product" d="M51 207.5L51 348L686 348L686 67L51 67L51 207.5Z" />
<path class="product" d="M51 544.5L51 685L686 685L686 404L51 404L51 544.5Z" />
<path class="product" d="M51 883.5L51 1024L686 1024L686 743L51 743L51 883.5Z" />
</svg>
<img class="background" src="boxes.jpg" alt="">
</div>
<div class="circles">
<div class="color" style="background-color: #ff0000" data-hex="#ff0000"></div>
<div class="color" style="background-color: #ffff33" data-hex="#ffff33"></div>
<div class="color" style="background-color: #3399ff" data-hex="#3399ff"></div>
</div>
You are setting "highlight" class only for products so you don't need id for what you are going to do.
you can just check if element which you clicked on has "product" class then first you remove all "highlight" classes and then add it to clicked product. If element doesn't have a "product" class then you just remove all "highlight" classes.
window.onload = function(){
document.onclick = function(e){
if(e.target.classList.contains("product")){
resetClass()
e.target.classList.add("highlight")
}else{
resetClass()
}
};
};
function resetClass(){
document.querySelectorAll(".highlight").forEach(item => {
item.classList.remove("highlight")
})
}
Is there a way to make both the html based div and the SVG based line in the snippet below respond to javascript?
I know of the pointer-events css property. But it seems to be all or nothing to me - I want both elements to respond to events ...
#main {
position: relative;
width: 600px;
}
svg {
border: 1px solid blue;
red;
position: absolute;
top: 0;
width: 100%;
height: 100%;
stroke: rgb(155, 155, 155);
stroke-width: 10
}
#box-container {
display: flex;
}
.box {
width: 200px;
background-color: pink;
height: 100px;
}
<div>
<p>
Html based boxes should be clickable
</p>
<p>
SVG based line should be clickable
</p>
</div>
<div id="main">
<svg>
<line onClick="alert('line')" x1="0" y1="0" x2="600" y2="100" />
</svg>
<div style="display:flex;">
<div onClick="alert('one')" class="box">
One
</div>
<div onClick="alert('two')" style="margin-left:auto" class="box">
Two
</div>
</div>
</div>
#main {
position: relative;
width: 600px;
}
svg {
border: 1px solid blue;
red;
position: absolute;
top: 0;
width: 100%;
height: 100%;
stroke: rgb(155, 155, 155);
stroke-width: 10
}
#box-container {
display: flex;
}
.box {
width: 200px;
background-color: pink;
height: 100px;
}
<div>
<p>
Html based boxes should be clickable
</p>
<p>
SVG based line should be clickable
</p>
</div>
<div id="main">
<svg>
<line style="pointer-events:all;" onClick="alert('line')" x1="0" y1="0" x2="600" y2="100" />
</svg>
<div style="display:flex;">
<div onClick="alert('one')" class="box">
One
</div>
<div onClick="alert('two')" style="margin-left:auto" class="box">
Two
</div>
</div>
</div>
You can't. But you can trigger click event on another element like this.
function lineClick() {
alert('line')
document.querySelector("#one").click()
}
#main {
position: relative;
width: 600px;
}
svg {
border: 1px solid blue;
position: absolute;
top: 0;
width: 100%;
height: 100%;
stroke: rgb(155, 155, 155);
stroke-width: 10
}
#box-container {
display: flex;
}
.box {
width: 200px;
background-color: pink;
height: 100px;
}
<div>
<p>
Html based boxes should be clickable
</p>
<p>
SVG based line should be clickable
</p>
</div>
<div id="main">
<svg>
<line onClick="lineClick()" x1="0" y1="0" x2="600" y2="100" />
</svg>
<div style="display:flex;">
<div onClick="alert('one')" class="box" id="one">
One
</div>
<div onClick="alert('two')" style="margin-left:auto" class="box">
Two
</div>
</div>
</div>
You can set svg{pointer-events:none}(won't respond to mouse events). Next you can give the line{pointer-events:all}/(will respond to mouse events). If you need one to respond on the same event as the line use #dgknca's solution.
#main {
position: relative;
width: 600px;
}
svg {
border: 1px solid blue;
red;
position: absolute;
top: 0;
width: 100%;
height: 100%;
stroke: rgb(155, 155, 155);
stroke-width: 10;
pointer-events:none;
}
line{pointer-events:all;}
#box-container {
display: flex;
}
.box {
width: 200px;
background-color: pink;
height: 100px;
}
<div>
<p>
Html based boxes should be clickable
</p>
<p>
SVG based line should be clickable
</p>
</div>
<div id="main">
<svg>
<line onClick="alert('line')" x1="0" y1="0" x2="600" y2="100" />
</svg>
<div style="display:flex;">
<div onClick="alert('one')" class="box">
One
</div>
<div onClick="alert('two')" style="margin-left:auto" class="box">
Two
</div>
</div>
</div>
If you want a single delegated Event handler, you can use elementsFromPoint(x,y)
<script>
function clickHandler(evt) {
// get all Elements under this X,Y point
let elements = document.elementsFromPoint(evt.clientX, evt.clientY);
// disregard the SVG element
let element = elements[(elements[0].nodeName == "svg") ? 1 : 0];
console.log(elements);
console.log(element);
}
</script>
<style>
#main {
position: relative;
width: 600px;
}
svg {
position: absolute;
top: 0;
width: 100%;
height: 100%;
stroke: green;
stroke-width: 10
}
.box {
width: 200px;
background-color: pink;
height: 100px;
}
</style>
<div id="main" onclick="clickHandler(event)">
<svg>
<line x1="0" y1="0" x2="600" y2="100" />
</svg>
<div style="display:flex;">
<div class="box" id=ONE>
One
</div>
<div style="margin-left:auto" class="box" id=TWO>
Two
</div>
</div>
</div>
I'm messing about with this for a friend and I'm having some issues. I use
https://codepen.io/dinos6/pen/XWbBMOw
This is what I have so far:
var svgname = ('Trim-shape');
var sec = document.getElementsByClassName("section");
for (var i = 0; i < sec.length; i++) {
sec[i].onclick = changeSection;
}
function changeSection(e) {
// get the section name
let secname = e.target.getAttribute("data-text");
// document.write (secname);
svgname = secname;
// document.write (svgname);
}
// Reference the color shape that was drawn over the image
var overlay = document.getElementById(svgname);
// Click on a color
var el = document.getElementsByClassName("color");
for (var i = 0; i < el.length; i++) {
el[i].onclick = changeColor;
}
function changeColor(e) {
// get the hex color
let hex = e.target.getAttribute("data-hex");
// set the hex color
overlay.style.fill = hex;
}
body,
html {
margin: 0;
padding: 0;
height: 100%;
}
*,
*:before,
*:after {
box-sizing: border-box;
}
#container {
position: relative;
}
#Siding-svg {
position: relative;
z-index: 2;
mix-blend-mode: multiply;
}
#Siding-shape {
fill: #0009a2;
}
#Trim-svg {
position: relative;
z-index: 2;
mix-blend-mode: multiply;
}
#Trim-shape {
fill: #fff8a2;
}
#Door-svg {
position: relative;
z-index: 2;
mix-blend-mode: multiply;
}
#Door-shape {
fill: #b90500;
}
#background-image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: auto;
z-index: 1;
}
.colors {
display: flex;
position: fixed;
bottom: 2em;
right: 2em;
z-index: 3;
}
.color {
height: 36px;
width: 36px;
margin-left: 0.5em;
border-radius: 18px;
box-shadow: 0px 4px 10px rgba(0,0,0,0.3);
border: 2px solid #aaa;
cursor: pointer;
}
.building {
display: flex;
position: fixed;
bottom: 2em;
left: 2em;
z-index: 3;
}
.section {
height: 50px;
width: 75px;
margin-left: 0.5em;
border-radius: 18px;
box-shadow: 0px 4px 10px rgba(0,0,0,0.3);
border: 2px solid #aaa;
cursor: pointer;
}
<div id="container">
<svg id="Siding-svg" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1169 673">
<path id="Siding-shape" d="M733 290l-274 42 1 217 179 4-1-115 90-3 2 122h17l-5-269-9 2z M780 272l-428-84-232 180v10l140-19v-45s43-28 78-36 43-10 80-8 42 8 42 8l1 48 275-43 1-5z M259 363l-135 20v160l138 2V365l-3-2z M1098 388L747 288l3 269 352-47-4-122z" />
<svg id="Trim-svg" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1169 673">
<path id="Trim-shape" d="M777 225l-4 17-463-82L59 368v-9l251-211 467 77z" />
<svg id="Door-svg" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1169 673">
<path id="Door-shape" d="M278 320v290l181 16-4-343s-55-10-86 0-43 9-91 37z" />
</svg>
<img id="background-image" src="https://images2.imgbox.com/ca/36/PUSVCKm8_o.png" alt="">
</div>
<div class="colors">
<div class="color" style="background-color: #e1e851" data-hex="#e1e851"></div>
<div class="color" style="background-color: #8cd147" data-hex="#8cd147"></div>
<div class="color" style="background-color: #4a9ccf" data-hex="#4a9ccf"></div>
<div class="color" style="background-color: #661f45" data-hex="#661f45"></div>
<div class="color" style="background-color: #1e2024" data-hex="#1e2024"></div>
</div>
<div class="building">
<div class="section" style="background-color: #333333" data-text="Siding-shape">Siding</div>
<div class="section" style="background-color: #444444" data-text="Trim-shape">Trim</div>
<div class="section" style="background-color: #555555" data-text="Door-shape">Door</div>
</div>
I used this tutorial to get started:
https://tympanus.net/codrops/2019/09/03/how-to-dynamically-change-the-colors-of-product-images-using-css-blend-mode-and-svg/
I want the buttons on the left to select the different paths of the SVG and allow them to be colored independently, but also for the color selected for each path to highlight and save across different path selections (i.e. if I select door, then red, and then go to trim and back to door, the red swatch should still be highlighted).
I will be the first to say the code is incomplete and buggy, but I'm not that good with javascript and it's been annoying me for a while now.
Just a little change
You determine overlay only once in your code at starttime. So it isn't changed, if you click on the left buttons.
I transform it to a function which returns the value and then use the function
var svgname = ('Trim-shape');
var sec = document.getElementsByClassName("section");
for (var i = 0; i < sec.length; i++) {
sec[i].onclick = changeSection;
}
function changeSection(e) {
// get the section name
let secname = e.target.getAttribute("data-text");
// document.write (secname);
svgname = secname;
// document.write (svgname);
}
// Reference the color shape that was drawn over the image
function overlay() { return document.getElementById(svgname) };
// Click on a color
var el = document.getElementsByClassName("color");
for (var i = 0; i < el.length; i++) {
el[i].onclick = changeColor;
}
function changeColor(e) {
// get the hex color
let hex = e.target.getAttribute("data-hex");
// set the hex color
overlay().style.fill = hex;
}
body,
html {
margin: 0;
padding: 0;
height: 100%;
}
*,
*:before,
*:after {
box-sizing: border-box;
}
#container {
position: relative;
}
#Siding-svg {
position: relative;
z-index: 2;
mix-blend-mode: multiply;
}
#Siding-shape {
fill: #0009a2;
}
#Trim-svg {
position: relative;
z-index: 2;
mix-blend-mode: multiply;
}
#Trim-shape {
fill: #fff8a2;
}
#Door-svg {
position: relative;
z-index: 2;
mix-blend-mode: multiply;
}
#Door-shape {
fill: #b90500;
}
#background-image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: auto;
z-index: 1;
}
.colors {
display: flex;
position: fixed;
bottom: 2em;
right: 2em;
z-index: 3;
}
.color {
height: 36px;
width: 36px;
margin-left: 0.5em;
border-radius: 18px;
box-shadow: 0px 4px 10px rgba(0,0,0,0.3);
border: 2px solid #aaa;
cursor: pointer;
}
.building {
display: flex;
position: fixed;
bottom: 2em;
left: 2em;
z-index: 3;
}
.section {
height: 50px;
width: 75px;
margin-left: 0.5em;
border-radius: 18px;
box-shadow: 0px 4px 10px rgba(0,0,0,0.3);
border: 2px solid #aaa;
cursor: pointer;
}
<div id="container">
<svg id="Siding-svg" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1169 673">
<path id="Siding-shape" d="M733 290l-274 42 1 217 179 4-1-115 90-3 2 122h17l-5-269-9 2z M780 272l-428-84-232 180v10l140-19v-45s43-28 78-36 43-10 80-8 42 8 42 8l1 48 275-43 1-5z M259 363l-135 20v160l138 2V365l-3-2z M1098 388L747 288l3 269 352-47-4-122z" />
<svg id="Trim-svg" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1169 673">
<path id="Trim-shape" d="M777 225l-4 17-463-82L59 368v-9l251-211 467 77z" />
<svg id="Door-svg" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1169 673">
<path id="Door-shape" d="M278 320v290l181 16-4-343s-55-10-86 0-43 9-91 37z" />
</svg>
<img id="background-image" src="https://images2.imgbox.com/ca/36/PUSVCKm8_o.png" alt="">
</div>
<div class="colors">
<div class="color" style="background-color: #e1e851" data-hex="#e1e851"></div>
<div class="color" style="background-color: #8cd147" data-hex="#8cd147"></div>
<div class="color" style="background-color: #4a9ccf" data-hex="#4a9ccf"></div>
<div class="color" style="background-color: #661f45" data-hex="#661f45"></div>
<div class="color" style="background-color: #1e2024" data-hex="#1e2024"></div>
</div>
<div class="building">
<div class="section" style="background-color: #333333" data-text="Siding-shape">Siding</div>
<div class="section" style="background-color: #444444" data-text="Trim-shape">Trim</div>
<div class="section" style="background-color: #555555" data-text="Door-shape">Door</div>
</div>
I have a svg image with a mouse hover on four rect elements and an accordion with four buttons done with javascript. I want to connect each element by hovering them simultaneously (a rect and button accordion) and by clicking on a svg rect it will open a button accordion and by clicking on an accordion it will fill a rect. You can check my fiddle here: https://jsfiddle.net/pfrutuoso/zcsj8g05/2/
This is my html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div class="wrapper">
<div class="stage_info">
<button class="accordion">Stage 1</button>
<div class="panel">
<p>Information here..</p>
</div>
<button class="accordion">Stage 2</button>
<div class="panel">
<p>Information here..</p>
</div>
<button class="accordion">Stage 3</button>
<div class="panel">
<p>Information here..</p>
</div>
<button class="accordion">Stage 4</button>
<div class="panel">
<p>Information here..</p>
</div>
</div>
<div class="stage_img">
<map id="big_stage">
<svg version="1.1" id="stadium" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="100%" height="100%" viewBox="0 0 456.122 451.02" enable-background="new 0 0 456.122 451.02"
xml:space="preserve">
<rect id="stage4" x="25.51" y="25.51" fill="#1C6131" width="200" height="200"/>
<rect id="stage3" x="230.612" y="25.51" fill="#1C6131" width="200" height="200"/>
<rect id="stage2" x="25.51" y="229.592" fill="#1C6131" width="200" height="200"/>
<rect id="stage1" x="230.612" y="229.592" fill="#1C6131" width="200" height="200"/>
</svg>
</map>
</div>
</div>
</body>
</html>
My css:
.accordion {
background-color: #eee;
color: #444;
cursor: pointer;
padding: 18px;
width: 100%;
text-align: left;
border: none;
outline: none;
transition: 0.4s;
}
.active,
.accordion:hover {
background-color: #ccc;
}
.panel {
padding: 0 18px;
background-color: white;
display: none;
overflow: hidden;
}
.wrapper {
display: inline-block;
max-width: 1140px;
margin: 0 auto;
width: 100%;
text-align: center;
}
.stage_info,
.stage_img {
display: inline-block;
width: calc(50% - 80px);
vertical-align: top;
}
rect {
cursor: pointer;
z-index: 999;
position: relative;
}
rect:hover {
fill: #ccc;
pointer-events: all;
}
And my javascript:
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function() {
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.display === "block") {
panel.style.display = "none";
} else {
panel.style.display = "block";
}
});
}
I tried to do it as procedurally as possible as that seemed to be what you were going for in your JS.
I added value attributes to your stage buttons with the id of the corresponding rect elements, and I added a class called grey with the same properties as those applied when hovering.
let stgInf = document.querySelector(".stage_info");
let svg = document.querySelector("#stadium");
function onClick(num) {
let acc = stgInf.querySelector(`.accordion:nth-of-type(${num})`);
let panel = acc.nextElementSibling;
let rect = svg.querySelector(`rect#${acc.getAttribute("value")}`);
return () => {
acc.classList.toggle("active");
if (acc.classList.contains("active")) {
panel.style.display = "block";
rect.classList.add("grey");
} else {
panel.style.display = "none";
rect.classList.remove("grey");
}
}
}
function hover(num) {
let acc = stgInf.querySelector(`.accordion:nth-of-type(${num})`);
let rect = svg.querySelector(`rect#${acc.getAttribute("value")}`);
return (event) => {
if (event.type === "mouseenter") {
acc.classList.add("grey");
rect.classList.add("grey");
} else if (!acc.classList.contains("active")) {
acc.classList.remove("grey");
rect.classList.remove("grey");
}
}
}
let accs = stgInf.querySelectorAll(".accordion");
let i = 1;
for (let acc of accs) {
let rect = svg.querySelector(`rect#${acc.getAttribute("value")}`);
acc.addEventListener("click", onClick(i));
acc.addEventListener("mouseenter", hover(i));
acc.addEventListener("mouseout", hover(i));
rect.addEventListener("click", onClick(i));
rect.addEventListener("mouseenter", hover(i));
rect.addEventListener("mouseout", hover(i));
++i;
}
.accordion {
background-color: #eee;
color: #444;
cursor: pointer;
padding: 18px;
width: 100%;
text-align: left;
border: none;
outline: none;
transition: 0.4s;
}
.panel {
padding: 0 18px;
background-color: white;
display: none;
overflow: hidden;
}
.wrapper {
display: inline-block;
max-width: 1140px;
margin: 0 auto;
width: 100%;
text-align: center;
}
.stage_info,
.stage_img {
display: inline-block;
width: calc(50% - 80px);
vertical-align: top;
}
rect {
cursor: pointer;
z-index: 999;
position: relative;
}
.grey,
.active,
.accordion:hover,
rect:hover {
fill: #ccc;
background-color: #ccc;
pointer-events: all;
}
<div class="wrapper">
<div class="stage_info">
<button class="accordion" value="stage1">Stage 1</button>
<div class="panel">
<p>Information here..</p>
</div>
<button class="accordion" value="stage2">Stage 2</button>
<div class="panel">
<p>Information here..</p>
</div>
<button class="accordion" value="stage3">Stage 3</button>
<div class="panel">
<p>Information here..</p>
</div>
<button class="accordion" value="stage4">Stage 4</button>
<div class="panel">
<p>Information here..</p>
</div>
</div>
<div class="stage_img">
<map id="big_stage">
<svg version="1.1" id="stadium" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="100%" height="100%" viewBox="0 0 456.122 451.02" enable-background="new 0 0 456.122 451.02"
xml:space="preserve">
<rect id="stage4" x="25.51" y="25.51" fill="#1C6131" width="200" height="200"/>
<rect id="stage3" x="230.612" y="25.51" fill="#1C6131" width="200" height="200"/>
<rect id="stage2" x="25.51" y="229.592" fill="#1C6131" width="200" height="200"/>
<rect id="stage1" x="230.612" y="229.592" fill="#1C6131" width="200" height="200"/>
</svg>
</map>
</div>
</div>
EDIT
I implemented the hover functionality you mentioned in your comment by adding mouseenter and mouseout event listeners in addition to the click listeners.