How to select and then deselect area?

Viewed 70

I am a beginner learning from tutorials and have created a product colour customiser. Currently, there is only one way to deselect the highlighted area, and that is by clicking outside the image.

How do I select and highlight an area, and then click and select it again to deselect that specific area?

const overlays = [];
document.querySelectorAll(".product").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) {
  // get the hex color
  let hex = e.target.getAttribute("data-hex");
  overlays.forEach((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: relative;
  z-index: 1;
  width: 85%;
  height: auto;
}

[data-test] {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: start;
  padding-left: 15px;
  padding-right: 15px;
}

[data-test] span.color {
  flex-shrink: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  width: 60px;
  padding-bottom: 9px;
}

[data-test] span.color span {
  height: 23px;
  width: 20px;
  background: var(--color);
  clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
  margin-bottom: -6px;
}

[data-test] span.color span:first-child {
  margin-left: 1px;
}

.highlight {
  stroke-width: 10px;
  stroke: #000;
}

.red {
  z-index: 2;
  padding: 100px;
}
<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>
</div>
<div class="text"><span class="product-number"></span></div>
</div>

<div class="mySlides fade">
  <img>
  <div class="text"><span class="product-number"></span></div>
</div>

<div class="mySlides fade">
  <img>
  <div class="text"><span class="product-number"></span></div>
</div>

</div>

</main>

<section class="color-select">

  <div data-test>
    <span class="color red">
      <span class="color-selected" style="--color: #ff0000 " data-hex="#ff0000"></span>
      <span class="color-selected" style="--color: #660000 " data-hex="#660000"></span>
      <span class="color-selected" style="--color: #990000 " data-hex="#990000"></span>
      <span class="color-selected" style="--color: #cc0000 " data-hex="#cc0000"></span>
      <span class="color-selected" style="--color: #ff6666 " data-hex="#ff6666"></span>
      <span class="color-selected" style="--color: #ff9999 " data-hex="#ff9999"></span>
      <span class="color-selected" style="--color: #ffcccc " data-hex="#ffcccc"></span>
    </span>

  </div>
</section>

3 Answers

I made the following adjustments to your chooseProduct function to achieve the desired effect-- obviously you'll probably want to restructure, rename, and properly abstract this functionality, but it meets your requirements:

function chooseProduct(e) {
  // iterate over the overlays...
  for (let i = 0; i < overlays.length; i += 1) {
    let currentOverlay = overlays[i];
    // and if the item you just clicked is already in overlays...
    if (currentOverlay.isSameNode(e.target)) {
      // remove it from overlays...
      overlays.splice(i, 1);
      // remove its highlight class...
      e.target.classList.remove('highlight')
      // and exit the function completely-- we don't want to re-add it
      return;
    }
  }
  // else;

  overlays.push(e.target);
  overlays.forEach((overlay) => overlay.classList.add("highlight"));

}

You can see it working in the below snippet:

const overlays = [];
document.querySelectorAll(".product").forEach(function(path) {
  path.onclick = chooseProduct;
});

function chooseProduct(e) {
  // iterate over the overlays...
  for (let i = 0; i < overlays.length; i += 1) {
    let currentOverlay = overlays[i];
    // and if the item you just clicked is already in overlays...
    if (currentOverlay.isSameNode(e.target)) {
      // remove it from overlays...
      overlays.splice(i, 1);
      // remove its highlight class...
      e.target.classList.remove('highlight')
      // and exit the function completely-- we don't want to re-add it
      return;
    }
  }
  // else;

  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) {
  // get the hex color
  let hex = e.target.getAttribute("data-hex");
  overlays.forEach((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: relative;
  z-index: 1;
  width: 85%;
  height: auto;
}

[data-test] {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: start;
  padding-left: 15px;
  padding-right: 15px;
}

[data-test] span.color {
  flex-shrink: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  width: 60px;
  padding-bottom: 9px;
}

[data-test] span.color span {
  height: 23px;
  width: 20px;
  background: var(--color);
  clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
  margin-bottom: -6px;
}

[data-test] span.color span:first-child {
  margin-left: 1px;
}

.highlight {
  stroke-width: 10px;
  stroke: #000;
}

.red {
  z-index: 2;
  padding: 100px;
}
<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>
</div>
<div class="text"><span class="product-number"></span></div>
</div>

<div class="mySlides fade">
  <img>
  <div class="text"><span class="product-number"></span></div>
</div>

<div class="mySlides fade">
  <img>
  <div class="text"><span class="product-number"></span></div>
</div>

</div>

</main>

<section class="color-select">

  <div data-test>
    <span class="color red">
      <span class="color-selected" style="--color: #ff0000 " data-hex="#ff0000"></span>
    <span class="color-selected" style="--color: #660000 " data-hex="#660000"></span>
    <span class="color-selected" style="--color: #990000 " data-hex="#990000"></span>
    <span class="color-selected" style="--color: #cc0000 " data-hex="#cc0000"></span>
    <span class="color-selected" style="--color: #ff6666 " data-hex="#ff6666"></span>
    <span class="color-selected" style="--color: #ff9999 " data-hex="#ff9999"></span>
    <span class="color-selected" style="--color: #ffcccc " data-hex="#ffcccc"></span>
    </span>

  </div>
</section>

  1. Click on product class to check if it has highlight class exists.
  2. If there is a highlight class, remove it.
  3. If there is no highlight class then to add

const product_box = document.getElementsByClassName('product');
const color_piker = document.getElementsByClassName('color-selected');

for (const box of product_box) {
    box.addEventListener('click', function (e) {
        if (box.classList.contains('highlight')) {
            box.classList.remove('highlight');
        } else {
            box.classList.add('highlight');
        }
    });
}

document.addEventListener('click', function (e) {
    const target = e.target;
    if (!target.classList.contains('product') && !target.classList.contains('color-selected')) {
        for (const box of product_box) {
            box.classList.remove('highlight');
        }
    }
});

for (const color of color_piker) {
    color.addEventListener('click', function (e) {
        const color_value = color.getAttribute('data-hex');
        const product_box = document.querySelectorAll('.product.highlight');

        for (const product of product_box) {
            product.style.fill = color_value;
            product.classList.remove('highlight');
        }
    });
}
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: relative;
    z-index: 1;
    width: 85%;
    height: auto;
}

[data-test] {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: start;
    padding-left: 15px;
    padding-right: 15px;
}

[data-test] span.color {
    flex-shrink: 0;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    width: 60px;
    padding-bottom: 9px;
}

[data-test] span.color span {
    height: 23px;
    width: 20px;
    background: var(--color);
    clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
    margin-bottom: -6px;
}

[data-test] span.color span:first-child {
    margin-left: 1px;
}

.highlight {
    stroke-width: 10px;
    stroke: #000;
}

.red {
    z-index: 2;
    padding: 100px;
}
<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>
</div>
<div class="text"><span class="product-number"></span></div>
</div>
<div class="mySlides fade">
    <img>
    <div class="text"><span class="product-number"></span></div>
</div>
<div class="mySlides fade">
    <img>
    <div class="text"><span class="product-number"></span></div>
</div>
</div>
</main>
<section class="color-select">
    <div data-test>
        <span class="color red">
        <span class="color-selected" style="--color: #ff0000 " data-hex="#ff0000"></span>
        <span class="color-selected" style="--color: #660000 " data-hex="#660000"></span>
        <span class="color-selected" style="--color: #990000 " data-hex="#990000"></span>
        <span class="color-selected" style="--color: #cc0000 " data-hex="#cc0000"></span>
        <span class="color-selected" style="--color: #ff6666 " data-hex="#ff6666"></span>
        <span class="color-selected" style="--color: #ff9999 " data-hex="#ff9999"></span>
        <span class="color-selected" style="--color: #ffcccc " data-hex="#ffcccc"></span>
        </span>
    </div>
</section>

to toggle the highlight class you need the toggle function of classList, this is the code, also, I change some names

const overlays = [];

document.querySelectorAll(".product").forEach((path) => {
  path.addEventListener("click", toggleProductHighlight);
});

function toggleProductHighlight({ target }) {
  const highlightClass = "highlight"
  
  target.classList.toggle(highlightClass);
  
  if(!target.classList.contains(highlightClass)) overlays.push(target);
}

var removeHighlight = function ({ target }) {
  if (
    !target.classList.contains("product") &&
    !target.classList.contains("color")
  ) {
    overlays.length = 0;
    document.querySelectorAll(".product").forEach((prod) => {
      prod.classList.remove("highlight");
    });
  }
};

document.onclick = removeHighlight;

const colorsEl = document.getElementsByClassName("color");

function changeColor({ target }) {
  const hexColor = target.getAttribute("data-hex");

  overlays.forEach((overlay) => (overlay.style.fill = hexColor));
}

[...colorsEl].forEach((el) => el.addEventListener("click", changeColor));
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: relative;
  z-index: 1;
  width: 85%;
  height: auto;
}

[data-test] {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: start;
  padding-left: 15px;
  padding-right: 15px;
}

[data-test] span.color {
  flex-shrink: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  width: 60px;
  padding-bottom: 9px;
}

[data-test] span.color span {
  height: 23px;
  width: 20px;
  background: var(--color);
  clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
  margin-bottom: -6px;
}

[data-test] span.color span:first-child {
  margin-left: 1px;
}

.highlight {
  stroke-width: 10px;
  stroke: #000;
}

.red {
  z-index: 2;
  padding: 100px;
}
<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>
</div>
<div class="text"><span class="product-number"></span></div>
</div>

<div class="mySlides fade">
  <img>
  <div class="text"><span class="product-number"></span></div>
</div>

<div class="mySlides fade">
  <img>
  <div class="text"><span class="product-number"></span></div>
</div>

</div>

</main>

<section class="color-select">

  <div data-test>
    <span class="color red">
      <span class="color-selected" style="--color: #ff0000 " data-hex="#ff0000"></span>
      <span class="color-selected" style="--color: #660000 " data-hex="#660000"></span>
      <span class="color-selected" style="--color: #990000 " data-hex="#990000"></span>
      <span class="color-selected" style="--color: #cc0000 " data-hex="#cc0000"></span>
      <span class="color-selected" style="--color: #ff6666 " data-hex="#ff6666"></span>
      <span class="color-selected" style="--color: #ff9999 " data-hex="#ff9999"></span>
      <span class="color-selected" style="--color: #ffcccc " data-hex="#ffcccc"></span>
    </span>

  </div>
</section>

Related