I have a simple issue with React and event handling. My component looks like this (basically a table):
const MyList = ({ items, onBlur }) => <table onBlur={onBlur}}> <thead> <tr> <th>ID</th> <th>Title</th> <th>Publisher</th> <th>Year</th> <th>Author</th> <th>System</th> <th/> </tr> </thead> <tbody> {items.map(item => <MyListRow key={item.Id} item={item}/>)} </tbody> </table>;
I want the blur
event to fire only if the focus goes out of the table. Instead the event fires on each child element of the table when it loses focus.
According to the docs React lets focus events bubble up.
The question is: How can I get my onBlur
method fire only when the focus gets out of the table? IOW: How can I filter out and discard the unwanted events bubbling up so that I reveal only the events which indicate a lost of focus for the table?
Reactjs provides an onBlur event bound to Input elements on the user form. onBlur is a javascript blur event, can attach a callback handler to this, called when input lost focus. onBlur is bounded to an input element with an event handler, Event handler is a function in a component, executed when input focus is lost.
The onBlur event handler is called when focus has left the element (or left some element inside of it). For example, it's called when the user clicks outside of a focused text input. function Example() { return ( <input onBlur={(e) => { console.
The blur event fires when an element has lost focus. The main difference between this event and focusout is that focusout bubbles while blur does not. The opposite of blur is focus . This event is not cancelable and does not bubble.
blur( [eventData ], handler ) An object containing data that will be passed to the event handler. A function to execute each time the event is triggered.
The problem is that a table doesn't actually have a concept of focus since it's not an input itself.
When the onBlur fires on the contained inputs we will check the relatedTarget
of the onBlur
event which should be set to the element that has RECEIVED focus (or null
). We then use a function that will traverse upwards through parentNode
s from that newly focused element and ensure that our event's currentTarget
(the table) is not an ancestor of the newly focused element. If the condition passes it is assumed that the table no longer has any focus.
const focusInCurrentTarget = ({ relatedTarget, currentTarget }) => { if (relatedTarget === null) return false; var node = relatedTarget.parentNode; while (node !== null) { if (node === currentTarget) return true; node = node.parentNode; } return false; } const onBlur = (e) => { if (!focusInCurrentTarget(e)) { console.log('table blurred'); } } const MyList = ({ items, onBlur }) => ( <table onBlur={onBlur}> <thead> <tr> <th>ID</th> <th>Title</th> <th>Publisher</th> <th>Year</th> <th>Author</th> <th>System</th> <th/> </tr> </thead> <tbody> <tr> <td>1</td> <td> <input type="text" /> </td> <td> <input type="text" /> </td> <td> <input type="text" /> </td> <td> <input type="text" /> </td> <td> <input type="text" /> </td> </tr> </tbody> </table> ); ReactDOM.render( <MyList onBlur={onBlur} />, document.getElementById('root') );
table { padding: 10px; border: 1px solid red; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> <div id="root"></div> <br /> <input type="text" />
References:
UPDATED:
Removed use of ReactDOM.findDOMNode
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With