Script snippet loading before DOM is ready

I am basically just rendering a map through Leaflet, using a marker with a popup. (So the only markup I have is a div with a map ID).On the popup I’m iterating through a list of “amenities” available for each location. When I click on any one of those icons, I want a side/bottom bar to appear explaining what they mean.I am doing this currently

mapping over the list

return list .map( (item) => `<img class="ammenities-icons" src=img/${item}.png alt=${item} />` ) .join("");

and try to grab the class this way

  let content = document.querySelectorAll("img.ammenities-icons");

  content.forEach((el) => {
    el.addEventListener("click", () => {
              console.log("clicked");
    });
  });

That’s not doing anything however.

I am guessing the issue has to do with the fact that there is no “hard coded” mark-up to grab on to… so the snippet runs before the DOM is completely loaded. But it’s a guess and I don’t know how to solve it.

Would need to see your full code to be able to give advice.

JS

const tileUrl =
  "https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png";

const attribution =
  '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors &copy; <a href="https://carto.com/attributions">CARTO</a>';

const initialZoom = 13;

const map = L.map("map").setView([0, 0], initialZoom);

const locations = [
  {
    id: 1,
    name: "Espresso House Knalleland",
    address: {
      street: "Lundbygatan 1",
      city: "Borås",
      postcode: "506 38",
    },
    coordinates: {
      latitude: "57.73296",
      longitude: "12.93726",
    },
    ammenities: {
      changeroom: true,
      toys: true,
      books: true,
      playground: true,
      garden: true,
    },
  },
  {
    id: 2,
    name: "Espresso House Borås Station",
    address: {
      street: "Stationsgatan 16",
      city: "Borås",
      postcode: "503 38",
    },
    coordinates: {
      latitude: "57.72057",
      longitude: "12.93258",
    },
    ammenities: {
      changeroom: true,
      toys: false,
      books: true,
      playground: true,
      garden: false,
    },
  },
  {
    id: 3,
    name: "Café Viskan",
    address: {
      street: "Södra Strandgatan 6",
      city: "Borås",
      postcode: "503 35",
    },
    coordinates: {
      latitude: "57.71984",
      longitude: "12.94025",
    },
    ammenities: {
      changeroom: true,
      toys: true,
      books: true,
      playground: false,
      garden: true,
    },
  },
  {
    id: 4,
    name: "Waynes Coffee",
    address: {
      street: "Drottninggatan 33",
      city: "Stockholm",
      postcode: "111 51",
    },
    coordinates: {
      latitude: "59.33151",
      longitude: "18.06369",
    },
    ammenities: {
      changeroom: true,
      toys: false,
      books: true,
      playground: false,
      garden: true,
    },
  },
  {
    id: 5,
    name: "Caffetteria del corso",
    address: {
      street: "Drottninggatan 56",
      city: "Stockholm",
      postcode: "111 21",
    },
    coordinates: {
      latitude: "59.33029",
      longitude: "18.06489",
    },
    ammenities: {
      changeroom: true,
      toys: true,
      books: true,
      playground: true,
      garden: false,
    },
  },
  {
    id: 6,
    name: "Viktors Kaffe",
    address: {
      street: "Geijersgatan 7",
      city: "Göteborg",
      postcode: "411 34",
    },
    coordinates: {
      latitude: "57.69775",
      longitude: "11.97663",
    },
    ammenities: {
      changeroom: true,
      toys: true,
      books: true,
      playground: true,
      garden: false,
    },
  },
  {
    id: 7,
    name: "Café Berlin",
    address: {
      street: "Vasagatan 46",
      city: "Göteborg",
      postcode: "411 37",
    },
    coordinates: {
      latitude: "57.69981",
      longitude: "11.97188",
    },
    ammenities: {
      changeroom: true,
      toys: true,
      books: true,
      playground: false,
      garden: true,
    },
  },
];

const icon = L.icon({
  iconUrl: "/img/baby.png",
  iconSize: [30, 30],
  iconAnchor: [15, 30],
  popupAnchor: [0, -25],
});

function getLocation() {
  navigator.geolocation.getCurrentPosition(success, error);
}

function success(pos) {
  const crd = pos.coords;
  map.setView([crd.latitude, crd.longitude], initialZoom);
}

