264

React is able to render custom attributes as described at http://facebook.github.io/react/docs/jsx-gotchas.html:

If you want to use a custom attribute, you should prefix it with data-.

<div data-custom-attribute="foo" />

And that's great news except I can't find a way to access it from the event object e.g.:

render: function() {
...
<a data-tag={i} style={showStyle} onClick={this.removeTag}></a>
...
removeTag: function(event) {
    this.setState({inputVal: event.target????}); 
},

The element and data- property render in html fine. Standard properties like style can be accessed as event.target.style fine. Instead of event.target I tried:

 event.target.props.data.tag
 event.target.props.data["tag"]
 event.target.props["data-tag"]  
 event.target.data.tag
 event.target.data["tag"]
 event.target["data-tag"]

none of these worked.

Anastasia
  • 712
  • 1
  • 5
  • 11
andriy_sof
  • 2,673
  • 2
  • 13
  • 8
  • 1
    May be one comment help someone, i found out React 16.7 ***doesnt rerenders*** and update the component's custom html attributes if you changed only them in a store (f.e. redux) and tied to component. This means the component has f.e.```aria-modal=true```, you push the changes (to false) to the store of **aria/data** attributes, but nothing else is changed (such as component's content or class or variables in there) as the result ReactJs will not update **aria/data** attrs in that components. I've been messing around about whole day to realise that. – Alexey Nikonov Feb 13 '19 at 09:48

17 Answers17

382

event.target gives you the native DOM node, then you need to use the regular DOM APIs to access attributes. Here are docs on how to do that:Using data attributes.

You can do either event.target.dataset.tag or event.target.getAttribute('data-tag'); either one works.

Edgar
  • 6,022
  • 8
  • 33
  • 66
Sophie Alpert
  • 139,698
  • 36
  • 220
  • 238
  • 35
    react 0.13.3, IE10 `event.target.dataset` is undefined but `event.target.getAttribute('data-tag')` works. the other browsers are fine. Thanks – Andrey Borisko Oct 22 '15 at 17:41
  • 1
    Is there anything wrong with this approach? For example depending on what button the user pushes I want to pass a string to a function. I was hoping to avoid making three functions in my component for each case. – Michael J. Calkins Apr 10 '16 at 23:52
  • 7
    This answer is better than the accepted one in terms of performance. Doing it this way means that we do *not* create a new function on every render. Not only does it skip creating a new function each render, but since the function reference will be the same each time, pure (or memoized) components won't see it as a different function. Therefore, it won't unnecessarily re-render the entire component every time. Even a custom implementation of `shouldComponentUpdate` would have the same issue unless you just completely ignored the function prop. – Michael Yaworski May 31 '19 at 16:07
  • 7
    I use typescript. In my case I had to use `event.currentTarget.getAttibute('data-tag')` – karianpour May 04 '20 at 11:23
  • 5
    This answer is incorrect and the result is unreliable. Please use `e.currentTarget.getAttribute()` – Nishant Ghodke Mar 12 '21 at 15:16
  • @AndreyBorisko If `event.target.dataset` is undefined it is probably because the `tag` does not follow a format like `data-tag` – Ricardo Jul 12 '21 at 22:50
  • 1
    @NishantGhodke it's not incorrect, just not complete. event.target is the DOM element clicked on, not the element that has the event handler attached to it. In many cases target will be the same as currentTarget, but it's far more likely that the user actually wants currentTarget. event.currentTarget.dataset.tag will return the same data as event.currentTarget.getAttribute('data-tag') – Phil Jul 19 '21 at 00:29
183

To help you get the desired outcome in perhaps a different way than you asked:

render: function() {
    ...
    <a data-tag={i} style={showStyle} onClick={this.removeTag.bind(null, i)}></a>
    ...
},
removeTag: function(i) {
    // do whatever
},

Notice the bind(). Because this is all javascript, you can do handy things like that. We no longer need to attach data to DOM nodes in order to keep track of them.

IMO this is much cleaner than relying on DOM events.

Update April 2017: These days I would write onClick={() => this.removeTag(i)} instead of .bind

