Coloring HTML <area> based on click

Viewed 99

function eating(Num) {
  console.log("entered eating", Num);
  
  bodyEating = Num;
  
  for (let i = 0; i < Num; i++) {
    BODYEATING[0].background = "black";
  }
  console.log(BODYEATING[0]);
  //e.preventDefault();
}
<div class="container text-center">
  <img src="https://media.geeksforgeeks.org/wp-content/uploads/unique-rectangles-formed-using-n-unit-squares.png" usemap="#image-map">
  <map id="map" name="image-map">
    <area target="_self" alt="1" title="1" onclick="eating(1)" coords="21,385,24,309,100,309,101,385" shape="poly">
    <area target="_self" alt="2" title="2" onclick="eating(2)" coords="102,305,23,304,23,228,101,227" shape="poly">
    <area target="_self" alt="3" title="3" onclick="eating(3)" coords="103,225,26,228,25,149,99,151" shape="poly">
    <area target="_self" alt="4" title="4" onclick="eating(4)" coords="103,147,102,65,25,70,23,147" shape="poly">
  </map>
</div>

I have something like 5 images, each image is a pile of 10 squares. I want for each image when the user clicks, for example, the image on square 3, then, I will get in my code that the user clicked on square 3 and, also, the squares from 1 to 3 will have a background-color applied.

I did the first goal by making an image map. However, I can't make the coloring goal and I've searched a lot for it. Can anyone help?

3 Answers

Here's an alternative using an in-line SVG with an image background and polygon elements in the front. The handleSVGClick handler is on the SVG element itself to make things easier to manage with less repetition.

function handleSVGClick(event) {
  if (event.target.tagName === "polygon") {
    event.target.style.fill = `hsl(${Math.random() * 360}, 90%, 60%)`;
  }
}
polygon {
  stroke-width: 2px;
  stroke: #333;
  fill: transparent;
}
<svg width="564" height="409" onclick="handleSVGClick(event)">
  <image
    href="https://media.geeksforgeeks.org/wp-content/uploads/unique-rectangles-formed-using-n-unit-squares.png"
    width="564"
    height="409"
  />
  <polygon title="1" points="21,385 24,309 100,309 101,385" />
  <polygon title="2" points="102,305 23,304 23,228 101,227" />
  <polygon title="3" points="103,225 26,228 25,149 99,151" />
  <polygon title="4" points="103,147 102,65 25,70 23,147" />
</svg>

If you know the size/position will be static, you can manually position the map element and size each area to match:

function eating(Num) {
  const squares = document.querySelectorAll('area');

  console.log("entered eating", Num);

  for (let i = 0; i < squares.length; i++) {
    if (i < Num) {
      squares[i].style.background = "black";
    } else {
      squares[i].style.background = "transparent";
    }
  }
}
.container {
  position: relative;
}

map {
  display: grid;
  gap: 1px;
  position: absolute;
  top: 68px;
  left: 22.5px;
}

area {
  width: 79px;
  height: 79px;
  top: 0;
}
<div class="container text-center">
  <img src="https://media.geeksforgeeks.org/wp-content/uploads/unique-rectangles-formed-using-n-unit-squares.png" usemap="#image-map">
  <map id="map" name="image-map">
    <area target="_self" alt="1" title="1" onclick="eating(1)" coords="21,385,24,309,100,309,101,385" shape="poly">
    <area target="_self" alt="2" title="2" onclick="eating(2)" coords="102,305,23,304,23,228,101,227" shape="poly">
    <area target="_self" alt="3" title="3" onclick="eating(3)" coords="103,225,26,228,25,149,99,151" shape="poly">
    <area target="_self" alt="4" title="4" onclick="eating(4)" coords="103,147,102,65,25,70,23,147" shape="poly">
  </map>
</div>

However, I wouldn't recommend this method as it isn't scalable, reusable or, in my opinion, best practice.

Well in order to have the area clickable they need to have an href attribute. (at least in Firefox). Since they are hidden it is tough to style them cross browser. So I went another way and created elements on the same position as the areas and style those when ever the area is clicked.

Those overlays are just a proof of concept, but since I am working with percentages here this could work even if the image is scaled down.

Also I'd recommend doing all of this when Javascript is definitely supported, and also suggest removing the area element if you do not want users to reclick on them a second time. let me know if there is anything more you would like me to fine tune here:

function eating(Num) {
  console.log("entered eating", Num);    
  if(Num == 4) { // testcode
     document.querySelector("#overlay"+Num).style.display = "block";  
  }
  // remove area from map
 
}
(function init() {
    var container = document.querySelector('.container');
    container.classList.add("js")
})()
img {
  max-width: 100%; 
}

.overlay {
  display: none;
}

.container {
  display: inline-block; /* container same width as image */
  width: auto;
  outline: 2px solid red;
}

.container.js {
  position: relative;
}
.container.js .overlay {
  position: absolute;
  top: 16.8%; left: 4.3%;
  width: 13.5%; height: 18.5%;
  background: black;
  z-index: 1;
}
<div class="container text-center">
  <span class="overlay" id="overlay4"></span>
  <img src="https://media.geeksforgeeks.org/wp-content/uploads/unique-rectangles-formed-using-n-unit-squares.png" usemap="#image-map">
  <map id="map" name="image-map">
    <area target="_self" alt="1" title="1" href="#" onclick="eating(1)" coords="21,385,24,309,100,309,101,385" shape="poly">
    <area target="_self" alt="2" title="2" href="#" onclick="eating(2)" coords="102,305,23,304,23,228,101,227" shape="poly">
    <area target="_self" alt="3" title="3" href="#" onclick="eating(3)" coords="103,225,26,228,25,149,99,151" shape="poly">
    <area target="_self" alt="4" title="4" href="#" onclick="eating(4)" coords="103,147,102,65,25,70,23,147" shape="poly">
  </map>
</div>

Related