271

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.

1
  • 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. Commented Feb 13, 2019 at 9:48

17 Answers 17

391

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.

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

7 Comments

react 0.13.3, IE10 event.target.dataset is undefined but event.target.getAttribute('data-tag') works. the other browsers are fine. Thanks
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.
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.
I use typescript. In my case I had to use event.currentTarget.getAttibute('data-tag')
This answer is incorrect and the result is unreliable. Please use e.currentTarget.getAttribute()
|
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

17 Comments

but you no longer get the event object passed.
@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 unshifting the function arguments array. If the "event object" was at index 0 it would now be at index 1.
@chovy You can if you need it. Just do removeTag: function(i, evt) {
It's cleaner, but all those binds do have a performance hit if you're doing a lot of them.
|
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.

2 Comments

You probably want to add a .value to get the actual value
I have edited the answer to add the .value at the end as @Wikunia has suggested
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 
}

2 Comments

Thanks, Tudor. Tried your solution and it works even w/o binding. I used it to toggle styles on e.target.
how do you know an inner function will contain the event args ?
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>

1 Comment

add some description to your code to make other understand the code
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

Comments

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}); 
}

4 Comments

Where does the 'i' come from?
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.
That's not what OP wants. They want to access an attribute value on the element (a) with the onClick handler.
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.
9

This single line of code solved the problem for me:

event.currentTarget.getAttribute('data-tag')

Comments

7

You can access data attributes something like this

event.target.dataset.tag

Comments

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.

Comments

4

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

Comments

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 

1 Comment

Mine keep saying null
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});
}

1 Comment

looks like a great comment! how can i see that it much more slower assigning dom properties
1

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

e.target.selectedOptions[0].attributes.attr.value

Comments

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]);
    }
  };

1 Comment

and here is how you can add same for components inputProps={{ 'data-exclusive': 1 }}
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}); 
},

Comments

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.