Jared Forsyth
  • 12,808
  • 7
  • 45
  • 54
  • 18
    but you no longer get the event object passed. – chovy Jan 07 '15 at 10:18
  • 9
    @chovy Please correct me if I'm wrong but aren't all javascript functions inherently variadic? I haven't tested this, but I would assume the "event object" is still being passed. It's just NOT at the same parameter index it previously was. Binding in the fashion as outlined above is like `unshift`ing the function [arguments](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments) array. If the "event object" was at index `0` it would now be at index `1`. – Ryder Brooks Mar 11 '15 at 01:02
  • 8
    @chovy You can if you need it. Just do `removeTag: function(i, evt) {` – Jared Forsyth Mar 11 '15 at 05:30
  • Data attributes didn't even work for me in a pager - it seemed to be stuck with the initial settings. I mean, the `data-page` in the DOM has changed, but when clicking the link and getting the attribute from the event, it was still on the previous one? So thanks, @JaredForsyth it worked perfectly! :) – Barnabas Kecskes Sep 29 '15 at 13:02
  • "We no longer need to attach data to DOM nodes in order to keep track of them." I like it. I like it a lot. – andyengle Feb 02 '16 at 16:51
  • 10
    It's cleaner, but all those binds do have a performance hit if you're doing a lot of them. – El Yobo Feb 26 '16 at 05:50
  • I'd be preetty surprised if a .bind impacted your performance in any way – Jared Forsyth Feb 26 '16 at 07:34
  • 6
    https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md – Jordan Apr 21 '16 at 15:11
  • yes. things do have perf impact. but 95% of the time, this would be premature optimization. If you're calling render often enough for that to be an issue, you're probably going to run into something else being a perf bottleneck first. I mean, if we're speaking of allocations, you're allocating several objects *for each element* in your render function. js is *super* good at handling short-lived objects. – Jared Forsyth Apr 21 '16 at 16:15
  • 4
    @JaredForsyth It's not that .bind itself impacts your performance, it's that allocating a new function every render breaks shallow equality. As a result if you make use of any `shouldComponentUpdate` with [shallowCompare](https://facebook.github.io/react/docs/shallow-compare.html) you're guaranteed for it to return false every render. I think React may make use of shallow equality as well to do optimizations. – Yonatan Kogan Dec 23 '16 at 23:20
  • 2
    bind() returns each time new function and DOM will be updated. It's not a react way. – Alex Shwarc Apr 11 '17 at 18:22
  • "not the react way"? :P please enlighten us on an alternative. As it happens, the DOM won't be updated b/c react has a synthetic event system. – Jared Forsyth Apr 14 '17 at 15:59
  • 1
    Great answer. A note on the Update April 2017, from the [documentation](https://facebook.github.io/react/docs/handling-events.html): "_The problem with this syntax is that a different callback is created each time the LoggingButton renders. In most cases, this is fine. However, if this callback is passed as a prop to lower components, those components might do an extra re-rendering. We generally recommend binding in the constructor or using the property initializer syntax, to avoid this sort of performance problem._" – Luca Fagioli May 25 '17 at 03:39
  • Yup! For this example though, a custom argument must be passed for each element, so neither of those options apply. If you determine that there's a performance bottleneck, you can opt for more elaborate methods like pre-binding a whole array of callbacks, but usually that'll be premature optimization – Jared Forsyth May 31 '17 at 20:49
  • 1
    This solution is against React nature. ()=> returns new functions each time and eliminates DOM reconciliation. – Alex Shwarc Aug 07 '17 at 16:04
  • Event handlers are actually handled differently from other attributes -- they aren't reconciled with the DOM, but are rather handled through a synthetic event system. Because of this, allocating a new function doesn't have a negative impact on DOM reconciliation. – Jared Forsyth Aug 15 '17 at 16:55
  • I learnt a something beautiful from your April '17 Update: It's no longer necessary to write lengthy, repetitive attribute lists into the markup elements. We can simply leave away the data-attributes and just use an arrow function. So clean, so readable, and keeping the DOM slim as well. Nice! – Wu Wei May 29 '19 at 18:47
57

Here's the best way I found:

var attribute = event.target.attributes.getNamedItem('data-tag').value;

Those attributes are stored in a "NamedNodeMap", which you can access easily with the getNamedItem method.

fguillen
  • 36,125
  • 23
  • 149
  • 210
roctimo
  • 894
  • 7
  • 9
25

Or you can use a closure :

render: function() {
...
<a data-tag={i} style={showStyle} onClick={this.removeTag(i)}></a>
...
},
removeTag: function (i) {
    return function (e) {
    // and you get both `i` and the event `e`
    }.bind(this) //important to bind function 
}
Tudor Campean
  • 351
  • 3
  • 3
25
// Method inside the component
userClick(event){
 let tag = event.currentTarget.dataset.tag;
 console.log(tag); // should return Tagvalue
}
// when render element
<a data-tag="TagValue" onClick={this.userClick}>Click me</a>
Mentori
  • 426
  • 4
  • 10
11
<div className='btn' onClick={(e) =>
     console.log(e.currentTarget.attributes['tag'].value)}
     tag='bold'>
    <i className='fa fa-bold' />
</div>

so e.currentTarget.attributes['tag'].value works for me

Rohan Kumar
  • 40,431
  • 11
  • 76
  • 106
Manindra Gautam
  • 387
  • 3
  • 8
9

As of React v16.1.1 (2017), here is the official solution: https://reactjs.org/docs/handling-events.html#passing-arguments-to-event-handlers

TLDR: OP should do:

render: function() {
...
<a style={showStyle} onClick={(e) => this.removeTag(i, e)}></a>
...
removeTag: function(i, event) {
    this.setState({inputVal: i}); 
}
tytk
  • 2,082
  • 3
  • 27
  • 39
  • 1
    Where does the 'i' come from? – Freshchris Feb 28 '19 at 12:38
  • 2
    `i` is the custom attribute that OP wanted to pass in somehow. It's some variable that's in scope when the `a` element is defined. – tytk Mar 05 '19 at 19:43
  • 1
    That's not what OP wants. They want to access an attribute value on the element (a) with the onClick handler. – Design by Adrian Jun 21 '19 at 13:27
  • 1
    My point was that the official solution is to define the event-handler function with the variable in scope, rather than setting a data attribute on the element. This doesn't stop you from accessing attributes of elements if you really want, but that's not the idiomatic way to access a variable in React. – tytk Jun 22 '19 at 19:39
9

This single line of code solved the problem for me:

event.currentTarget.getAttribute('data-tag')
Emeka Augustine
  • 891
  • 1
  • 12
  • 17
7

You can access data attributes something like this

event.target.dataset.tag
Rameez Iqbal
  • 507
  • 1
  • 5
  • 24
4

If anyone is trying to use event.target in React and finding a null value, it is because a SyntheticEvent has replaced the event.target. The SyntheticEvent now holds 'currentTarget', such as in event.currentTarget.getAttribute('data-username').

https://facebook.github.io/react/docs/events.html

It looks like React does this so that it works across more browsers. You can access the old properties through a nativeEvent attribute.

Eduardo
  • 41
  • 1
  • 1
4

You can simply use event.target.dataset object . This will give you the object with all data attributes.

Vikash Kumar
  • 399
  • 3
  • 4
3

I do not know about React, but in the general case you can pass custom attributes like this:

1) define inside an html-tag a new attribute with data- prefix

data-mydatafield = "asdasdasdaad"

2) get from javascript with

e.target.attributes.getNamedItem("data-mydatafield").value 
Dimitrios Ververidis
  • 1,118
  • 1
  • 9
  • 33
3

Try instead of assigning dom properties (which is slow) just pass your value as a parameter to function that actually create your handler:

render: function() {
...
<a style={showStyle} onClick={this.removeTag(i)}></a>
...
removeTag = (customAttribute) => (event) => {
    this.setState({inputVal: customAttribute});
}
msangel
  • 9,895
  • 3
  • 50
  • 69
1

This worked for me... My attribute is named "attr" in the example.

e.target.selectedOptions[0].attributes.attr.value
Milan
  • 25
  • 6
1

If you have multiple icons with different data-label (age,name,email):

       <button
          data-label="name" 
          onMouseOver={handleValue}
          className="icon"
        >
          <FaUser />
        </button>

when the mouse is over an icon, you change the title by accessing data-label

const handleValue = (e) => {
    // making sure mouse is over an icon
    if (e.target.classList.contains("icon")) {
      const newValue = e.target.dataset.label;
      setTitle(newValue);
      setValue(person[newValue]);
    }
  };
Yilmaz
  • 35,338
  • 10
  • 157
  • 202
0

In React you don't need the html data, use a function return a other function; like this it's very simple send custom params and you can acces the custom data and the event.

render: function() {
...
<a style={showStyle} onClick={this.removeTag(i)}></a>
...
removeTag: (i) => (event) => {
    this.setState({inputVal: i}); 
},
miguel savignano
  • 1,109
  • 13
  • 9
0

I think it's recommended to bind all methods where you need to use this.setState method which is defined in the React.Component class, inside the constructor, in your case you constructor should be like

    constructor() {
        super()
        //This binding removeTag is necessary to make `this` work in the callback
        this.removeTag = this.removeTag.bind(this)
    }
    removeTag(event){
        console.log(event.target)
        //use Object destructuring to fetch all element values''
        const {style, dataset} = event.target
        console.log(style)
        console.log(dataset.tag)
    }
   render() {
   ...
      <a data-tag={i} style={showStyle} onClick={this.removeTag.bind(null, i)}></a>
   ...},

For more reference on Object destructuring https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring

Amitesh Bharti
  • 14,264
  • 6
  • 62
  • 62