Leaflet Web Map Helper Functions

two people holding hands near the ocean

I believe it was back at GIS Day 2015 when I decided I was going to give the Leaflet.js web mapping library a shot.  I had some HTML and CSS skills under my belt, and knew a little bit about JavaScript.  I started creating simple embedded maps.  My first full-page app had a weak design, but got the job done.  I soon learned about the BootLeaf and Esri Calcite Maps projects, which provide a great Bootstrap-based design template.

Through this journey of developing web mapping applications, I discovered various helper functions that I believe create a better experience for the user.  I share links to a few of these apps so you can see these helper functions in action.

Helper Functions

Setting the Initial Map Zoom Level

When you construct the map object in Leaflet, there is an option to set the zoom level (or map scale).  I quickly found that having the same zoom level on desktop computers and mobile phones provides a less than ideal user experience.  For example, the map will have the desired map extent on desktop devices.  But on smaller screens, the desired map extent will not load.  To solve this issue, I created a function that returns the initial zoom level based upon a series of conditional statements that test the browser window width.

comparison between a map loading on a desktop and mobile device
Using the same initial zoom level, devices with smaller screens do not show the intended map extent.
// Set the initial map zoom level based upon viewport width
function setInitialMapZoom(windowWidth) {
    // create variable for map zoom level
    var mapZoom;    

    // test for various browser widths
    // fine tune the conditional statements as needed for your map
    if (windowWidth < 500) {
       mapZoom = 9;
    }  else if (windowWidth = 500 && windowWidth < 1000) {
       mapZoom = 10;
    }  else {
       mapZoom = 11;
    }

    // we can pass this function as the value for the initial zoom level
    // when constructing the map object
    return mapZoom;
}

// browser window width
var windowWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;

// Pass the function into the map constructor
var map = L.map('map', {
   center: [40.125, -80.258],
   zoom: setInitialMapZoom(windowWidth)
});

Setting Max Height and Width for Popup

I also found that it is beneficial to provide height and width constraints for layer popups on mobile devices.  Similar to the previous function, there is a set of conditional tests that assign the max height and/or width for the popup, based upon the browser height/width.  The functions are called within the options of the the bindPopup() method of the layer.

These functions help with the limited space on smaller devices like mobile phones.  We might have the popup have a vertical scroll bar of phones, but no scroll bar on desktop devices.  Controlling the width of the popup helps to keep it from being wider than the screen.

screen shots of web map comparing how to control the popup size on desktop and mobile phones
The map features pop-up has different settings on desktop and mobile devices.
// Set max width of pop-up window
function setPopupMaxWidth(windowWidth) {
   // create variable for max width
   var maxWidth;

   // test for various browser widths
   if (windowWidth &lt; 450 ) {
      maxWidth = 240;
   } else {
     maxWidth = 300;
   }

   // set the maxWidth property of the popup equal to this function
   return maxWidth;
}

// Set max height of pop-up window
function setPopupMaxHeight(windowArea) {
   // create variable for max height
   var maxHeight;

   // windowArea is the viewport width multiplied by the viewport height
   if (windowArea &lt; 315000) {
        maxHeight = 150;
    } else {
        maxHeight = 300;
    }

    // set the maxHeight property of the popup equal to this function
    return maxHeight;
}

// viewport related variables
// viewport width
var windowWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
// viewport height
var windowHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
// viewport area
var windowArea = windowWidth * windowHeight;

// Bind popup to a layer
layer.bindPopup(function(evt,layer) {
   // add content
   return L.Util.template(&quot;
<div>
<h1>{Name}</h1>
</div>
", evt.feature.properties);},
{closeOnClick: true, maxHeight: setPopupMaxHeight(windowArea), maxWidth: setPopupMaxWidth(windowWidth)}
);

Controlling Map Tooltips

One of the great features of the 1.x release of Leaflet was the ability to add tooltips to features.  Let’s say you have a map of farmer’s markets.  Whenever you mouse-over a site, you would get a tooltip with the name and address of the market.  But what about for touch devices, which don’t emit a mouse-over event?

By default, when you would press on a site, you would get a tooltip and a popup to open, which if you ask me, doesn’t make sense.  My solution was to use Leaflet’s L.Browser.touch, which detects touch devices.  If the browser isn’t on a touch device, then I call a function which binds a tooltip to a layer.  An additional step I take for non-touch browsers is that I unbind the tooltip when the popup is open, and then re-bind it when the popup closes.

I’m only adding the tool-tip for non-touch browsers. I am aware I may be missing some browsers here.  If you have any tips for a better approach, please let me know in the comments.

// boolean if browser is on a touch device or not
var isTouchDevice = L.Browser.touch;

// Add tooltip to farmers markers
function bindTooltipFarmMarkets() {
   // use Leaflet's bindTooltip method
   farmMarkets.bindTooltip(function(evt) {
      // create HTML content for tooltip. Assumes using GeoJSON
      return L.Util.template("{Name}, located at {Address}, {City}, {State}, {Zip}", evt.feature.properties);
      }, {opacity: 1, interactive: true});
}

// Add Popup to farmers markets
farmMarkets.bindPopup(function(evt, layer) {
    // for non-touch devices, close tool tip when popup opens
    if (!isTouchDevice) {
        evt.on("popupopen", function() {
           // remove tooltip on popup open event
           farmMarkets.unbindTooltip();
        });
        // add tooltip on popup close event
        evt.on("popupclose", bindTooltipFarmMarkets);
    }
// other code for bindPopup method
});    

// Add tool tip for non-touch browsers
if (!isTouchDevice) {
    bindTooltipFarmMarkets();
}

Final Thoughts

So that’s a sample of a few functions/tricks I use whenever I’m creating a Leaflet web map.  Hopefully you’ve found these useful.  If you want to check these out in action, I’ve included links to a couple apps I’ve developed.  I would suggest using Chrome’s developer tools to see how the apps respond across browser sizes.  Or you could visit it on your computer and phone/tablet as well.

Maps:

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s