Реакция и размытие

У меня есть простая проблема с React и обработкой событий. Мой компонент выглядит так (в основном таблица):

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

Я хочу, чтобы событие blur срабатывало , только если фокус выходит за пределы таблицы. Вместо этого событие срабатывает для каждого дочернего элемента таблицы, когда он теряет фокус.

В соответствии с документами React разрешает событиям фокуса.

Возникает вопрос: как я могу получить метод метода onBlur только тогда, когда фокус выходит из таблицы? IOW: как я могу отфильтровать и отбросить нежелательные события, пузырьки, чтобы я показывал только события, которые указывают на потеря фокуса для таблицы?

Ответы

Ответ 1

Проблема заключается в том, что на самом деле у таблицы нет концепции фокуса, поскольку она не является самим входом.

Когда onBlur загорается на вложенных входах, мы проверим relatedTarget события onBlur, которое должно быть установлено на элемент с RECEIVED focus (или null). Затем мы используем функцию, которая будет проходить через parentNode из этого вновь сфокусированного элемента и гарантирует, что наше событие currentTarget (таблица) не является предком нового сфокусированного элемента. Если условие проходит, предполагается, что в таблице больше нет фокуса.

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" />

Ответ 2

Без настраиваемых функций и совместимого с Internet Explorer, поскольку node.contains и document.activeElement поддерживаются с Internet Explorer 5, это работает:

const onBlur = (e) => { if ( !e.currentTarget.contains( document.activeElement ) ) { console.log('table blurred'); } }

  • Метод Node.contains() возвращает логическое значение, указывающее, является ли node потомком данного node или нет.
  • Document.activeElement возвращает элемент, сфокусированный в данный момент, то есть элемент, который будет получать события нажатия клавиши, если пользователь набирает какие-либо символы.