1

Trying to implement collapsible side bars instead of using Mapbox built in Popup boxes for messages.

Had a go and built upon this and this thread.

My attempt can be found here, JavaScript below:

// TO MAKE THE MAP APPEAR YOU MUST
    // ADD YOUR ACCESS TOKEN FROM
    // https://account.mapbox.com
    mapboxgl.accessToken = 'pk.eyJ1Ijoibml0dHlqZWUiLCJhIjoid1RmLXpycyJ9.NFk875-Fe6hoRCkGciG8yQ';
    const map = new mapboxgl.Map({
        container: 'map',
        // Choose from Mapbox's core styles, or make your own style with Mapbox Studio
        style: 'mapbox://styles/mapbox/streets-v12',
        center: [-77.04, 38.907],
        zoom: 11.15
    });

    map.on('load', () => {
        map.addSource('places', {
            // This GeoJSON contains features that include an "icon"
            // property. The value of the "icon" property corresponds
            // to an image in the Mapbox Streets style's sprite.
            'type': 'geojson',
            'data': {
                'type': 'FeatureCollection',
                'features': [
                    {
                        'type': 'Feature',
                        'properties': {
                            'description':
                                '<strong>Make it Mount Pleasant</strong><p><a href="http://www.mtpleasantdc.com/makeitmtpleasant" target="_blank" title="Opens in a new window">Make it Mount Pleasant</a> is a handmade and vintage market and afternoon of live entertainment and kids activities. 12:00-6:00 p.m.</p>',
                            'icon': 'theatre'
                        },
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [-77.038659, 38.931567]
                        }
                    },
                   
                    {
                        'type': 'Feature',
                        'properties': {
                            'description':
                                '<strong>Ballston Arts & Crafts Market</strong><p>The <a href="http://ballstonarts-craftsmarket.blogspot.com/" target="_blank" title="Opens in a new window">Ballston Arts & Crafts Market</a> sets up shop next to the Ballston metro this Saturday for the first of five dates this summer. Nearly 35 artists and crafters will be on hand selling their wares. 10:00-4:00 p.m.</p>',
                            'icon': 'art-gallery'
                        },
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [-77.111561, 38.882342]
                        }
                    },
                    {
                        'type': 'Feature',
                        'properties': {
                            'description':
                                '<strong>Seersucker Bike Ride and Social</strong><p>Feeling dandy? Get fancy, grab your bike, and take part in this year\'s <a href="http://dandiesandquaintrelles.com/2012/04/the-seersucker-social-is-set-for-june-9th-save-the-date-and-start-planning-your-look/" target="_blank" title="Opens in a new window">Seersucker Social</a> bike ride from Dandies and Quaintrelles. After the ride enjoy a lawn party at Hillwood with jazz, cocktails, paper hat-making, and more. 11:00-7:00 p.m.</p>',
                            'icon': 'bicycle'
                        },
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [-77.052477, 38.943951]
                        }
                    },
                    {
                        'type': 'Feature',
                        'properties': {
                            'description':
                                '<strong>Capital Pride Parade</strong><p>The annual <a href="http://www.capitalpride.org/parade" target="_blank" title="Opens in a new window">Capital Pride Parade</a> makes its way through Dupont this Saturday. 4:30 p.m. Free.</p>',
                            'icon': 'rocket'
                        },
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [-77.043444, 38.909664]
                        }
                    },
                    
                    {
                        'type': 'Feature',
                        'properties': {
                            'description':
                                '<strong>A Little Night Music</strong><p>The Arlington Players\' production of Stephen Sondheim\'s  <a href="http://www.thearlingtonplayers.org/drupal-6.20/node/4661/show" target="_blank" title="Opens in a new window"><em>A Little Night Music</em></a> comes to the Kogod Cradle at The Mead Center for American Theater (1101 6th Street SW) this weekend and next. 8:00 p.m.</p>',
                            'icon': 'music'
                        },
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [-77.020945, 38.878241]
                        }
                    },
                    {
                        'type': 'Feature',
                        'properties': {
                            'description':
                                '<strong>Truckeroo</strong><p><a href="http://www.truckeroodc.com/www/" target="_blank">Truckeroo</a> brings dozens of food trucks, live music, and games to half and M Street SE (across from Navy Yard Metro Station) today from 11:00 a.m. to 11:00 p.m.</p>',
                            'icon': 'music'
                        },
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [-77.007481, 38.876516]
                        }
                    }
                ]
            }
        });
        // Add a layer showing the places.
        map.addLayer({
            'id': 'places',
            'type': 'symbol',
            'source': 'places',
            'layout': {
                'icon-image': ['get', 'icon'],
                'icon-allow-overlap': true
            }
        });

        // When a click event occurs on a feature in the places layer, open a popup at the
        // location of the feature, with description HTML from its properties.
        map.on('click', 'places', (e) => {
            // Copy coordinates array.
            const coordinates = e.features[0].geometry.coordinates.slice();
            const description = e.features[0].properties.description;

            // Ensure that if the map is zoomed out such that multiple
            // copies of the feature are visible, the popup appears
            // over the copy being pointed to.
            while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {
                coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360;
            }

            toggleSidebar('left')
            $(".sidebar-content").empty();
            $(".sidebar-content").append(description)

        });

        // Change the cursor to a pointer when the mouse is over the places layer.
        map.on('mouseenter', 'places', () => {
            map.getCanvas().style.cursor = 'pointer';
        });

        // Change it back to a pointer when it leaves.
        map.on('mouseleave', 'places', () => {
            map.getCanvas().style.cursor = '';
        });
    });
    
    function toggleSidebar(id) 
    {

        var elem = document.getElementById(id);
        var classes = elem.className.split(' ');
        var collapsed = classes.indexOf('collapsed') !== -1;

        var padding = {};

        if ( collapsed ) 
        {

            // Remove the 'collapsed' class from the class list of the element, this sets it back to the expanded state.
            classes.splice(classes.indexOf('collapsed'), 1);

            padding[id] = 260; // In px, matches the width of the sidebars set in .sidebar CSS class
            map.easeTo({
                padding: padding,
                duration: 1000 // In ms, CSS transition duration property for the sidebar matches this value
            });

        } 

        else 
        {

            padding[id] = 0;
            // Add the 'collapsed' class to the class list of the element
            classes.push('collapsed');

            map.easeTo({
                padding: padding,
                duration: 1000
            });

        }

        // Update the class list on the element
        elem.className = classes.join(' ');

    }

