Mapbox.js - click item on list and pan to the marker and show popup

2.8k views Asked by At

I started out with a .js file and revised it so that it would: 1. Filter the data in the geoJSON (top right buttons)
2. Show popups with relevant data when you click the marker
3. Shows a list of stores based on the markers that are inbounds or the markers that are currently showing on the map. When you zoom in, there will be less markers and also less items listed on the left.

Now I can't get these 2 to work:

  1. Div class with 'item' changes so that it adds "active" to the classname
  2. List item when I click the links in the list on the lefthand side, it doesn't pan to the marker that is clicked on and show the popup anymore. Instead it shows the markers for all of the locations in the list one at a a time, ending with the very last one. I'm sure it has something to do with locations.eachLayer, but I don't know how to fix it so that it will show the data or select the data that I want to. I have a feeling I may not be using the event handler in the right order as well.

Visual
OLD FILE: When I click on an item in the list on the left, the map automatically pans to and opens up that marker and popup.
NEW FILE (in progress): Trying to get it to work

// *****This is the map
L.mapbox.accessToken = 'pk.eyJ1IjoiamVubmlmZXJwaGFtIiwiYSI6Ijc3NmJkZWE1YjM0ZDc0MWU2Yzc0MWM0YWQ5NzRiNzliIn0.OaKjiklBTRs_Saoh1wSglw';
var map = L.mapbox.map('map', 'glidewell.jno36i2l');
// END of the map

$( document ).ready(function() {


// ******This code zooms to the country locations listed on the page
document.getElementById('navigation').onclick = function(e) {
    e.preventDefault();
    var pos = e.target.getAttribute('data-position');
    var zoom = e.target.getAttribute('data-zoom');
    if (pos && zoom) {
        var loc = pos.split(',');
        var zoo = parseInt(zoom);
        map.setView(loc, zoo);
        return false;
    }
}
// END of that code


//this calls the <div id="listings>
    var listings = document.getElementById('listings');
// *****This brings in the locations or GEO JSON file
    var locations = L.mapbox.featureLayer().addTo(map);
    var listing = $('div.item');
    var link = $('a.title');

//this is the file that the data comes from
locations.loadURL('js/testingdummydata.geojson');
// END of that code

// ****** This code zooms to the point on hr map when a selection is made from the list
function setActive(el) {
    var siblings = listings.getElementsByTagName('div');
    for (var i = 0; i < siblings.length; i++) {
        siblings[i].className = siblings[i].className
                .replace(/active/, '').replace(/\s\s*$/, '');
    }

    el.className += ' active';
}
// END of that code


// *****This code merges the Map and the list together
  locations.on('ready', makepopup).on('ready', showMarkersinBound)
          .on('ready', goToMarker);


function makepopup() {
    locations.eachLayer(function (locale) {

        var prop = locale.feature.properties;
// Shorten locale.feature.properties to just `prop` so we're not
// writing this long form over and over again.
        var popup = '<h3>' + prop.title + '</h3><div>' + prop.address + '<br>' + prop.city + ', ' + prop.state + '<br>' + prop.phone + '<br>' + '<a href="http://' + prop.website + '" target="_blank">' + prop.website + '</a>';

        if (prop.crossStreet) {
        //    link.innerHTML += '<br /><small class="quiet">' + prop.crossStreet + '</small>';
            popup += '<br /><small class="quiet">' + prop.crossStreet + '</small>';
        };

     

        // Marker interaction
        locale.on('click', function (e) {
            // 1. center the map on the selected marker.
            map.panTo(locale.getLatLng());

            // 2. Set active the markers associated listing.
            setActive(listing);
        });

        popup += '</div>';
        locale.bindPopup(popup);
    });
};

function goToMarker(){

    locations.eachLayer(function (locale) {

        $("a[href='#'][class='title']").click(function () {
              setActive(listing);
                // When a menu item is clicked, animate the map to center
                // its associated locale and open its popup.
              map.setView(locale.getLatLng(), 16);

                locale.openPopup();
                return false;
        });
    });
};


// *****Filters

$('.menu-ui-btn a').on('click', function() {
    //adds "active" class to the link being clicked on and removes active class from the other links in menu
    $(this).addClass('active').siblings().removeClass('active');
    // For each filter link, get the 'data-filter' attribute value and assign it to a variable called filter.
    var filter = $(this).data('filter');
    // The setFilter function takes a GeoJSON feature object
    // and returns true to show it or false to hide it.
    locations.setFilter(function(feature) {
        return (filter === 'all') ? true : feature.properties[filter] === true;
    });
    makepopup();
    showMarkersinBound();
    return false;
});

//*******LIST OF MARKERS INSIDE VIEW

function showMarkersinBound() {
    // Construct an empty list to fill with onscreen markers.
    var inBounds = [];

    // Get the map bounds - the top-left and bottom-right locations.
    var bounds = map.getBounds();

    // For each marker, consider whether it is currently visible by comparing
    // with the current map bounds.
    locations.eachLayer(function (marker, locale) {
        if (bounds.contains(marker.getLatLng())) {
            var oneListing;
            var prop = marker.toGeoJSON().properties;
    
            oneListing= '<div class="item"><a href="#" class="title">' + prop.title + '</a>' + prop.city;
            if (prop.state) {
                oneListing += ', ' + prop.state;
            };
            oneListing += '</div>';
          inBounds.push(oneListing);  //adds the oneListing item into inBounds array
        }  //closes if statement
    });

    // Display a list of markers in id="listings" on the DOM.
   listings.innerHTML = inBounds.join('');

};


//when map moves, trigger showMarkersinBound function
map.on('move', showMarkersinBound);

});
/* google.com/fonts import files go above this line ^


/* Nav / Menus
================= */
.menu-ui-btn {
background:#fff;
position:absolute;
top:10px;right:10px;
z-index:1;
border-radius:3px;
width:120px;
border:1px solid rgba(0,0,0,0.4);
}
.menu-ui-btn a {
font-size:13px;
color:#404040;
display:block;
margin:0;padding:0;
padding:10px;
text-decoration:none;
border-bottom:1px solid rgba(0,0,0,0.25);
text-align:center;
}
.menu-ui-btn a:first-child {
border-radius:3px 3px 0 0;
}
.menu-ui-btn a:last-child {
border:none;
border-radius:0 0 3px 3px;
}
.menu-ui-btn a:hover {
background:#f8f8f8;
color:#404040;
}
.menu-ui-btn a.active,
.menu-ui-btn a.active:hover {
background:#3887BE;
color:#FFF;
}


