5

I have a static image made interactive using the concept of HTML maps.

enter image description here

Coordinates of the image set by uploading on https://imagemap.org/

Expected Behavior:

An overlay should display on hover in its respective box. For example, when the mouse hovers over red box, the overlay text should come in the red box itself, if it hovers on green then in green and so on.

Current Behavior:

The overlay text position is not coming in its respective box. It is displayed at the bottom. To achieve this, I am thinking of appending the div that contains the text right after the respective area tag when it is clicked.

My code:

<body>
  <div class="interactive-map" >
  <img src="https://www.politicalmetaphors.com/wp-content/uploads/2015/04/blog-shapes-square-windows.jpg">
  <div class="card" style="width:40%; height: 10%;">
    <div class="card-body">
      This is some text within a card body.
    </div>
  </div>
  <map name="image_map">
  <area id="one" title="Red" coords="25,33,68,65" shape="rect" data-placement="25,33,68,65">
  <area title="Green" coords="132,30,194,67" shape="rect">
  <area title="Blue" coords="22,147,74,192" shape="rect">
  <area title="Yellow" coords="131,144,197,188" shape="rect">
</map>

</div>

</body>
area{
    cursor: pointer;
    
}

$('area').hover(function(){
    ????
})

Fiddle- https://jsfiddle.net/woke_mushroom/2u3kbnv9/14/

Itika Bandta
  • 163
  • 1
  • 2
  • 12

4 Answers4

3

You need to associate the image with the image map, so

<img usemap="#image_map" src="https://www.politicalmetaphors.com/wp-content/uploads/2015/04/blog-shapes-square-windows.jpg" >

Then set the position of the thing you want to move to be absolute:

<div class="card" style="width:40%; height: 10%; position:absolute;">

Then access the mouse pointer position in the event handler:

$('area').hover(function(e)
{
  const card = document.querySelector('.card');
  card.style.top = e.clientY+'px';
  card.style.left = e.clientX+'px';
});

$('area').mouseenter(function(e)
{
  const card = document.querySelector('.card');
  $(card).show();
  card.style.top = e.clientY+'px';
  card.style.left = e.clientX+'px';
});

$('area').mouseleave(function(e)
{
  const card = document.querySelector('.card');
  $(card).hide();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="interactive-map" >
  <img src="https://www.politicalmetaphors.com/wp-content/uploads/2015/04/blog-shapes-square-windows.jpg" usemap="#image_map">
  <div class="card" style="width:40%; height: 10%; position:absolute;">
    <div class="card-body">
      This is some text within a card body.
    </div>
  </div>
  <map name="image_map">
  <area id="one" title="Red" coords="25,33,68,65" shape="rect" data-placement="25,33,68,65">
  <area title="Green" coords="132,30,194,67" shape="rect">
  <area title="Blue" coords="22,147,74,192" shape="rect">
  <area title="Yellow" coords="131,144,197,188" shape="rect">
</map>

</div>
Matt Ellen
  • 11,268
  • 4
  • 68
  • 90
  • Hi. Could you please check my fiddle? I tried your solution but it is still floating at the bottom of the image. https://jsfiddle.net/woke_mushroom/2u3kbnv9/18/ What am I missing here? Please help ! – Itika Bandta Aug 27 '20 at 14:17
  • Looks like bootstrap is interfering with the other style... – Matt Ellen Aug 27 '20 at 14:21
  • I've put the `position: absolute` into the style attribute now, so it should work – Matt Ellen Aug 27 '20 at 14:22
  • Yes. It works now ! I have a question, though. The .hover takes care of mouseenter and mouseleave automatically, but in this case it continues to move along the mouse pointer. If I want it to go away when the mouseleaves , what should I add in this same code? – Itika Bandta Aug 27 '20 at 14:54
  • I am trying - $("area").mouseleave(function(){ $('.card').show(); }); but doesn't work :( – Itika Bandta Aug 27 '20 at 15:07
  • 2
    I've changed `hover` to `mouseenter` and added a `mouseleave` handler. it should work now, although it looks like the definitions of each area are not the same size as the size of each colour – Matt Ellen Aug 27 '20 at 15:08
  • While your solution works perfectly, there is small issue. It works fine for a standalone image but when there are other components on the page , the card div is not displayed where it should be.Could you please suggest something? – Itika Bandta Sep 02 '20 at 10:34
  • 2
    It could be because `clientX` and `clientY` are relative to the client, not the screen. If the image is in an iframe, for example, then values will be relative to the inside of the iframe. https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientX – Matt Ellen Sep 02 '20 at 15:57
3

I think easiest way to show content inside a certain "area" is to make it a child-element of that area. You can use any block-element (e.g. <div></div>) as area. You will be be way more flexible this way as with using image maps.

Also showing contents when hovering can be achieved without any javascript with the :hover css pseudo class.

Below I positioned some boxes with css flex and hide/show the contents with css. You might want to position them in a css grid or some other way (like absolutely positioned in front of an image).

.container {
    display: flex;
    flex-wrap: wrap;
    width: 30em;
}

.area {
    cursor: pointer;
    width: 15em;
    height: 15em;
    border: 2px solid black;
    box-sizing: border-box;
}

.area > span {
    opacity: 0;
}

.area:hover > span {
    opacity: 1;
}

#area-red {
  background-color: red;
}
#area-green {
  background-color: green;
}
#area-blue {
  background-color: blue;
}
#area-yellow {
  background-color: yellow;
}
<div class="container">
    <div id="area-red" class="area">
        <span>Red contents</span>
    </div>
    <div id="area-green" class="area">
        <span>Green contents</span>
    </div>
    <div id="area-blue" class="area">
        <span>Blue contents</span>
    </div>
    <div id="area-yellow" class="area">
        <span>Yellow contents</span>
    </div>
