Search code examples
cssleafletz-indexcss-calc

CSS Calc setting a new z-index value based on current z-index value


I have a Leaflet map with hundreds of map markers. Some of these map markers are underneath others, as the map is crowded.

The user would like certain types or criteria of markers to appear on the top of the marker "stack" on the map.

This is easily done with adjusting the Z-index of each marker element.

The marker elements currently have this below CSS, which is loop-generated by Leaflet.js in the construction/population phase:

/* Constructed by Leaflet 1.7 */
element {
    margin-left: -10px;
    margin-top: -20px;
    width: 25px;
    height: 30px;
    transform: translate3d(551px, 252px, 0px);
    z-index: 58;
    outline: currentcolor none medium;
}

The value of z-index is a counter of markers, so this marker is number 58, and the next marker, (regardless of its position on the map) has z-index: 59;, etc.

Here is an example difference, with manually edited z-index;

enter image description here
Above; some red pins are hidden behind blue pins. These would need to be promoted to be more visible.

enter image description here
Above; red pins are promoted above other pins via z-index editing, but also retain their stacking with each other (Ascending).

However, because the number of markers is variable the value of z-index is also variable. I can of course update all of these with javascript, and I'm almost certain that will have to be done, but I was curious if:

Is there a way with CSS (only) to relatively update the z-index value, based on its current value?

For example; to 'promote' this marker could be z-index: calc(<current_value> + 200);

However, I can not find a CSS-only way of catching the current value of Z-index; most "calc" current values use 100% but Z-index does not use percentage.

Why can't we just set it to some high number?
Using some arbitary high catch-all value will not give an ideal result because, for example, setting the z-index to 9999 means all the selected marker elements will have the same z-index. The markers are output from database data and ordered in a certain way (in this case, by date), so the group of markers that are "promoted" should still be relative to each other, while being promoted with an added value to their z-index above those who are not in the group.


Solution

  • You're wrong in this part of your analysis:

    The value of z-index is a counter of markers, so this marker is number 58, and the next marker, (regardless of its position on the map) has z-index: 59;, etc.

    Leaflet manages the zIndex of the marker icons so that markers further north (up) have a greater zIndex than those south (down), with this bit of code, setting a value that is later applied to the CSS property of the icon's ImageHTMLElement:

    this._zIndex = pos.y + this.options.zIndexOffset;
    

    But Leaflet markers have a zIndexOffset option to control the kind of issue you're facing. There's also a setZIndexOffset method available in L.Markers to change this value, e.g.:

    marker1.setZIndexOffset(100);
    marker2.setZIndexOffset(0);
    

    Assuming that you have markers in a L.LayerGroup (or a L.FeatureGroup or L.GeoJSON), you can leverage its invoke method to call setZIndexOffset on all of its markers at the same time, e.g.

    groupWithRedMarkers.invoke("setZIndexOffset", 100);
    groupWithBlueMarkers.invoke("setZIndexOffset", 0);
    

    The value of zIndexOffset should be at least the height of the marker icons (measured in CSS pixels), since Leaflet uses screen pixel coordinates to calculate the final zIndex values. Remember to reset the zIndexOffsets to zero when needed.


    Another approach would be to leverage map panes, assuming that the criteria to display markers on top/bottom doesn't change through time.

    The tl;dr version would be:

    map.createPane('topmarkers');
    map.getPane('topmarkers').style.zIndex = 650;
    marker = L.Marker(latlng, {pane: 'topmarkers'});
    

    Is there a way with CSS (only) to relatively update the z-index value, based on its current value?

    No, since Leaflet changes the z-index of markers dynamically as the user pans and zooms the map.