Arbitrary function on react-leaflet marker click
Asked Answered
S

4

15

React-leaflet nicely provides the ability to put content within a Popup of a Marker.

For instance in my example:

            <Marker position={[item.lat, item.lng]} key={item.machineid}>
              <Popup maxWidth={720}>
                <ItemGrid machineid={item.machineid}
                          username={this.props.username}/>
              </Popup>
            </Marker>

However if this content is too big, it can be unwieldly, especially on mobile. I would like to have a (bootstrap) Modal interface activate on the click of the Marker. Is there way to do that in react-leaflet?

Somber answered 1/12, 2016 at 2:15 Comment(0)
S
49

Update: Evented behavior#

Use the eventHandlers listener function inside the Marker component:

<Marker
  position={[50.5, 30.5]}
  eventHandlers={{
    click: (e) => {
      console.log('marker clicked', e)
    },
  }}
/>
Spinning answered 24/11, 2020 at 12:42 Comment(1)
wish i could +5 this (and have found it yesterday when I was losing my mind over it)Telfore
D
3

As per the react-leaflet docs,

Leaflet exposes its own events, different from React. You can listen to them using React-Leaflet by adding a callback to a property prefixed by on. Ex: <Map onMoveend={this.handleMoveend}>...</Map>.

Check 🍃 Leaflet's documentation for the events associated to each component.

Therefore we can use Leaflet's native onClick DOM event.

With react-leaflet, it would look something like this:

import React, { Component } from "react"
import { Map as LeafletMap, TileLayer, Marker } from "react-leaflet"

class Map extends Component {
  handleClick = event => {
    const { lat, lng } = event.latlng
    console.log(`Clicked at ${lat}, ${lng}`)
  }

  render () {
    return (
      <LeafletMap center={[52.5134, 13.4225]} zoom={13}>
        <TileLayer attribution={attribution} url={url}>
          <Marker
            position={[52.5134, 13.4225]}
            onClick={this.handleClick} // <-- call handleClick()
          />
        </TileLayer>
      </LeafletMap>
    )
  }
}

export default Map
Duodenary answered 11/2, 2019 at 14:14 Comment(0)
I
2

For people who want pass an argument, you should use:

<Marker
  position={[50.5, 30.5]}
  data="FooBar"
  eventHandlers={{
    click: (e) => {
      console.log(e.target.options.data);  // will print 'FooBar' in console
    },
  }}
/>
Interlay answered 30/6, 2021 at 10:10 Comment(2)
Property 'data' does not exist on type 'IntrinsicAttributes & MarkerProps & RefAttributes<Marker<any>>Akers
Data does still work, it seems, but I believe this should be e.sourceTarget.options.data to pass the argument.Akers
S
1

I found a kind of hack to perform an arbitrary action on tapping on the Marker. (1)Keep the popup, but have it's contents do whatever you like (e.g. Open a modal by defaut) and (2) Hide the popup's container div with CSS.

In my case it looked like this: Map View, remains unchanged:

<Marker position={[item.lat, item.lng]} key={item.machineid}>
    <Popup maxWidth={720}>
      <ItemGrid machineid={item.machineid}
                         username={this.props.username}/>
    </Popup>
</Marker>

Then ItemGrid which was previously in a pop changes to include a modal. (Here we are using reactstrap components and set the modal to true whnn the component mounts.) :

class ItemGrid extends Component {
  constructor(props){
    super(props);
     this.state = {modal:false}
    this.toggle = this.toggle.bind(this);
  }

  toggle() {
    this.setState({
      modal: !this.state.modal
    });
  }

  componentDidMount() {
    this.setState({modal:true})
  }

  render()  {
    return (
      <div>
      <Modal isOpen={this.state.modal} toggle={this.toggle} className={this.props.className}>
        <ModalHeader toggle={this.toggle}>Modal Header</ModalHeader>
        <ModalBody>
          {ContentThatWasPreviouslyInPopup}
        </ModalBody>
      </Modal>
    </div>

And Finally in leaflet CSS:

.leaflet-container a.leaflet-popup-close-button {
    position: absolute;
    top: 0;
    right: 0;
    padding: 8px 8px 0 0;
    text-align: center;
    width: 0px;
    height: 0px;
    font: 0px/0px Tahoma, Verdana, sans-serif; //DANGEROUS HACK
    color: #c3c3c3;
    text-decoration: none;
    font-weight: bold;
    background: transparent;
    }

.leaflet-popup-content-wrapper {
    padding: 1px;
    text-align: left;
    border-radius: 12px;
    width: 0px // DANGEROUS HACK
    }
Somber answered 5/12, 2016 at 18:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.