</div>
lupz
  • 3,620
  • 2
  • 27
  • 43
  • Hi. Thank you for providing this alternate solution. Since, in real scenario I have to display overlay text in many places, adding css and describing each div will become a tedious task. It would be better if it moves along the mouse pointer as answered by the other gentleman. Thank you for this solution, regardless. It will come handy in another part of my application. – Itika Bandta Aug 27 '20 at 15:19
2

$(function() {
    $('area').mouseenter(function() {
        let coords = this.coords.split(',').map(a => a.trim())
        $('.card').css({display: 'block', top: coords[1] + 'px', left: coords[0] + 'px', width: coords[2] - coords[0], height: coords[3] - coords[1]})
    });
    $('area').mouseleave(function() {
        $('.card').css({display: 'none'})
    });
});
.interactive-map {
    position: relative;
}
.card {
    display: none;
    position: absolute;
    pointer-events: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="interactive-map" >
<img usemap="#image_map" src="https://www.politicalmetaphors.com/wp-content/uploads/2015/04/blog-shapes-square-windows.jpg">
  <div class="card">
    <div class="card-body">
      This is some text within a card body.
    </div>
  </div>
  <map name="image_map">
    <area title="Red" coords="0,0,150,150" shape="rect">
    <area title="Green" coords="150,0,300,150" shape="rect">
    <area title="Blue" coords="0,150,150,300" shape="rect">
    <area title="Yellow" coords="150,150,300,300" shape="rect">
  </map>
</div>

This code will place the overlay nicely in one position and will avoid flicker by using "pointer-events: none" in the css. It also auto-calculate the position and size of the overlay based on the area tags. (Note: I have altered the area coordinates based upon your requirement that each color be considered its own box)

nzn
  • 1,014
  • 11
  • 20
  • Hi. Thank you for your solution. Could you please explain what the a.trim() is doing? And why and how have you set the height and width ? I didn't understand it completely. – Itika Bandta Sep 10 '20 at 14:36
  • @ItikaBandta removes space, tab, no-break space, etc in the beginning of a string and end. For more go here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim#:~:text=The%20trim()%20method%20removes,%2C%20CR%2C%20etc.). – user13806962 Sep 10 '20 at 15:29
  • @ItikaBandta I used trim to make it more robust. In case someone enters the coordinates with spaces like coords="0 , 0 , 150 , 150" which is legal, but if we don't trim we will get a gap and end up with "150 px" which breaks the code. I also set the height and width because you mentioned you may want to use it for several different images which may have differently sized rectangles so the overlay will auto-adjust its width and height, again based on the area co-ordinates. – nzn Sep 11 '20 at 09:52
1

As you are specifying coords attribute to your area, you can specify cards left and top property

let pos = e.target.coords.split(",");
            card.style.top = pos[1] + 'px';
            card.style.left = pos[0] + 'px';
            card.style.display = "block";

Initially set it's style to display none, then on some event calculate its actual position and set its left and top. Add padding left and top to show text exactly in center.

$('area').on("click", function(e) {
            let pos = e.target.coords.split(",");
            const card = document.querySelector('.card');
            card.style.top = pos[1] + 'px';
            card.style.left = pos[0] + 'px';
            card.style.display = "block";

        });
.card {
            position: absolute;
        }
        
        area {
            cursor: pointer;
        }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <div class="interactive-map">
        <img src="https://www.politicalmetaphors.com/wp-content/uploads/2015/04/blog-shapes-square-windows.jpg" usemap="#image_map">
        <div class="card" style="width:40%; height: 10%; display: none;">
            <div class="card-body" style="width: 20%;">
                This is some text within a card body.
            </div>
        </div>
        <map name="image_map">
          <area id="one" title="Red" coords="25,33,68,65" shape="rect" data-placement="25,33,68,65">
          <area title="Green" coords="132,30,194,67" shape="rect">
          <area title="Blue" coords="22,147,74,192" shape="rect">
          <area title="Yellow" coords="131,144,197,188" shape="rect">
        </map>

    </div>

    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"></script>
halfer
  • 19,824
  • 17
  • 99
  • 186
anand shukla
  • 666
  • 5
  • 14