function error(err) {
  console.warn(`ERROR(${err.code}): ${err.message}`);
}

const getAmmenities = (item) => {
  const list = [];

  for (const [key, value] of Object.entries(item.ammenities)) {
    if (value === true) {
      list.push(key);
    }
  }

  const letList = list
    .map(
      (item) =>
        `<img class="ammenities-icons" src=img/${item}.png alt=${item} />`
    )
    .join("");

  document.addEventListener("DOMContentLoaded", getIcons);
  let content = document.querySelectorAll("img.ammenities-icons");
  content.forEach((el) => {
    el.addEventListener("click", () => {
      
      if (el.target.className === "ammenities-icons") {
        console.log("clicked");
      }
    });
  });

  return letList;
};

function getCaffees() {
  locations.forEach((item) => {
    L.marker(
      [Number(item.coordinates.latitude), Number(item.coordinates.longitude)],
      { icon }
    )
      .bindPopup(
        `
            <h3>${item.name}</h3>
            <h5 class="street-address">${item.address.street}</h5>
            <h5 class="other-address">${item.address.postcode} ${
          item.address.city
        }</h5>
            ${getAmmenities(item)}
            
            `
      )
      .openPopup()
      .addTo(map);
  });
}

getLocation();
getCaffees();

L.tileLayer(tileUrl, {
  attribution,
  maxZoom: 20,
}).addTo(map);

HTML

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <!-- leaflet css -->
    <link
      rel="stylesheet"
      href="https://unpkg.com/leaflet@1.8.0/dist/leaflet.css"
      integrity="sha512-hoalWLoI8r4UszCkZ5kL8vayOGVae1oxXe/2A4AO6J9+580uKHDO3JdHb7NzwwzK5xr/Fs0W40kiNHxM9vyTtQ=="
      crossorigin=""
    />
    <!-- leaflet javascript -->
    <script
      src="https://unpkg.com/leaflet@1.8.0/dist/leaflet.js"
      integrity="sha512-BB3hKbKWOc9Ez/TAwyWxNXeoV9c1v6FIeYiBieIWkpLjauysF18NzgR1MBNBXf8/KABdlkX68nAhlwcDFLGPCQ=="
      crossorigin=""
    ></script>
    <!-- custom css -->
    <link rel="stylesheet" href="style.css" />
    <title>Little Sippers</title>
  </head>
  <body>
    <div id="map"></div>
    <script src="leaflet.js"></script>
  </body>
</html>

Can share repository if you want…

That is even better.

One way of accomplishing what you want is to add a click handler to each img element when creating all the images. This means you would do this in your getAmmenities function. Something like this:

`<img onclick="handleImageClick()" class="ammenities-icons" src="img/${item}.png" alt="${item}" />`

Then you would create a function in your leaflet.js file named handleImageClick that would do whatever you want when the image is clicked. One issue with this approach is that you could potentially be adding a ton of click event handlers to the DOM depending on how many popups are created and how many images per popup.

I’ll give that a try.

Eventually, there could be a lot of click events so it maíght not be a solution that scales. But that’s an issue for much later if ever!

Is there any way (apart from what I am doing) to defer the script until DOM is loaded?

Thank you for your help by the way!

A better way would be to create a single function named handlePopupClick that could be assigned to the onlick property of the popup. Leaflet has a method L.DomUtil.create that lets you create DOM elements instead of a html string like you were doing before. You could create a div like:

const popupDiv = L.DomUtil.create('div', 'infoWindow');

Next, you could assign your existing html string to the innerHTML property of popupDiv.

popupDiv.innerHTML = `
      <h3>${item.name}</h3>
      <h5 class="street-address">${item.address.street}</h5>
      <h5 class="other-address">${item.address.postcode} ${item.address.city}</h5>
      ${myAmmenities}
    `;

Assign handlePopupClick to the onclick property of popupDiv.

popupDiv.onclick = handlePopupClick;`

Last pass popupDiv as the argument to the bindPopup method.

This approach would scale much better as only the popups have the click handlers instead of each image element.

1 Like

@danmalmx Were you ever able to make the changes I mentioned above?

Sorry that I didn’t reply!

Yes it worked like a charm! :slightly_smiling_face:

Thank you!!!

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