CMS Filter + Mapbox.js

Hey Gang, I have a housing website in which I use your filters to change the cards displayed. However with that list view I also have a map view, with that map view my objective is to have it so that if the same filters are used it will also filter my locations on the map.

Im currently pulling markers from the cms to display on the map, however when the cards are filtered the markers are not.

Any help would be greatly appreciated!

LINKS
Staging
https://webflow.com/design/inplace-living-c4d32bd44decd271ffabe91c

Share
https://preview.webflow.com/preview/inplace-living-c4d32bd44decd271ffabe91c?utm_medium=preview_link&utm_source=designer&utm_content=inplace-living-c4d32bd44decd271ffabe91c&preview=3814fd83bf5838c1f3462d37dbd8778a&workflow=preview

Hey @ethan! This could be done with the help of the API. I see you select the elements by class to gather all the info needed for the map.

An idea to make this work is to replace this class selection with the array of rendered elements and gather the info from here

Hey Luis, is there any more guidance you could give me on how to accomplish this? Am very stuck and think I’ve been trying to figure it out for far too long, haha.

Any help would be greatly appreciated!

Hey @ethan, first thing to do is get your filter working. I noticed you added the fs-cmsfilter-element = list attribute to the lists used for your custom select. This is not needed here, only on your main list.

For development purposes can we have the filters as radio buttons? Once the filtering is working reliably I can help with the code :muscle:

Excellent thank you, I’ve gone in and changed them to radio buttons. Also removed the list attribute from the filters also.

Is it somewhere within the code I need to change something?
in order for it to affect the map?

Hey @ethan! This is the starting point for you to build upon. This code selects only the rendered items and collects the data needed from them. This refreshes each time a user filters the list.

