Leaflet marker popup extends past the map - javascript

I had to change the default size of the marker popup to accommodate the data I wanted to put in it. I changed some map settings so it always bounces back to a max bound when the user drags it out of view. The issue I have is that for markers close to the edge, when its popup opens, it is not fully visible, as in it hides behind the edges of the container for the map.
Q: What would be the fix to this? Can I get the popup to show the full content?

In this Leaflet tutorial they have an option to offset the popup anchor. You could do an if statement that if true would offset the popup anchor by enough to display properly on the screen.
Pseudo code:
if (latlng of point clicked < rightBounds of map) {
popupOptions = {
offset: (-popupwidth,0)
}
} ...
You could also try experimenting with the various popup options in the API.

Related

Opening modal where SVG map is clicked

Looking for best practise around a feature I'm looking to implement.
I have an SVG map, which contains little blue icons where a user can click, see image below for reference:
When a user clicks any of the blue icons, I'm looking to open a little modal by it that will showcase that regions info. For example, see the below screenshot:
So, in the above instance, the user has clicked on the blue circle on the bottom, which opens a little modal for that region.
Now, in my svg, I have added id's for each blue icon (<g class="mapLocations__icon" id="uk" ...">, but with this approach, the two options I see are:
Absolute positioning the modal for each region. But this sounds like a pain for responsive.
Writing repeat markup for the modal within the svg itself.
Neither seem like a good approach.
Any recommendations?
Here is my svg for reference: https://jsfiddle.net/6squyc7d/ (Added as jsfiddle as pasting the code puts me over the charachter limit)
In the onclick event you can use the x and y coordinates to absolute position the modal dynamically. So actually you could create and position the modal right on the onclick function.
function openModal( evt ) {
var intCoordX = evt.clientX;
var intCoordY = evt.clientY;
// Modify modal properties
// Open modal
}
For each dot, you have an Id. You can add an onclick listener to each dot with an forEach loop (all Ids in an array). You can then put this code into the forEach loop:
const dot = document.getElementById('canada')
dot.addEventListener('click', event => {
console.log(event.x, event.y)
});
In this code you get the dot with the Id (canada). Then you have the click event and you can see where the user clicked on their screen (x and y pixels).
Then you can place an absolute div with the top and left value.
.position {
position: absolute;
opacity: 0;
}
You can then with JS place the modal with top and left on the right position and set the opacity to 1;
It will make the div show on the correct place.

Expanding“clickable” area for custom icons using Leaflet

I have a searchable Leaflet map. The green icons represent all of the existing data and when you search for an address it shows a blue pushpin icon.
I would expect to be able to click anywhere on my custom (blue pushpin) icon and get a popup as a result. But nothing happens when I click on it.
I have to click on a very specific spot in order to get the popup to activate. I can tell it's the right spot because my hover text (on the bottom left) changes. I want to make it so that the results pop up if I click anywhere on the custom icon (or even the original green icon).
I'm using a template, so I'm not completely sure which part of the code controls the hover actions or the "clickable" area, but I'm guessing it's this...?
var hover_template;
$.get( "./templates/hover.ejs", function( template ) {
hover_template = template;
});
SearchableMapLib.info.update = function (props) {
if (props) {
this._div.innerHTML = ejs.render(hover_template, {obj: props});
}
else {
this._div.innerHTML = 'Hover over a ' + SearchableMapLib.recordName;
}
};
SearchableMapLib.info.clear = function(){
this._div.innerHTML = 'Hover over a ' + SearchableMapLib.recordName;
};
From the source code of the framework / template you are using, it seems like the custom icon / blue pushpin is just a "center mark" of the geocoding result, without attached data. Hence the absence of hover and popup.
https://github.com/datamade/searchable-map-template-csv/blob/master/js/searchable_map_lib.js#L321-L329
On the contrary, the green icon Markers seem to be results of a 2nd search, based on the coordinates of the previous result and a fixed radius. They have this hover and click popup features. Hence you get them when on the green icon behind the blue pin.
Therefore it seems like what you experience is the expected behaviour, even though it can be strange when the blue pin matches one of the green icons.
If you expect a different behaviour, it seems to me that you should raise an issue on the repo of that framework / template.

don't change mouse icon when hovering over a marker

I have a map with a number of markers. When a user clicks on one marker information for that marker is displayed on a side pane. To accomplish that I have added 'click' listeners to the markers and also store marker identifiers more or less as suggested in this SO answer.
Now, on certain modes I don't want the markers to be clickable (but still want them to appear on the screen). It is easy for me to remove all the 'click' listeners. However, when I hover over them with my mouse, the icon does change from the "open palm" to the "pointed hand" confusing the user. Upon investigating I see that the canvas class normally has the leaflet-zoom-animated class, but when I hover over a marker, the leaflet-interactive class gets added. I can change that cursor using, e.g.:
.leaflet-interactive {
cursor: crosshair !important;
}
... but this has two problems:
it's not something I can toggle on and off depending on the various user interaction modes my application finds itself in
it's still jarring because the cursor does change and, further, I can't change it to the open palm cursor that Leaflet is normally using, since that's a non-default cursor and it's not clear to me how to access it.
If you can remove the click listener, I suppose that you can also add a css class to your marker. Here is an example http://jsfiddle.net/Mossart/w9830at1/7/ (look at the top marker)
var breakside = [45.571601194035345, -122.65673562884331];
var marker1 = L.marker(breakside).addTo(map);
marker1._icon.classList.add("not-clickable");
CSS:
.not-clickable {
cursor: grab;
}
This works for L.circleMarker objects on a canvas renderer:
marker.options.interactive = false;
Curiously, it doesn't work on a non-canvas renderer.

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;
}
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.
this._map.getContainer().focus = ()=>{}

Positioning button inside leaflet layer control

I'm using the leaflet styled layer control plug in to customize my map control.
I'm trying to add a button to the top right corner of my controls that closes the controls (primarily for mobile use).
I found this question and it works, but now I'm wondering how I can position this button. I want it to be in the top right corner of the control.
When I position the class "leaflet-control-close" it works (I tested with "position: relative; float: right;" and it did float right but stayed at the bottom), but if I give it an absolute position at the top of the page it seems to be appear behind my overlays.
I want it to push down the overlays a little bit so that it doesn't overlap.
EDIT: here is a jsfiddle showing what's happening. It's a little convoluted because the styled layer control files don't have a CDN so I just pasted in the CSS and javascript. I made a note where I add the button in the original Javascript, and below that JS is the JS to create the map and overlays.
I know the button on a desktop is kind of annoying because when you close the control with it, it immediately pops back open because your mouse is hovering. In my actual code I've set the display to none if the screen width is larger than tablet size, because that's the only time I feel like I need the button.
I customized the styled layer control javascript in order to include the button:
L.Control.StyledLayerControl = L.Control.Layers.extend({
onAdd: function(map) {
this._initLayout();
this._update();
this._addButton();
map
.on('layeradd', this._onLayerChange, this)
.on('layerremove', this._onLayerChange, this);
return this._container;
},
_addButton: function () {
var elements = this._container.getElementsByClassName('leaflet-control-layers-list');
var button = L.DomUtil.create('button', 'layer-control-close', elements[0]);
button.innerText = 'Close control';
L.DomEvent.on(button, 'click', function(e){
L.DomEvent.stop(e);
this._collapse();
}, this);
}
)};

Categories