/* Content
================= */

#marker-list {
        position:absolute;
        top:0; right:0;
        width:200px;
        bottom:0;
        overflow-x:auto;
        background:#fff;
        margin:0;
        padding:5px;
        height: 400px;
        z-index: 50;
        background-color: white;
}
#marker-list li {
        padding:5px;
        margin:0;
        list-style-type:none;
}
#marker-list li:hover {
        background:#eee;
}


.sidebar {
position:absolute;
width:25%;
height:100%;
top:0;left:0;
overflow:hidden;
border-right:1px solid rgba(0,0,0,0.25);
}
.pad2 {
padding:20px;
}
.quiet {
color:#888;
}
.map {
position:absolute;
left:25%;
width:75%;
top:0;bottom:0;
}
.heading {
background:#fff;
border-bottom:1px solid #eee;
padding:0 10px;
}
.listings {
height:100%;
overflow:auto;
padding-bottom:60px;
}
.listings .item {
display:block;
border-bottom:1px solid #eee;
padding:10px;
text-decoration:none;
}
.listings .item:last-child { border-bottom:none; }
.listings .item .title {
display:block;
color:#BA222B;
font-weight:700;
}
.listings .item .title small { font-weight:400; }
.listings .item.active .title,
.listings .item .title:hover { color:#bbb; }
.listings .item.active {
  background-color:#f8f8f8;
  }

::-webkit-scrollbar {
width:22px;
height:3px;
border-left:0;
background:rgba(0,0,0,0.1);
}
::-webkit-scrollbar-track {
background:none;
}
::-webkit-scrollbar-thumb {
background:#BA222B;
border-radius:0;
height: 60px;
}

.clearfix { display:block; }
.clearfix:after {
content:'.';
display:block;
height:0;
clear:both;
visibility:hidden;
}

/* Marker tweaks
====================*/
.leaflet-popup-close-button {
display:none;
}
.leaflet-popup-content {
font:400 15px/22px 'Source Sans Pro', 'Helvetica Neue', Sans-serif;
padding:0;
width: auto;
}
.leaflet-popup-content-wrapper {
padding:0;
}
.leaflet-popup-content h3 {
background:#BA222B;
color:#fff;
margin:0;
display:block;
border-radius:3px 3px 0 0;
font-weight:700;
margin-top:-15px;
}
.leaflet-popup-content div {
padding:10px;
}
.leaflet-container .leaflet-marker-icon {
 cursor:pointer;
}
/**********************************************************
Media Querys (480px, 768px, 990px, 1200px, 1500px, 2000px)
***********************************************************/

/* Smaller than
================= */

/* Applied to resolutions less than 2000 (3x) */
 @media (max-width: 2000px) {

 }

/* Applied to resolutions less than 1740 (2x) */
 @media (max-width: 1740px) {

 }

/* Applied to resolutions less than 1500 (Xl) */
 @media (max-width: 1500px) {

 }

/* Applied to resolutions less than 1200 (lg) */
 @media (max-width: 1200px) {

 }

/* Applied to resolutions less than 990 (md) */
 @media (max-width: 990px) {

 }

/* Applied to resolutions less than 768 (sm) */
 @media (max-width: 768px) {

 }
 
/* Applied to resolutions less than 568 (sm) */
 @media only screen and (max-width: 568px) {
        .sidebar {
        position: absolute;
        width: 100%;
        height: 60%;
        bottom: 0;
        left: 0;
        overflow: hidden;
        border-right: 1px solid rgba(0,0,0,0.25);
        top: inherit;
        }
        .map {
        position: absolute;
        bottom: 60%;
        width: 100%;
        top: 0;
        left: 0;
        }
        .heading {
        background: #fff;
        border-bottom: 1px solid #eee;
        height: 60px;
        padding: 3px 10px;
        }
        .leaflet-container .leaflet-control-attribution {
          display: none;
        }
        .listings {
        height: 233px;
        }
      }     

/* Applied to resolutions less than 480 (XS) */
 @media (max-width: 480px) {

 }

/* Larger than
================= */

/* Applied to resolutions larger than 2000 (3x) */
 @media (min-width: 2001px) {

 }

/* Applied to resolutions larger than 1500 (2x) */
 @media (min-width: 1501px) {

 }

/* Applied to resolutions larger than 1200 (xl) */
 @media (min-width: 1201px) {

 }

/* Applied to resolutions larger than 990 (lg) */
 @media (min-width: 991px) {

 }

/* Applied to resolutions larger than 768 (sm) */
 @media (min-width: 769px) {

 }

/* Applied to resolutions larger than 480 (xs) */
 @media (min-width: 481px) {

 }

/***************************************
Default Style Sheet created as part of - The Maui Toolbox
For more information visit: www.mauitoolbox.com
pub-20150501
***************************************/

#coordinates {
        height:450px;
        overflow:auto;
        padding-bottom:60px;
}
#coordinates .item {
        display:block;
        border-bottom:1px solid #eee;
        padding:10px;
        text-decoration:none;
}
#coordinates .item:last-child { border-bottom:none; }
#coordinates .item .title {
        display:block;
        color:#BA222B;
        font-weight:700;
}
#coordinates .item .title small { font-weight:400; }
#coordinates .item.active .title,
#coordinates .item .title:hover { color:#bbb; }
#coordinates .item.active {
        background-color:#f8f8f8;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!DOCTYPE html>
