【问题标题】:checkbox onChange in <legend> inside disabled <fieldset> not firing in Firefox with React禁用 <fieldset> 中 <legend> 中的复选框 onChange 未在 Firefox 中使用 React 触发
【发布时间】:2020-09-05 01:29:45
【问题描述】:

以下适用于 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 article 说:

[当 fieldset 元素被禁用时] 注意&lt;legend&gt; 元素内的表单元素不会被禁用。

W3C (Example B)WHATWG 还提到不应禁用&lt;legend&gt; 的内容。

所以我相信这两段代码的行为方式应该相同:我应该能够使用复选框切换disabled 属性。

如何在 React on Firefox 80.0+ 上实现同样的效果?

【问题讨论】:

    标签: javascript html reactjs firefox dom


    【解决方案1】:

    根据该段落前面的 2 行,浏览器似乎期望这种行为。

    disabled 如果设置了此布尔属性,则所有属于&lt;fieldset&gt; 后代的表单控件都将被禁用,这意味着它们不可编辑并且不会与 . 它们不会收到任何浏览事件,例如鼠标点击或焦点相关事件。默认情况下,浏览器将此类控件显示为灰色。请注意,元素内的表单元素不会被禁用。 - MDN

    Firefox 似乎完全按照它描述的那样做。
    这里的解决方案是将&lt;input&gt; 放在字段集之外,这样它就不会受到disabled 属性的影响。


    编辑

    您关于在 DOM 中侦听更高层事件的评论让我开始思考。如果您通过将自己的事件侦听器与 addEventListener 结合使用 useRefuseEffect 挂钩来绕过它,怎么样。创建对复选框的引用并在第一次渲染后侦听更改事件。现在事件正在监听输入本身。
    这种“解决方法”似乎确实适用于 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>

    【讨论】:

    • 感谢您的回答,但我不同意您对 MDN 文章的解释。我为我的问题添加了更多链接(W3C 和 WHATWG),这两个链接都表明规范定义了 &lt;legend&gt; 的内容未被禁用。
    • @TomFenech 很好奇。我看过你的例子。 W3C 第二个示例在 Firefox 中运行,但使用 React.js 的实现失败。 React.js 是否可以为表单中的事件添加一些逻辑?
    • 我认为问题在于 React 在 DOM 中将事件侦听器附加到更高的位置,而 Firefox 会阻止禁用字段集中的事件冒泡。我有一种感觉,我的问题的“答案”将是“你不能在 Firefox 中做到这一点”,很遗憾。
    • @TomFenech 很棒的团队合作!你的评论让我想到了一些事情。查看 FF 中的编辑版本。
    • 是的,这行得通!我会暂时保留这个问题,看看是否有人有其他想法,但这是一个不错的解决方法,谢谢。
    猜你喜欢
    • 2019-06-06
    • 1970-01-01
    • 2015-08-09
    • 1970-01-01
    • 2017-10-18
    • 2022-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多