<script>
  window.fsAttributes = window.fsAttributes || [];
  window.fsAttributes.push([
    'cmsfilter',
    (filterInstances) => {
      console.log('cmsfilter Successfully loaded!');

      const [filterInstance] = filterInstances;

      // This function gather all info from within the cards to create the elements needed for mapbox
      function mapData(item) {
        const resultElement = item.element;

        let cmsItem = resultElement;
        let img = cmsItem
          .querySelector('.location-profile')
          .style.backgroundImage.slice(4, -1)
          .replace(/["']/g, '');

        let name = cmsItem.querySelector('.location-name').innerHTML.trim();
        let lat = cmsItem.querySelector('.lat').innerHTML;
        let lon = cmsItem.querySelector('.lon').innerHTML;
        let detail1 = cmsItem.querySelector('.popup-detail'); //left blank as it is undefined now
        let detail2 = cmsItem.querySelector('.popup-detail-2'); //left blank as it is undefined now

        //These can be removed after all info needed is reliably gathered
        console.log('image:', img);
        console.log('name', name);
        console.log('lat', lat);
        console.log('lon', lon);
        console.log('detail1', detail1);
        console.log('detail2', detail2);

        //the rest of the mapbox.js code goes here
      }

      filterInstance.listInstance.on('renderitems', (renderedItems) => {
        // console.log(renderedItems);
        renderedItems.forEach((item) => {
          mapData(item);
        });
      });
    },
  ]);
</script>

Hey Luis, first thankyou so much. Appreciate it, but I am unable to get this working still.

This is the code I am using

<script>
  window.fsAttributes = window.fsAttributes || [];
  window.fsAttributes.push([
    'cmsfilter',
    (filterInstances) => {
      console.log('cmsfilter Successfully loaded!');

      const [filterInstance] = filterInstances;

      // This function gather all info from within the cards to create the elements needed for mapbox
      function mapData(item) {
        const resultElement = item.element;

        let cmsItem = resultElement;
        let img = cmsItem
          .querySelector('.location-profile')
          .style.backgroundImage.slice(4, -1)
          .replace(/["']/g, '');

        let name = cmsItem.querySelector('.location-name').innerHTML.trim();
        let lat = cmsItem.querySelector('.lat').innerHTML;
        let lon = cmsItem.querySelector('.lon').innerHTML;
        let detail1 = cmsItem.querySelector('.popup-detail'); //left blank as it is undefined now
        let detail2 = cmsItem.querySelector('.popup-detail-2'); //left blank as it is undefined now

        //These can be removed after all info needed is reliably gathered
        console.log('image:', img);
        console.log('name', name);
        console.log('lat', lat);
        console.log('lon', lon);
        console.log('detail1', detail1);
        console.log('detail2', detail2);

        document.addEventListener('DOMContentLoaded', function () {
  function initializeMap() {
    // Your Mapbox access token
    mapboxgl.accessToken = 'pk.eyJ1IjoiZXRoYW5xIiwiYSI6ImNsazIwc3V6MDBiZnozbnFtd3Q1ODdjeWUifQ.b06Fc0rBTSM8Bquhg-cDXw';

    // Initialize the map
    var map = new mapboxgl.Map({
      container: 'map',
      style: 'mapbox://styles/ethanq/cllombele001t01ofh6yz6evy',
      center: [144.977731, -37.823803],
      zoom: 8,
    });

    // Handle state filter dropdown change
    $(".state-filter-dropdown").on("change", function () {
      var selectedCoordinates = $(this).val();
      var [longitude, latitude] = selectedCoordinates.split(",").map(parseFloat);

      if (!isNaN(longitude) && !isNaN(latitude)) {
        map.flyTo({
          center: [longitude, latitude],
          zoom: 8,
          essential: true
        });
      } else {
        console.log("Invalid coordinates format");
      }
    });

    var current_popup, current_marker, current_item;

    $(".location").each(function () {
      var cmsItem = $(this);
      var img = cmsItem.find(".location-profile").css("background-image").slice(4, -1).replace(/["']/g, "");
      var name = cmsItem.find(".location-name").text();
      var lat = cmsItem.find(".lat").text();
      var lon = cmsItem.find(".lon").text();
      var detail1 = cmsItem.find(".popup-detail").text();
      var detail2 = cmsItem.find(".popup-detail-2").text();

      var el = document.createElement('div');
      el.classList.add('star');
      var body = cmsItem.find(".pre-popup");

      var pop = new mapboxgl.Popup({
        offset: 25,
        closeButton: false,
        maxWidth: "auto"
      }).setHTML(body[0].outerHTML);

      var mark = new mapboxgl.Marker(el)
        .setLngLat([lon, lat])
        .setPopup(pop)
        .addTo(map);

      map.on('click', function (e) {
        if (e.originalEvent.srcElement.ariaLabel === "Map") {
          current_marker.classList.remove('show');
        }
      });

      // MARKERS EVENT
      el.addEventListener('click', function () {
        pop.addTo(map);
        if (current_marker != undefined) {
          current_item.classList.remove('active');
          current_marker.classList.remove('show');
          pop.remove();
        }
        current_item = this;
        current_item.classList.add('active');
        current_marker = el;
        current_marker.classList.add('show');
        map.flyTo({
          center: [lon, lat],
          zoom: 8,
          essential: true
        });
      });

      el.addEventListener('mouseover', function () {
        pop.addTo(map);
        el.classList.add('show');
      });

      el.addEventListener('mouseout', function () {
        if (current_marker !== el) {
          pop.remove();
          el.classList.remove('show');
        }
      });

      // LIST ITEMS EVENT
      this.addEventListener('click', function () {
        map.flyTo({
          center: [lon, lat],
          zoom: 8,
          essential: true
        });
        if (current_marker != undefined) {
          current_item.classList.remove('active');
          current_marker.classList.remove('show');
          current_popup.remove();
        }
        pop.addTo(map);
        el.classList.add('show');
        current_marker = el;
        current_popup = pop;
        current_item = this;
        current_item.classList.add('active');
      });

      this.addEventListener('mouseover', function () {
        pop.addTo(map);
        el.classList.add('show');
      });

      this.addEventListener('mouseout', function () {
        if (current_marker !== el) {
          pop.remove();
          el.classList.remove('show');
        }
      });
    });

    // Add zoom and rotation controls to the map
    map.addControl(new mapboxgl.NavigationControl());

    // Disable map zoom when using scroll
    map.scrollZoom.disable();
  }

  // Call the initialization function
  initializeMap();
      }

      filterInstance.listInstance.on('renderitems', (renderedItems) => {
        // console.log(renderedItems);
        renderedItems.forEach((item) => {
          mapData(item);
        });
      });
    },
  ]);
</script>

hey @ethan! I see you have removed the list from your page so I can’t fully test this code but we are getting somewhere :muscle:

<script>
  window.fsAttributes = window.fsAttributes || [];
  window.fsAttributes.push([
    'cmsfilter',
    (filterInstances) => {
      console.log('cmsfilter Successfully loaded!');

      const [filterInstance] = filterInstances;

      let listItems = filterInstance.listInstance.items;

      // Create an array to store the map markers
      var markers = [];

      function initializeMap(items) {
        // Your Mapbox access token
        mapboxgl.accessToken =
          'pk.eyJ1IjoiZXRoYW5xIiwiYSI6ImNsazIwc3V6MDBiZnozbnFtd3Q1ODdjeWUifQ.b06Fc0rBTSM8Bquhg-cDXw';

        var mapContainer = document.getElementById('map');
        mapContainer.innerHTML = '';
        // Initialize the map
        var map = new mapboxgl.Map({
          container: 'map',
          style: 'mapbox://styles/ethanq/cllombele001t01ofh6yz6evy',
          center: [144.977731, -37.823803],
          zoom: 8,
        });

        // Handle state filter dropdown change
        $('.state-filter-dropdown').on('change', function () {
          var selectedCoordinates = $(this).val();
          var [longitude, latitude] = selectedCoordinates.split(',').map(parseFloat);

          if (!isNaN(longitude) && !isNaN(latitude)) {
            map.flyTo({
              center: [longitude, latitude],
              zoom: 8,
              essential: true,
            });
          } else {
            console.log('Invalid coordinates format');
          }
        });

        // Check if the items array is empty
        if (items && items.length > 0) {
          var current_popup, current_marker, current_item;

          items.forEach((location) => {
            const resultElement = location.element;
            console.log(resultElement);

            let cmsItem = resultElement;
            let img = cmsItem
              .querySelector('.location-profile')
              .style.backgroundImage.slice(4, -1)
              .replace(/["']/g, '');

            let name = cmsItem.querySelector('.location-name').innerHTML.trim();
            let lat = cmsItem.querySelector('.lat').innerHTML;
            let lon = cmsItem.querySelector('.lon').innerHTML;

            // Create a marker element
            var el = document.createElement('div');
            el.classList.add('star');

            var body = cmsItem.querySelector('.pre-popup');
            var pop = new mapboxgl.Popup({
              offset: 25,
              closeButton: false,
              maxWidth: 'auto',
            });

            if (body) {
              pop.setHTML(body.outerHTML);
            } else {
              pop.setHTML('No popup content available');
            }

            var mark = new mapboxgl.Marker(el).setLngLat([lon, lat]).setPopup(pop).addTo(map);
            markers.push(mark); // Add the marker to the array

            // MARKERS EVENT
            el.addEventListener('click', function () {
              pop.addTo(map);
              if (current_marker != undefined) {
                current_item.classList.remove('active');
                current_marker.classList.remove('show');
                pop.remove();
              }
              current_item = this;
              current_item.classList.add('active');
              current_marker = el;
              current_marker.classList.add('show');
              map.flyTo({
                center: [lon, lat],
                zoom: 1,
                essential: true,
              });
            });

            el.addEventListener('mouseover', function () {
              pop.addTo(map);
              el.classList.add('show');
            });

            el.addEventListener('mouseout', function () {
              if (current_marker !== el) {
                pop.remove();
                el.classList.remove('show');
              }
            });

            // LIST ITEMS EVENT
            resultElement.addEventListener('click', function () {
              map.flyTo({
                center: [lon, lat],
                zoom: 8,
                essential: true,
              });
              if (current_marker != undefined) {
                current_item.classList.remove('active');
                current_marker.classList.remove('show');
                current_popup.remove();
              }
              pop.addTo(map);
              el.classList.add('show');
              current_marker = el;
              current_popup = pop;
              current_item = this;
              current_item.classList.add('active');
            });

            resultElement.addEventListener('mouseover', function () {
              pop.addTo(map);
              el.classList.add('show');
            });

            resultElement.addEventListener('mouseout', function () {
              if (current_marker !== el) {
                pop.remove();
                el.classList.remove('show');
              }
            });
          });
        } else {
          console.log('No items to display on the map.'); // Handle the case where items is empty
        }

        // Add zoom and rotation controls to the map
        map.addControl(new mapboxgl.NavigationControl());

        // Disable map zoom when using scroll
        map.scrollZoom.disable();
      }

      initializeMap(listItems);

      filterInstance.listInstance.on('renderitems', (renderedItems) => {
        initializeMap(renderedItems);
      });
    },
  ]);
</script>

This can now add and remove markers according to the arrays passed into the initializeMap function which change on page load and the renderitems event.

@Support-Luis Absolute legend thank you!

Works great, but what I was attempting to achieve, doesnt cause the map to reload on each filter use. Thankyou so much for your assistance, not sure if something like this is something you’re capable of helping with but would be immensely helpful

Similar to how wrkshop created theirs - Filterable CMS Map in Webflow | Built by theWrkShop

@ethan as I have not used the library before I am not sure if there is an updateMap function. If there is, the functionality you are after is totally do able but the list would have to be there as the example you are showing