<html lang="en">
 <head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="Description" content="#"> <!-- (Example: Compelling description to be displayed on Search Engine) -->
  <meta name="author" content="#"> <!-- (example: Developers Name) -->
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>[PAGE TITLE]</title>
<!-- (Extrenal) Default Style Sheets =========== -->
  <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css">
  <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
  <link rel="stylesheet" type="text/css" href="http://gl-dev-staticweb.s3.amazonaws.com/blimp.css">
  <link rel="stylesheet" type="text/css" href="http://gl-dev-staticweb.s3.amazonaws.com/maui.css"> 
<!-- Style Sheets ============================== -->
  <link rel="stylesheet" type="text/css" href="css/map-styles.css">
<!-- Additional Style Sheets =================== -->
  <link href='https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,700' rel='stylesheet'>
  <script src='https://api.tiles.mapbox.com/mapbox.js/v2.1.9/mapbox.js'></script>
  <link href='https://api.tiles.mapbox.com/mapbox.js/v2.1.9/mapbox.css' rel='stylesheet' />

 </head>
 <body>

<div class='sidebar'>
 <div class='heading'>
  <h1>ABC Company</h1>
  <div class="dropdown">
   <a id="dLabel" data-target="#" href="http://example.com" data-toggle="dropdown" aria-haspopup="true" role="button" aria-expanded="false">
   Select Country
   <span class="caret"></span>
    </a>
    <ul class="dropdown-menu" role="menu" aria-labelledby="dLabel" id='navigation'>
    <li><a href='#' data-zoom="5" data-position='37.0,-95.7'>USA</a></li>
    <li><a href='#' data-zoom="8" data-position='56.1,9.5'>DNK</a></li>
    <li><a href='#' data-zoom="8" data-position='55.3,-3.4'>GB</a></li>
    <li><a href='#' data-zoom="6" data-position='46.2,2.2'>FRA</a></li>
    <li><a href='#' data-zoom="7" data-position='41.8,12.5'>ITA</a></li>
    <li><a href='#' data-zoom="6" data-position='-30.5,22.9'>ZAF</a></li>
    <li><a href='#' data-zoom="6" data-position='20.5,78.9'>IN</a></li>
    <li><a href='#' data-zoom="8" data-position='39.0,21.8'>GRC</a></li>
    <li><a href='#' data-zoom="6" data-position='26.8,30.8'>EGY</a></li>
    <li><a href='#' data-zoom="8" data-position='60.4,8.4'>NOR</a></li>
    <li><a href='#' data-zoom="8" data-position='48.6,19.6'>SVK</a></li>
    <li><a href='#' data-zoom="5" data-position='-25.3,133.7'>AUS</a></li>
    <li><a href='#' data-zoom="8" data-position='-40.9,174.8'>NZ</a></li>
    <li><a href='#' data-zoom="5" data-position='35.4,104.1'>CHN</a></li>
    <li><a href='#' data-zoom="8" data-position='15.8,100.9'>THA</a></li>
    <li><a href='#' data-zoom="8" data-position='52.1,5.2'>NLD</a></li>
    <li><a href='#' data-zoom="8" data-position='9.7,-83.7'>CR</a></li>
    <li><a href='#' data-zoom="6" data-position='23.6,-102.5'>MX</a></li>
    </ul>
  </div>
 </div>
 <div id='listings' class='listings'></div>
