Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent Leaflet from scrolling the page when clicking the zoom buttons?

I have a leaflet map on an HTML page. When the page is scrolled so that any part of the map is outside of the viewport and I click the zoom buttons, the map jumps so that it's completely visible in the viewport. If the whole map doesn't fit, then the top of the map becomes aligned with the top of the viewport and the bottom just hangs off the end.

This is annoying, because then the zoom button which the user just pressed is no longer under the cursor and they have to go find it again.

I have tried both the built-in zoom bar and a custom one where I use map.setZoom() and the behaviour is consistent. However, the sample maps on https://leafletjs.com don't do this, so I'm at a loss.

My map is quite simple:

var home = {center: L.LatLng(51.499725, -0.124695), zoom: 11};
var mymap = L.map('map').setView(home.center, home.zoom);

This question sounds similar to what I'm seeing, but is more complex than my situation. And anyway, there's no solution.

Edit: Here is the html and CSS, but it couldn't be more basic.

map.html

    <div class="container mx-sm-3">
        <div class="row">
            <div class="col">
                <div id="map"></div>
            </div>
        </div>
    </div>

map.css

#map {
    height: 570px;
    width: 700px;
}
like image 957
Nick K9 Avatar asked Jul 24 '19 13:07

Nick K9


2 Answers

The culprit is this line of code:

this._map.getContainer().focus();

The rationale behind that? The map container must have the focus so that keyboard events are routed through it. Clicking on a zoom button would set the focus on the button itself, and make the map misbehave under some circumstances.

But a side effect of focus is that focusing a HTML element will make the user agent (web browser) to scroll it into view, since it is assumed that the user will want to see the focused element before doing any keyboard input into it.

Armed with this knowledge, I'll rephrase your question in two different ways:

I do not need the map element to be focused at all. How do I prevent Leaflet from focusing the map after clicking on the zoom buttons?

By making the _refocusOnMap() private method of L.Control do nothing.

This can be done the quick&dirty way by monkey-patching it:

L.Control.prototype._refocusOnMap = function _refocusOnMap() {};

Remember to monkey-patch before instantiating the map. The "clean" would be to create subclasses of L.Control and/or L.Control.Zoom, based on this. If you have your own zoom controls, you can also provide a custom _refocusOnMap method instead of relying on the one provided by the parent class.

I would like the keyboard controls to keep working, but I do not want to scroll the whole map into view after pressing the zoom buttons. Can the map be focused but skipping the "scroll into view" business?

Yes, with caveats.

The focus method of HTMLElements accepts an options parameter which can hold a preventScroll option.

So you could monkey-patch _refocusOnMap() in the following way:

L.Control.prototype._refocusOnMap = function _refocusOnMap(ev) {
    // if map exists and event is not a keyboard event
    if (this._map && ev && ev.screenX > 0 && ev.screenY > 0) {
        this._map.getContainer().focus({ preventScroll: true });
    }
};

Be aware, however, that support for preventScroll is not present in all browsers.

Another approach would be to preventDefault either the focus or focusin events on the map, with something like:

L.DomEvent.on(map.getContainer(), 'focus', L.DomEvent.preventDefault)

However, due to inconsistencies between web browsers, this might not only stop the scroll but the focusing, or might have no effect at all, depending on which web browser is being used.

like image 175
IvanSanchez Avatar answered Sep 28 '22 00:09

IvanSanchez


    this._map.getContainer().focus = ()=>{}
like image 23
Drok Avatar answered Sep 27 '22 22:09

Drok