Reputation: 95
I implemented the example just fine:
https://docs.mapbox.com/mapbox-gl-js/example/popup-on-hover/
but I'm struggling to keep the popup open, when hovering the popup (which means leaving the symbol layer) BUT closing the popup when leaving it.
I tried several combinations of event handlers (even a mousemove handler), but since the layer is drawn on canvas, but popup is an domNode I didnt find a solution.
Anyone know where is is implemented, or how to do it?
My Goal is similar behaviour like googla maps on web:
Sample Code from Docs:
// Add a layer showing the places.
map.addLayer({
'id': 'places',
'type': 'circle',
'source': 'places',
'paint': {
'circle-color': '#4264fb',
'circle-radius': 6,
'circle-stroke-width': 2,
'circle-stroke-color': '#ffffff'
}
});
// Create a popup, but don't add it to the map yet.
const popup = new mapboxgl.Popup({
closeButton: false,
closeOnClick: false
});
map.on('mouseenter', 'places', (e) => {
// Change the cursor style as a UI indicator.
map.getCanvas().style.cursor = 'pointer';
// Copy coordinates array.
const coordinates = e.features[0].geometry.coordinates.slice();
const description = e.features[0].properties.description;
// Ensure that if the map is zoomed out such that multiple
// copies of the feature are visible, the popup appears
// over the copy being pointed to.
while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {
coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360;
}
// Populate the popup and set its coordinates
// based on the feature found.
popup.setLngLat(coordinates).setHTML(description).addTo(map);
});
map.on('mouseleave', 'places', () => {
map.getCanvas().style.cursor = '';
popup.remove();
});
Upvotes: 5
Views: 3791
Reputation: 1
For folks who are okay with the popup overlapping the feature, I've had success with the default style and its bit of overlap of the point and hijacking the map level onMouseMove, this works because when a user is hovering a popup, onMouseMove isn't firing.
const [popupFeature, setPopupFeature] = useState(null)
const onFeatureHover = (f) => setPopupFeature(f)
handleMouseMove(e) {
e.features.length ? onFeatureHover(e.features[0]) :
setPopupFeature(null)
return (
<Map
onMouseMove={(e) => handleMouseMove(e)}
/>
)
Upvotes: 0
Reputation: 36
Recently I have the same problem.
My workaround is to have a function that will close the popup when the userleaves the icon layer or the popup.
If before the timeout is fullfilled the user enters in the popup content, clear the timeout function. (The timeout function will also be called when the user leave popup content).
// TEST modal variables
// popupTEST is the popup where the content will be insert
popupTEST = new mapboxgl.Popup({
closeButton: false,
closeOnClick: false,
className: "popupTEST"
});
// The close of the modal will be via timeout in order to stop removing in when hover the popUp content
popupTESTCloseTimeout = null;
....
this.map.on("mouseenter", "test", (e) => {
this.map.getCanvas().style.cursor = "pointer";
// Clear close timeout and delete posible popup
this.clearPopupTESTCloseTimeout();
this.popupTEST.remove();
if (e && e.features && e.features.length > 0 && e.features[0].properties) {
const popupcontent = document.createElement("div");
// Create a content using the properties ...
// Is a react component, but you can use plain javascript
const Contents = () => (
<TestModalContent
properties={properties}
onMouseEnter={this.clearPopupOACCloseTimeout}
onMouseLeave={this.closePopupOACWithTimeout}
/>
);
ReactDOM.render(Contents(), popupcontent);
this.popupTEST.setLngLat([long, lat]).setMaxWidth("none").setDOMContent(popupcontent).addTo(_this.map);
}
});
this.map.on("mouseleave", "sit-TEST", () => {
this.map.getCanvas().style.cursor = "";
this.closePopupTESTWithTimeout();
});
closePopupTESTWithTimeout = () => {
this.popupTESTCloseTimeout = setTimeout(() => this.popupTEST.remove(), 500);
}
clearPopupTESTCloseTimeout = () => {
if (this.popupTESTCloseTimeout) {
clearTimeout(this.popupTESTCloseTimeout);
this.popupTESTCloseTimeout = null;
}
}
Upvotes: 2
Reputation: 95
Solution for now:
setting misc. variables on misc. events, then checking in a mousemove handler
const popupDom = document.getElementsByClassName("project-marker-popup");
const popup = new mapboxgl.Popup({
className: "project-marker-popup",
maxWidth: "300px",
});
let popUpIsOpen = false;
let mouseOnCircles = false;
// ...
window.addEventListener(
"mousemove",
debounce(250, (e) => {
const mouseOnPopup = e.target.closest(".project-marker-popup");
console.log("mouse on popup", mouseOnPopup);
console.log("popup is open", popUpIsOpen);
console.log("mouse on circles", mouseOnCircles);
if (mouseOnPopup || mouseOnCircles) {
console.log("doing nothing");
return;
} else {
if (popup.remove()) {
popUpIsOpen = false;
} else {
console.error("couldnt close popup");
}
}
})
);
// ...
Upvotes: 0