</div>

<!-- Filter buttons - filters markers on map-->
<nav class='menu-ui-btn'>
 <a href='#' class='active' data-filter='all' id="allfilter">Show all</a>
 <a href='#' data-filter='mill' id="millfilter">Mills</a>
 <a href='#' data-filter='lab' id="labfilter">Labs</a>
</nav>
<!-- Map is displayed here -->
<div id='map' class='map'></div>

<script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>

2 relevant examples on Mapbox documentation:
1. List markers and pan to clicked items
2. Listing markers in view

I've spent 2 whole days looking at this over and over, and I could really use someone's help with expert Javascript knowledge and possibly even mapbox.js experience.

1

There are 1 answers

0
codergirl On

Nevermind I got it to work:

I added data-name attribute to the html being shown in the DOM so that I could get the unique name of the item in the list (added it to "oneListing" variable).

function showMarkersinBound() {
// Construct an empty list to fill with onscreen markers.
var inBounds = [];

// Get the map bounds - the top-left and bottom-right locations.
var bounds = map.getBounds();

// For each marker, consider whether it is currently visible by comparing
// with the current map bounds.
locations.eachLayer(function (marker) {
    if (bounds.contains(marker.getLatLng())) {
        var oneListing;
        var prop = marker.toGeoJSON().properties;
        //oneListing= '<div class="item"><a href="#" class="title">' + prop.title + '</a><br>' + prop.address + '<br>' + prop.city + ', ' + prop.state + '<br>' + prop.phone + '<br>' + '<a href="http://' + prop.website + '" target="_blank">' + prop.website + '</a></div>';
        oneListing= '<div class="item"><a href="#" class="title" data-name="' + prop.title + '">' + prop.title + '</a>' + prop.city;
        if (prop.state) {
            oneListing += ', ' + prop.state;
        };
        oneListing += '</div>';
      inBounds.push(oneListing);  //adds the oneListing item into inBounds array
    }  //closes if statement
});

   // Display a list of markers in id="listings" on the DOM.
   listings.innerHTML = inBounds.join('');
}; //closes function

Then function goes through all layers, and I made an if else statement so that it only changes the view and pans to the marker if the name of the link being clicked on is same as the title of that layer.

function goToMarker() {

locations.eachLayer(function (layer) {
    var itsTitle = layer.toGeoJSON().properties.title;

    // var prop = locale.feature.properties;
    $(".title").click(function () {
        if (($(this).data('name')) === itsTitle) {
            // When a menu item is clicked, animate the map to center
            // its associated locale and open its popup.
            map.setView(layer.getLatLng(), 16);
            layer.openPopup();
            return false;
        };
    });
   });
}

Then I added them as event listeners when location was ready.

var locations = L.mapbox.featureLayer().addTo(map);
locations.loadURL('js/testingdummydata.geojson');
locations.on('ready', makepopup).on('ready', howMarkersinBound).on('ready', goToMarker);
//when map moves, trigger showMarkersinBound function
map.on('move', showMarkersinBound).on('move', goToMarker);