Better Marker Controls with LeafletJS

This is a follow up post to Custom Marker Controls with Mapbox and LeafletJS.

As with many projects, things changed and I was asked to add more controls to the map.

Each marker had it’s own category, and now each had its own language, Welsh or English.

Adding another custom control to show or hide markers based on two different taxonomies means a proper system for checking if a marker should be displayed or not. Much of the code I previously wrote was refactored.

First of all the language had to be added to the GeoJSON object.

language = { language : plentyn.language, code : plentyn.lang_code };

If the video had no language (i.e. deemed to be bilingual) it was an empty object. This property was added just like the category.

// the marker. all values are for demonstration
var geoObj = {
    type: 'Feature',
    geometry : { type : 'Point', coordinates : [ 53, -4 ] },
    properties : { 
        title : 'Video Title',
        video : 'http://vimeo.com/123',
        category : category,
        language : language
    },

To keep track of which markers should be shown or hidden, I had two arrays – categoryHide and langHide – to store them.

When a category or language checkbox was clicked, the code would add or remove the marker from either array.

// each checkbox had a data-df-type attribute to store the category or language

// value of the checkbox
var Tvalue = input.value;

// for the category marker...
if(input.dataset.dfType == 'category')
{
    if(input.checked)
    {
        categoryHide.splice(categoryHide.indexOf(Tvalue), 1); // remove (to show)
    }
    else
    {
        categoryHide.push(Tvalue); // add (to hide)
    }
}

After each array was prepared, I called a toggleMarker() function. This function would loop through each marker in the main array. If the marker was hidden it would be shown, then hidden if it was in the either of the hide arrays.

// pass the main map object
toggleMarker = function(map) 
{
    // loop through each marker
    for(var i in Dymafi.marks)
    {
         var mrkr = Dymafi.marks[i];

        // show all hidden markers
        if(!map.hasLayer(mrkr))
            map.addLayer(mrkr);

        // check if marker language is in hide array
        if(languageHide.indexOf(mrkr.feature.properties.language.code) != -1)
        {                        
            // hide this marker
            if(map.hasLayer(mrkr))
                map.removeLayer(mrkr)
        }

        // check if marker category is in hide array
        if(categoryHide.indexOf(mrkr.feature.properties.category.id.toString()) != -1)
        {                        
            // hide this marker
            if(map.hasLayer(mrkr))
                map.removeLayer(mrkr)
        }
    }
}

This is an improvement on the old solution, and ready for any more checkboxes that may need to be added.

UI

Although it worked well, a new problem was created. The control box was growing and was getting in the way on smaller screens.

I looked at Leaflet’s documentation and tutorials for ideas. It had a nice built in show hide ability when controls were created for its own layers, but were unavailable (for whatever reason) for custom controls.

So with a bit of HTML, jQuery and CSS I had my own system working:

I added an anchor with a background icon to the control <div>:

<a href="#" class="menuclick"><i class="icon-align-justify"></i></a>

The jQuery adds or removes a class from the parent <div>:

// set a variable if touch is enabled
var clickevent = 'mousedown';
if ('ontouchstart' in document.documentElement) {
    clickevent = 'touchstart';
}

// show/hide the categories on hover
$('a.menuclick').on('mouseenter', function(){
    $(this).parent().addClass('expanded');
});
$('.cats').on('mouseleave', function(){
    $(this).removeClass('expanded');
});
// the same for touchscreens
if(L.Browser.touch) // LeafletJS utility 
{
    $('a.menuclick').on(clickevent, function(){
        $(this).parent().addClass('expanded');
    });

    // hide the div if the map is moved
    map.on('movestart', function(){
        $('.cats').removeClass('expanded');
    });
}  

And the CSS:

/* .cats is the main wrapper */
/* show hide on touch/mouseover */
.cats .options{
display:none;
}
.cats.expanded .options{
display:block;
min-width: 130px;
padding:15px;
}
.cats a{
display:inline;
padding:10px;
}
.cats.expanded a{
display:none;
}

This need some clearing up, but works great.

You can see the finished result on the Dyma Fi map page.

2 thoughts on “Better Marker Controls with LeafletJS”

  1. This helped me a LOT. Thanks for taking the time to put it online. There are a few typos in your code examples here (caegoryHide), but obviously the published website is fine.

    I need to do something similar where my markers can be turned on/off based on various properties, but I also need to integrate the layer switcher control and have the markers reset for each base layer. But this is a great start, thanks again.

Leave a Reply

Your email address will not be published. Required fields are marked *