0

I have here some interesting case! I'm using a react-input-range inside a react-leaflet Marker Popup

Which is something like this:

import React from 'react'; import { Map as LeafletMap, Marker, Popup, TileLayer } from 'react-leaflet'; import InputRange from 'react-input-range'; export default class MapPage extends React.Component { constructor(props) { super(props); this.state = { value: { min: 2, max: 200 }, name: 'sample name', }; } render() { return ( <div> {/* * This input range outside LeafletMap works */} <InputRange maxValue={300} minValue={0} value={this.state.value} onChange={value => this.setState({ value })} /> <LeafletMap center={[0, 0]} zoom={16}> <TileLayer attribution="&copy; <a href=&quot;http://osm.org/copyright&quot;>OpenStreetMap</a> contributors" url="http://{s}.tile.osm.org/{z}/{x}/{y}.png" /> <Marker position={[0, 0]}> <Popup> {/* * This input below works */} <input value={this.state.name} onChange={({ target }) => this.setState({ name: target.value })} /> {/* * This input range INSIDE LeafletMap does not work */} <InputRange maxValue={300} minValue={0} value={this.state.value} onChange={value => this.setState({ value })} /> </Popup> </Marker> </LeafletMap> </div> ); } } 

What I know so far

  • The input range component works outside the LeafletMap component.
  • Native input works inside the LeafletMap component!
  • I've confirmed that a native input works inside the popup! The input range works on mobile(android chrome browser)! (swipe & click events)

Any ideas how to make input range work inside react-leaflet map?

Here's a few more details that might help:

  • react-leaflet: ^2.0.0-rc.3
  • react: 16.4.2
  • react-input-range: ^1.3.0

Testing Env

Browsers: Chrome 64.0.32, Firefox 61 and Safari 11.1

OS: Mac OSX High Sierra 10.13.5

Codepen: https://codepen.io/codepen-theo/pen/OwdLXY?editors=0010

7
  • Are there any console errors? Also, have you tried debugging the props with this tool at all? chrome.google.com/webstore/detail/react-developer-tools/… Commented Aug 10, 2018 at 14:06
  • no errors mate, and events weren't triggered at all. I am using github.com/react-boilerplate/react-boilerplate w/ github.com/evgenyrodionov/redux-logger since the react-dev tools did not play well with the boilerplate @dkniffin Commented Aug 10, 2018 at 14:09
  • Does it work with multiple InputRange at all? This library seems to have do some very confusing stuff to attach and destroy event listeners on the document node. I suspect that instances of the components might be messing with the global event listeners from the other InputRange components. Commented Aug 10, 2018 at 14:10
  • Hi @HåkenLid, i tried adding 2 input ranges at the same time(outside map), both worked. does this clear some of your assumptions? Commented Aug 10, 2018 at 14:22
  • Yes. But I suspect that the problem might be related to how react-input-range handles events. It needs to attach event listeners to the document node, but with leaflet, there's some complicated stuff going on using react portals and iframes. So my guess is that some assumptions made by input range are not true in this case. Can you link to a running example of your code in a codepen or something like that? The specific combination of these two libraries is probabaly not so common. Commented Aug 10, 2018 at 14:32

1 Answer 1

2
+100

I have figured out what's happening. But I'm not sure there's a good way to get it to work as you want.

What happens is that leaflet.js will call event.stopPropagation() on the mousedown event when it bubbles to the popup element. This blocks reacts synthetic events from working as expected. React's event propagation is handled independently of the native bubbling / capturing.

https://github.com/Leaflet/Leaflet/blob/master/src/layer/Popup.js#L185

You can monkey patch the the event listener on the popup wrapper. See this example:

https://codepen.io/haakenlid/pen/ejxNKE

This works by simply removing one of the preventDefault event listener that leaflet.js has attached to the plugin wrapper node. (In a very hacky way)

class MyPopup extends React.Component { componentDidMount(){ if (!this.popup) return const el = this.popup.leafletElement el._initLayout() const wrapper = el._wrapper wrapper.removeEventListener('mousedown', wrapper._leaflet_events['mousedown6']) console.log('mount') } render(){ return <Popup ref={ el=>this.popup = el } {...this.props} /> } } 

This is just a proof of concept. There's a high likelihood that it will break again with future versions of leaflet.js (or in some other way).

Sign up to request clarification or add additional context in comments.

4 Comments

Hey, i'm glad you made it work! i'm confused though, isn't it that the bubble will start from child(input-range-element) to ancestor not the other way around? javascript.info/bubbling-and-capturing also, mousedown & onchange events seems to work with other sibling (native)elements codepen.io/codepen-theo/pen/pZGJbg?editors=0010 so i'm a little bit confused why event.stopPropagation() on popup wrapper blocks only input-range events. i see it this way -> plnkr.co/edit/ZtjJbVIm4r4iY0IHzWf8?p=preview Please correct me if i am wrong
React uses its own synthetic events framework and its own bubbling. github.com/reactjs/reactjs.org/pull/717/files
Leaflet only stops propagation on mousedown, touchstart and dblclick. Click events are handled differently. github.com/Leaflet/Leaflet/blob/master/src/dom/DomEvent.js#L185
alright this makes more sense now. it's quite hassle on my side but i'll consider your early suggestion to try other range pickers since this is making some side effects on the map. great work mate. thanks for all the inputs!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.