Resizing an image-map on resizing a container div

I know that this jquery from David J. Bradshaw works as expected to resize an image-map on resizing the whole browser window.
I wonder if it could be possible to modify the code to resize the image-map on resizing not of the whole window, but of a given element (a div, i.g.) containing the image.
It would be possible?

/*! Image Map Resizer
 *  Desc: Resize HTML imageMap to scaled image.
 *  Copyright: (c) 2014-15 David J. Bradshaw - dave@bradshaw.net
 *  License: MIT
 */

;(function() {
  'use strict'

  function scaleImageMap() {
    function resizeMap() {
      function resizeAreaTag(cachedAreaCoords, idx) {
        function scale(coord) {
          var dimension = 1 === (isWidth = 1 - isWidth) ? 'width' : 'height'
          return (
            padding[dimension] +
            Math.floor(Number(coord) * scalingFactor[dimension])
          )
        }

        var isWidth = 0
        areas[idx].coords = cachedAreaCoords
          .split(',')
          .map(scale)
          .join(',')
      }

      var scalingFactor = {
        width: image.width / image.naturalWidth,
        height: image.height / image.naturalHeight,
      }

      var padding = {
        width: parseInt(
          window.getComputedStyle(image, null).getPropertyValue('padding-left'),
          10
        ),
        height: parseInt(
          window.getComputedStyle(image, null).getPropertyValue('padding-top'),
          10
        ),
      }

      cachedAreaCoordsArray.forEach(resizeAreaTag)
    }

    function getCoords(e) {
      //Normalize coord-string to csv format without any space chars
      return e.coords.replace(/ *, */g, ',').replace(/ +/g, ',')
    }

    function debounce() {
      clearTimeout(timer)
      timer = setTimeout(resizeMap, 250)
    }

    function start() {
      if (
        image.width !== image.naturalWidth ||
        image.height !== image.naturalHeight
      ) {
        resizeMap()
      }
    }

    function addEventListeners() {
      image.addEventListener('load', resizeMap, false) //Detect late image loads in IE11
      window.addEventListener('focus', resizeMap, false) //Cope with window being resized whilst on another tab
      window.addEventListener('resize', debounce, false)
      window.addEventListener('readystatechange', resizeMap, false)
      document.addEventListener('fullscreenchange', resizeMap, false)
    }

    function beenHere() {
      return 'function' === typeof map._resize
    }

    function getImg(name) {
      return document.querySelector('img[usemap="' + name + '"]')
    }

    function setup() {
      areas = map.getElementsByTagName('area')
      cachedAreaCoordsArray = Array.prototype.map.call(areas, getCoords)
      image = getImg('#' + map.name) || getImg(map.name)
      map._resize = resizeMap //Bind resize method to HTML map element
    }

    var /*jshint validthis:true */
      map = this,
      areas = null,
      cachedAreaCoordsArray = null,
      image = null,
      timer = null

    if (!beenHere()) {
      setup()
      addEventListeners()
      start()
    } else {
      map._resize() //Already setup, so just resize map
    }
  }

  function factory() {
    function chkMap(element) {
      if (!element.tagName) {
        throw new TypeError('Object is not a valid DOM element')
      } else if ('MAP' !== element.tagName.toUpperCase()) {
        throw new TypeError(
          'Expected <MAP> tag, found <' + element.tagName + '>.'
        )
      }
    }

    function init(element) {
      if (element) {
        chkMap(element)
        scaleImageMap.call(element)
        maps.push(element)
      }
    }

    var maps

    return function imageMapResizeF(target) {
      maps = [] // Only return maps from this call

      switch (typeof target) {
        case 'undefined':
        case 'string':
          Array.prototype.forEach.call(
            document.querySelectorAll(target || 'map'),
            init
          )
          break
        case 'object':
          init(target)
          break
        default:
          throw new TypeError('Unexpected data type (' + typeof target + ').')
      }

      return maps
    }
  }

  if (typeof define === 'function' && define.amd) {
    define([], factory)
  } else if (typeof module === 'object' && typeof module.exports === 'object') {
    module.exports = factory() //Node for browserfy
  } else {
    window.imageMapResize = factory()
  }

  if ('jQuery' in window) {
    window.jQuery.fn.imageMapResize = function $imageMapResizeF() {
      return this.filter('map')
        .each(scaleImageMap)
        .end()
    }
  }
})()

It seems to do that already unless I’m mistaken.

2 Likes

Unfortunately I don’t manage to get this code working. Tomorrow I will do some other attempts and I share my whole code.
Thank you!

1 Like

It took me a while to get that to work as even the original does not work anymore.

Just hover the map and it gives the wrong states.:frowning:

In my example I used the non jquery version and then it all started working.

1 Like

I have uploaded an example of non working code.
As you can see in the source code I have added some css and js to get a modal image (not that easy to get: you have to find the “clickable” zone, sorry), and in the pop-up (modal image) you can’t see any map (I have left only titles, not href).
I’m wrong?

Sorry I’m out this morning but will take a look this afternoon (unless anyone else beats me to it). :slight_smile:

1 Like

There are a number of problems with your demo and I will list the most important.

  1. You have the js before the html instead of after it. That means when the js runs the htm doesn’t exist yet. Put your js at the end of the body just before the closing body tag.

  2. You only have an image map for the first image. Where is your image map for the image in the modal? You can’t use one map for 2 images as they are 2 different sizes.

  3. The modal image doesn’t actually exist at all when the page runs so even if there was an image map it would have no image to map out.

  4. You need to give the modal img a src on page load so even if its hidden the image is present.

  5. You need the usemap attribute on the modal image so that it can use the new image map (which is basically a duplicate of the first one).

  6. Do you need an image map on the small image? I’m guessing no and if that’s so you can remove the first image map.

  7. your modal image doesn’t resize. It’s a fixed width at the size it shows.

I’ve addressed some of those issues and here’s a working copy.

Note that you should consider removing the inline click handlers and use an event listener in the JS instead.

If you still have problems you may need to call the imageMapresize function when you show the modal. It’s not needed in my example but of you are going to do multiple images you may need this approach.

2 Likes

Thank you very much! In the example your code works!
I have to try to my page (with several images from a database): I will let you know.
But the main point seems clear.

1 Like

I have tied: the only problem is the image dimensions.
How should be the code to work with this css?

#modalimg {
  width: auto;
  height: auto;
  max-height: calc(100vh - 2rem);
  max-width: calc(100% - 2rem);  

Only this code gives the expected image dimensions. Otherwise (with width: 100%) the image is too big.

I’d need to see the code but generally you would not do that approach as you may squish the image.

Usually you would set the size of the container that the image sits in and then set the image to 100% of the container. I’d need to see a working demo as there are a lot of variables in there.

It also depends on what you want to achieve. Remember that images have aspect ratios so forcing a height and width will not maintain the aspect ratio,

1 Like

ok, thanks :blush:. Today I will upload a new example with the whole code.

With the help of @PaulOB I managed to solve my problem.
The key is what he said at #7 post above: to add an imagemap to the modal-image (and some other useful tips to fit to my needs).
I noticed that even one error (in the data from database) can damage seriously the final output.

1 Like

Glad you got it sorted.

Image maps are a pain but if you only want rectangular sections you can do some nice things in css only.

Or If you are bored you ca draw a clip-path element to make irregular shapes just like the map element.

As you see I lost the will to live after the second one :slight_smile:

2 Likes

:grinning: :grinning: :+1: :+1:

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.