So clicking on the points opens up the sidebar, but when I click on another point, while the sidebar is open, it toggles the sidebar instead of updating the message.

How do I update the toggle function so that if it is open, and another point is clicked, the sidebar remains open, but the content is updated?

0

1 Answer 1

1

Problem with your code logic is that sidebar toggle function toggleSidebar is called on each feature click, regardless if sidebar is already opened or not. Code should check if sidebar is opened and not toggle it in this case.

There is also no possibility to close sidebar once it's opened. Code below is modified so that sidebar is closed if mouse click is outside places layer features.

Relevant parts of the code could then look something like this:

map.on('click', (e) => {
  var description, toggleIt;
  const bbox = [
    [e.point.x - 5, e.point.y - 5],
    [e.point.x + 5, e.point.y + 5]
  ];
  const clickedFeatures = map.queryRenderedFeatures(bbox, {
    layers: ['places']
  });
  if (clickedFeatures.length > 0) {
    description = clickedFeatures[0].properties.description;
  }
  if (description) {
    $(".sidebar-content").empty();
    $(".sidebar-content").append(description)
    toggleIt = collapsed;
    }
  else {
    toggleIt = !collapsed;
  }
  if (toggleIt) toggleSidebar('left');;
});

var collapsed = true;
function toggleSidebar(id) {
  var elem = document.getElementById(id);
  var classes = elem.className.split(' ');
  collapsed = classes.indexOf('collapsed') !== -1;
  var padding = {};
  if (collapsed ) {
    classes.splice(classes.indexOf('collapsed'), 1);
    padding[id] = 260;
    map.easeTo({
      padding: padding,
      duration: 1000 
    });
    } 
  else {
    padding[id] = 0;
    classes.push('collapsed');
    map.easeTo({
      padding: padding,
      duration: 1000
    });
  }
  elem.className = classes.join(' ');
  collapsed = !collapsed;
}  
1
  • This is perfect. Thanks!
    – Matt
    Dec 5, 2023 at 1:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.