Search code examples
javascripthtmlimagemap

Error when recalculating imagemap area coordinates


I'm working on a script that recalculates the coordinates of the area attributes inside the imagemap. When loading the page for the first time, the function is initialised so that the area's coordinates adjust to the width of the users browser. The fist time everything goes well.

I've added a 'resize' event listener on the window element. The function fires (same as the init function), and the coordinates are adjusted as expected. So that works. But there is one problem; I keep getting the error you can see below every time I resize the window (The error doesn't occur while innit fires for the first time).

Error:

Uncaught TypeError: Cannot set property 'coords' of undefined
at http://localhost:3000/js/index.js:78697:33
at Array.forEach (native)
at Object.resize (http://localhost:3000/js/index.js:78696:23)
at Object.start (http://localhost:3000/js/index.js:78691:25)
at Object.init (http://localhost:3000/js/index.js:78679:27)
at Object.start (http://localhost:3000/js/index.js:78724:27)
at init (http://localhost:3000/js/index.js:78720:19)

JavaScript

    /* ALL IMPORTANT CONFIG DATA
------------------------------------------------ */
const config = {
  allMaps: [],
  actualMap: document.getElementsByTagName('map')[0],
  allAreas: false,
  areaCoords: [],
  vector: false,
};

/* RECALCULATION FUNCTIONALITY
------------------------------------------------ */
const RecalculateImageMap = {
  init() {
    /* AUTOMATICALLY UPDATE COORDINATES ON RESIZED WINDOW
    ------------------------------------------------ */
    window.addEventListener('resize', ImageMapSetup.init);
    if (!config.actualMap.newSize) {
      RecalculateImageMap.start();
    }
  },
  start() {
    config.allAreas = config.actualMap.getElementsByTagName('area');
    config.vector = document.getElementById('interactive_vector');
    /* ALL COORDINATES TO ARRAY
    ------------------------------------------------ */
    for (let i = 0; i < config.allAreas.length; i++) {
      const coords = config.allAreas[i].coords;
      config.areaCoords.push(coords.replace(/ *, */g, ',').replace(/ +/g, ','));
    }
    RecalculateImageMap.resize();
  },
  resize() {
    /* FOR EACH AREA => RESIZE THE COORDINATES
    ------------------------------------------------ */
    config.areaCoords.forEach(function(area, i) {
      config.allAreas[i].coords = RecalculateImageMap.scale(area);
    });
  },
  scale(area) {
    const allValues = area.split(',');
    /* CHECK FOR DIFFERENCE IN SCREENSIZE
    ------------------------------------------------ */
    let totalScale = config.vector.width / config.vector.naturalWidth;
    let newArea = [];
    /* CHANGE EACH COORDINATE BASED ON THE NEW SCALE (DIFFERENCE SINCE LAST WIDTH)
    ------------------------------------------------ */
    allValues.map(function(coordinate) {
      let result = Math.round(Number(coordinate) * totalScale);
      newArea.push(result);
    });
    return newArea;
  }
};

/* INITIALIZE RESIZING
------------------------------------------------ */
const ImageMapSetup = {
  init() {
    ImageMapSetup.start(config.actualMap);
  },
  start(element) {
    if (element) {
      RecalculateImageMap.init(element);
      config.allMaps.push(element);
    }
  }
};

ImageMapSetup.init();

Does anyone see what goes wrong when firing the init function on the resize event? I've been looking for a solution for a while now.. But without succes.

Thanks in advance!


Solution

  • IMHO the problem is that you are initializing config.allAreas in start(), but you are always adding coords to config.areaCoords

      start() {
        config.allAreas = config.actualMap.getElementsByTagName('area'); 
        ...
        for (let i = 0; i < config.allAreas.length; i++) {
          const coords = config.allAreas[i].coords;
          config.areaCoords.push(coords.replace(/ *, */g, ',').replace(/ +/g, ','));
        }
    

    Afterwards in resize() you are looping over config.areaCoords, which now contains much more elements than config.allAreas:

      resize() {
        /* FOR EACH AREA => RESIZE THE COORDINATES
        ------------------------------------------------ */
        config.areaCoords.forEach(function(area, i) {
          config.allAreas[i].coords = RecalculateImageMap.scale(area);
        });
      },
    

    So I suppose that the error occurs here at the config.allAreas[i].coords = assignment because i is much greater than the length of the config.allAreas array.

    Furthermore this would explain your statement "The weird thing is that the function works perfect for the first time".

    An assignment like config.areaCoords = []; in the start() function should solve the problem.