美文网首页
关于button你需要多点了解

关于button你需要多点了解

作者: 涅槃快乐是金 | 来源:发表于2024-05-29 15:19 被阅读0次

    你应该在HTML中使用a标签、button标签,还是其他标签(例如div)来创建可点击的元素呢?
    // 🚩
    export function MyButton() {
      return <div onClick={...}>Click me</div>
    }
    //❓
    export function MyButton() {
      return <button onClick={...}>Click me</button>
    }
    //❓
    export function MyButton() {
      return <a onClick={...}>Click me</a>
    

    答案比你想象的更复杂.

    div标签的问题

    首先明确一点——你不应该使用div作为可点击的元素(至少在99%的情况下)。为什么呢?

    简单来说,div != buttondiv只是一个通用的容器,缺乏一个真正的可点击元素应该具备的一些特性,例如:

    • div不可聚焦,例如你的Tab键不会像对其他按钮那样聚焦到div。
    • 屏幕阅读器和其他辅助设备不将div识别为可点击的元素。
    • div在聚焦时不会将某些按键输入(如空格键或回车键)转换为点击。

    你可以通过一些属性来解决这些问题,例如tabindex="0"role="button"

    //🚩 Trying to make a div *mostly* behave like a button...
    export function MyButton() {
      function onClick() { ... }
      return (
        <div
          className="my-button"
          tabindex="0" // 使div可聚焦
          role="button" // 提示屏幕阅读器这是可点击的
          onClick={onClick}
          onKeydown={(event) => {
            // 在聚焦时监听回车和空格键,并手动调用点击处理程序
            if (event.key === "Enter" || event.key === "Space") {
              onClick()
            }
          }}
        >
          Click me
        </div>
      )
    }
    

    哦,对了,我们还需要确保在聚焦状态下进行样式处理,以便用户知道该元素已被聚焦。同时,我们必须确保这满足所有的无障碍要求,例如:

    .my-button:focus-visible {
      outline: 1px solid blue;
    }
    

    这是一项艰巨的工作,我们需要努力找出所有细微但关键的按钮行为,并手动执行它们。

    但幸运的是,有一个更好的方法(大多数情况下)!

    按钮标签的救赎

    按钮标签的美妙之处在于,它就像你设备上的其他按钮一样工作,并且正是用户和无障碍工具所期望的。

    它是可聚焦的、可访问的、支持键盘输入的,有合规的聚焦状态样式,功能齐全!

    // ✅
    export function MyButton() {
      return (
        <button onClick={...}>
          Click me
        </button>
      )
    }
    

    但按钮也有一些问题需要注意。

    button标签拯救了我们

    对按钮最大的抱怨是样式处理。

    例如,如果你只是想给按钮一个漂亮的浅紫色背景:

    <button class="my-button">
      Click me
    </button>
    <style>
      /* 🤢 */
      .my-button { 
        background-color: purple; 
      }
    </style>
    

    你会得到这样一个丑陋的按钮:


    image.png

    看起来像是从Windows 95中出来的一样。

    是的,浏览器会对按钮元素强制应用各种奇怪的样式,而应用你自己的样式只会让情况更糟。

    这就是为什么我们都喜欢div。它们没有任何多余的样式或行为负担。它们总是按照预期的方式工作和显示。

    现在你可能会说,哦!appearance: none会重置外观!但实际上,它并不能完全达到你的预期。

    <button class="my-button">
      Click me
    </button>
    <style>
      .my-button { 
        appearance: none; /* 🤔 */
        background-color: purple; 
      }
    </style>
    

    我们的怪物仍然存在:


    image.png

    修正按钮样式

    没错,我们实际上必须逐行重置属性:

    /* ✅ */
    button {
      padding: 0;
      border: none;
      outline: none;
      font: inherit;
      color: inherit;
      background: none;
    }
    

    这最终会给我们一个看起来和行为都像div的按钮,但有一个额外的好处,它仍然使用浏览器的默认聚焦样式。

    另一个选择是使用all: unset来通过一个简单的属性恢复到没有特殊样式的状态:

    /* ✅ */
    button { all: unset; }
    button:focus-visible { outline: 1px solid var(--your-color); }
    

    别忘了添加你自己的聚焦状态;例如,使用与你品牌颜色对比度足够的轮廓——这样就可以了。

    修复按钮表单行为

    使用按钮标签时,还有一个问题需要注意。表单中的任何按钮默认会被视为提交按钮,点击时会提交表单。什么?

    function MyForm() {
      return (
        <form onSubmit={...}>
          ...
          <button type="submit">Submit</button>
          {/* 🚩 点击“Cancel”也会提交表单! */}
          <button onClick={...}>Cancel</button>
        </form>
      )
    }
    

    没错,按钮的默认type属性是submit。是的,这很奇怪,也很烦人。

    为了解决这个问题,除非你的按钮确实是用于提交表单的,否则请务必添加type="button",如下所示:

    export function MyButton() {
      return (
        <button 
          type="button" // ✅
          onClick={...}>
           Click me
        </button>
      )
    }
    

    现在,我们的按钮将不再尝试找到最近的表单父元素并提交它。呼,差点儿出问题。

    链接到其他页面

    这里有一个我们规则的重大例外。我们不想使用按钮来链接到其他页面:

    // 🚩
    function MyLink() {
      return (
        <button
          type="button"
          onClick={() => {
            location.href = "/"
          }}
        >
          Don't do this
        </button>
      )
    }
    

    使用点击事件链接到页面的按钮有几个问题:

    • 它们不可爬取,因此对SEO非常不利。
    • 用户无法在新标签或窗口中打开此链接,例如,不能通过cmd/ctrl点击,右键单击选择“在新标签中打开”。

    因此,不要使用按钮进行导航。这时候我们要用到可靠的a标签。

    // ✅
    function MyLink() {
      return (
        <a href="/">
          Do this for links
        </a>
      )
    }
    

    最棒的是,它们具有上述所有按钮的优点——可访问、可聚焦、支持键盘输入——而且没有一堆奇怪的样式!

    但是,在你说“等等,如果a标签有按钮的优点,没有奇怪的样式,我们是不是应该用它们来处理所有可点击的东西,省去一些麻烦?”之前……

    // 🚩
    function MyButton() {
      return (
        <a onClick={...}>
          Do this for links
        </a>
      )
    }
    

    实际上,不行。这是因为没有href的a标签不再像按钮一样工作。没错,只有当a标签有一个带值的href时,它才具备完整的按钮行为,如可聚焦。

    所以,让我们确保按钮就是按钮,锚链接就是链接。

    综合起来

    我喜欢的一个模式是将这些规则封装在一个组件中,这样你只需使用MyButton组件,如果你提供了一个URL,它就变成一个链接,否则就是一个按钮,如下所示:

    // ✅
    function MyButton(props) {
      if (props.href) {
        return <a className="my-button" {...props} />
      }
      return <button type="button" className="my-button" {...props} />
    }
    
    // 渲染一个 <a href="/">
    <MyButton href="/">Click me</MyButton>
    
    // 渲染一个 <button type="button">
    <MyButton onClick={...}>Click me</MyButton>
    

    这样,我们可以有一致的开发体验和用户体验,无论按钮的目的是点击处理程序还是链接到另一个页面。

    最后,让我们添加一些类型:

    type AnchorProps = React.AnchorHTMLAttributes<HTMLElement>
    type ButtonProps = React.ButtonHTMLAttributes<HTMLElement>
    type MyButtonProps = AnchorProps | ButtonProps
    
    function isAnchor(props: MyButtonProps): props is AnchorProps {
      return (props as AnchorProps).href !== undefined
    }
    
    export function MyButton(props: MyButtonProps) {
      if (isAnchor(props)) {
        return <a className="my-button" {...props} />
      }
      return <button type="button" className="my-button" {...props} />
    }
    

    简而言之:对于链接,使用带有href属性的锚标签;对于所有其他按钮,使用带有type="button"的按钮标签。

    相关文章

      网友评论

          本文标题:关于button你需要多点了解

          本文链接:https://www.haomeiwen.com/subject/enwkqjtx.html