флажок onChange в внутри отключен не запускается в Firefox с React

#javascript #html #reactjs #firefox #dom

#javascript #HTML #reactjs #firefox #dom

Вопрос:

Следующее работает в Firefox 80.0 и Chromium 84.0.4147.89.

 const fieldset = document.getElementById("fieldset");
const toggle = document.getElementById("toggle");

toggle.addEventListener("change", () => {
  if (fieldset.hasAttribute("disabled")) {
    fieldset.removeAttribute("disabled");
  } else {
    fieldset.setAttribute("disabled", true);
  }
});  
 <form action="#">
  <fieldset id="fieldset">
    <legend>
      <label>toggle <input id="toggle" type="checkbox" /></label>
    </legend>
    <input />
  </fieldset>
</form>  

Однако, когда я пытаюсь сделать что-то подобное в React, это не работает в Firefox. Событие onChange, по-видимому, не срабатывает, как только набор полей отключен.

 function App() {
  const [disabled, setDisabled] = React.useState(false);
  const toggleDisabled = React.useCallback(() => {
    setDisabled((disabled) => !disabled);
  }, []);

  return (
    <form action="#">
      <fieldset disabled={disabled}>
        <legend>
          <label>
            toggle <input onChange={toggleDisabled} type="checkbox" />
          </label>
        </legend>
        <input />
      </fieldset>
    </form>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));  
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>

<div id="root"></div>  

В статье MDN fieldset говорится:

[когда элемент fieldset отключен] Обратите внимание, что элементы формы внутри <legend> элемента не будут отключены.

W3C (пример B) и WHATWG также упоминают, что содержимое <legend> не должно быть отключено.

Поэтому я считаю, что две части кода должны вести себя одинаково: я должен иметь возможность переключать disabled атрибут с помощью флажка.

Как я могу добиться такого же эффекта в React на Firefox 80.0 ?

Ответ №1:

Такое поведение, похоже, ожидается браузером в соответствии с 2 строками ранее в этом абзаце.

disabled Если этот логический атрибут установлен, все элементы управления формой, которые являются потомками <fieldset> , отключены, что означает, что они недоступны для редактирования и не будут отправлены вместе с . Они не будут получать никаких событий просмотра, таких как щелчки мыши или события, связанные с фокусом. По умолчанию браузеры отображают такие элементы управления серым цветом. Обратите внимание, что элементы формы внутри элемента не будут отключены. — MDN

Кажется, что Firefox делает именно то, что он описывает, что он должен делать.
Решением здесь было бы просто поместить <input> вне набора полей, чтобы на него не повлияло disabled свойство.


Редактировать

Ваш комментарий о прослушивании событий выше в DOM заставил меня задуматься. Как насчет того, чтобы обойти это, связав свой собственный прослушиватель событий с good ol addEventListener , в сочетании с useRef и useEffect крючками. Создайте ссылку на флажок и прослушайте событие изменения после первого рендеринга. Теперь событие прослушивает сам ввод.
Этот «обходной путь», похоже, работает в FF.

 function App() {
  const [disabled, setDisabled] = React.useState(false);
  const toggleElement = React.useRef(null)
  const toggleDisabled = React.useCallback(() => {
    setDisabled(disabled => !disabled);
  }, []);
  
  React.useEffect(() => {
    toggleElement.current.addEventListener('change', toggleDisabled);
  }, []);

  return (
    <form action="#">
      <fieldset disabled={disabled}>
        <legend>
          <label>
          toggle <input ref={toggleElement} type="checkbox" />
      </label>
        </legend>
        <input />
      </fieldset>
    </form>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));  
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>

<div id="root"></div>  

Комментарии:

1. Спасибо за ваш ответ, но я не согласен с вашей интерпретацией статьи MDN. Я добавил еще пару ссылок на мой вопрос (W3C и WHATWG), обе из которых показывают, что спецификация определяет, что содержимое <legend> не отключено.

2. @TomFenech любопытно. Я просмотрел ваши примеры. Второй пример W3C действительно работает в Firefox, но реализация с React.js сбой. Может ли быть какая-то добавленная логика из React.js что касается событий в формах?

3. Я думаю, что проблема в том, что React подключает прослушиватели событий выше в DOM, а Firefox предотвращает всплывание событий из отключенного набора полей. У меня такое ощущение, что «ответом» на мой вопрос будет то, что «вы не можете сделать это в Firefox», к сожалению.

4. @TomFenech отличная командная работа! Ваш комментарий заставил меня кое о чем задуматься. Проверьте отредактированную версию в FF.

5. Да, это работает! Я собираюсь немного оставить этот вопрос открытым, чтобы посмотреть, есть ли у кого-нибудь другие идеи, но это достойный обходной путь, спасибо.