我有一支技术全面、经验丰富的小型团队,专注高效交付中等规模外包项目,有需要外包项目的可以联系我
你有没有过这种窘迫瞬间:
用了一手机好多年,有天无聊乱点设置,突然发现—— 原来它早就有你梦寐以求的功能,只是一直藏在角落,你从来没点进去看。
上周我就经历了同样的尴尬,只不过主角不是手机,是——HTML 的属性。
更具体一点: 我以为 title 这个属性,就是“鼠标放上去弹个小提示”这么简单。 结果一查规范,才发现:
这货干的事,比我想象的复杂多了。
我写 HTML 超过六年,自以为基础扎实。 直到那天我才意识到: 自己这几年一直在用 同样的几个属性、同样的几种写法, 完全错过了它们“隐藏的第二职业”。
今天就带你翻一翻这些“老熟人”, 看看它们背后那些你从来没正眼瞧过的能力。
那天,我差点被一个手风琴菜单气到重构整站
场景是这样的: 我要给一个网站写 FAQ 区域,用一组 <details> 做折叠。
客户的要求很简单:
“我只想让用户一次只能展开一个问题, 也就是说,点击新的,旧的要自动收起来。”
我心想:小菜一碟。 惯性思路:“写点 JavaScript 管状态就行。”
然后就是熟悉的一套流程:
- 绑事件监听
- 管当前展开项
- 处理重复点击
- 测试一些边界情况
- 调试到开始怀疑人生
正当我在 VSCode 前面念念有词的时候,我刷到了这一段写法:
<details name="faqs">
<summary>Question 1</summary>
Answer goes here.
</details>
<details name="faqs">
<summary>Question 2</summary>
Another answer.
</details>
等等——name 属性?出现在 <details> 上?
我半信半疑地试了一下,结果浏览器非常淡定地告诉我:
“是的亲,你给我加上同一个
name, 我就会自动帮你做到 ‘同组里一次只能展开一个’。”
一个属性。 零行 JavaScript。 需求完美满足。
那一刻我有点恍惚:
原来这些年,很多我自以为“必须靠逻辑来扛”的东西, 其实 HTML 自己早就会。
那些你以为“用熟了”的属性,其实一直在隐藏功能
下面这些属性,你肯定都见过:
name、title、value、multiple、disabled、target ……
但它们在不同标签里, 居然一个个都开启了“隐藏职业技能”。
1. name 不只是电台 DJ 在用的“分组按钮”
我们早就习惯了在 radio 上这么写:
<input type="radio" name="gender">
<input type="radio" name="gender">
同一个 name,就是一组互斥选项。
但你知道吗?在 <details> 上加 name,也能分组。
写成这样:
<details name="faqs">
<summary>Question 1</summary>
Answer goes here.
</details>
<details name="faqs">
<summary>Question 2</summary>
Another answer.
</details>
同一组 name 的 <details>, 浏览器会自动帮你做到:
- 打开新的
- 旧的那一个自动收起
也就是说,你想要的“手风琴效果”, 根本用不着写那堆“当前激活项”“关闭其他全部”的 JavaScript。
知道之前:为了管理折叠状态,我能写出 40 行 JS。
知道之后:复制粘贴同一个属性名三次,完事。
2. title 居然有三重身份,不只是个小气泡提示
大部分时候,我们在任意标签上加 title, 唯一效果就是鼠标悬停出现一个 tooltip。
<button title="点我一下试试">点我</button>
这没什么问题。 但真正精彩的地方在这里——
① 当它遇上带 pattern 的 <input>
在这种场景里:
<input
type="text"
pattern="[A-Za-z]{3,}"
title="Please enter at least 3 letters"
/>
title 不再只是“悬浮提示”。
当用户输入不符合 pattern 的格式时, 浏览器会直接把 title 的内容当成错误提示文案弹出来。https://wxa.wxs.qq.com/tmpl/op/base_tmpl.html
你不用自己写一大堆校验逻辑、挂一堆 blur 事件, 浏览器就帮你完成了“格式提示 + 错误信息文案”的一套流程。
② 当它跟 <abbr>(缩略词)在一起
写缩写时,我们常这样写:
<abbr title="Hypertext Markup Language">HTML</abbr>
这里的规范非常严格:
<abbr>上的title,只能写完整的全称。 不要写解释、注释、顺手吐槽, 就是干干净净的“HTML 的完整展开”。
为什么? 因为屏幕阅读器和辅助设备, 会依赖这个 title 来读出真实含义。 你乱写一堆,等于在无障碍层面“扔烟雾弹”。
③ 当它跟 <dfn>(定义)勾搭在一起
<dfn> 用来标记一个“被定义的术语”。
在这种情况下:
dfn元素里可以显示缩写或短名- 但它的
title属性,必须写出完整定义
我就是在调试“读屏软件为什么不按我预期读缩写”的时候, 才顺藤摸瓜查到这些规范。
那一刻我才意识到: 这么多年,我几乎把 title 当成“随便写写的小注释”, 完全没有意识到它在语义上是有严格约束的。
3. value 可以让有序列表“一键穿越”
你可能知道 <ol> 可以自动编号:
<ol>
<li>第一项</li>
<li>第二项</li>
</ol>
但你未必用过这个写法:
<ol>
<li>First item</li>
<li>Second item</li>
<li value="37">Suddenly thirty-seven</li>
<li>Thirty-eight (it continues from there)</li>
</ol>
<li value="37"> 会直接告诉浏览器:
“从这行开始,编号是 37。”
后面的项会在这个基础上继续往上加。
最妙的是:
- 不仅阿拉伯数字可用
- 罗马数字、字母编号这些样式也都能配合使用
我曾用这个技巧写过一个“TOP 10 同事雷人行为榜”, 前几条编号正常, 然后直接跳到 47. 写——
“在办公室微波炉里热鱼。”
莫名其妙地非常有喜感。
4. multiple 不止是“多文件上传专用”,居然能管邮箱
multiple 出现在 <input type="file"> 上,你肯定不陌生:
<input type="file" multiple>
允许用户一次选多个文件,这很合理。
但当你写成这样——
<input type="email" multiple>
事情就有趣了。
在这里,multiple 的含义变成:
“允许用户输入多个邮箱地址,用逗号隔开。”
更关键的是:
- 浏览器会分别校验每一个邮箱的格式
- 你不用自己写一大坨正则去 split + trim + validate
我曾经一本正经花了一个下午,写了几十行自定义校验逻辑:
- 拆分字符串
- 去除空格
- 校验每个邮箱
- 提示哪一个错了
后来才发现:
人家 HTML 早就帮我把这事想好了。
之前:60 行 regex + 校验 + 错误提示处理。
现在:在 input 上多加一个单词:multiple。
5. datetime 在 <ins> / <del> 上,就是自带版本记录
<ins> 和 <del> 这两个标签, 本来就是用来标记新增内容和删除内容的。
但当你给它们补上 datetime 属性之后——
<p>
The meeting is at
<del datetime="2024-01-15">3pm</del>
<ins datetime="2024-01-15">4pm</ins>
</p>
你就多了一层能力:
用机器可读的方式,记录“这行内容是在什么时候被修改的”。
这东西特别适合用在:
- 更新日志页面(changelog)
- 协议、条款、隐私说明那种需要体现“变更历史”的文档
- 长期维护的帮助中心文章
有点像 Word 里的“修订模式”, 但整个过程是语义化的, 机器和人都看得懂。
6. disabled 一口气搞瘫整个 <fieldset>
你大概习惯给某个输入框加:
<input type="text" disabled>
单个禁用,这没问题。
但要是你有一大块区域,在某些条件下都不能改, 比如“没有权限的用户不能编辑这一整组字段”, 你干嘛要一个个去写?
其实你可以这样:
<fieldset disabled>
<legend>Locked Section</legend>
<input type="text" name="field1">
<input type="text" name="field2">
<input type="text" name="field3">
</fieldset>
当 <fieldset> 自身被加上 disabled:
里面所有表单控件,都会自动进入禁用状态。
不用逐个写 disabled, 也不用在 JS 里循环 20 次去挨个改属性。
当你有一大片表单要根据角色权限统一“只读”或“锁定”时, 这就是最优解。
7. target="_blank" 不止能开新标签页链接,还能开“新标签页表单”
链接上用 target="_blank",谁都知道是“新标签页打开”:
<a href="/download" target="_blank">下载</a>
但你试过把它用在 <form> 上吗?
<form action="/submit" method="post" target="_blank">
<!-- form fields -->
</form>
效果是——
表单提交之后,响应会在新标签页打开, 当前页面保持不动。
这在这些场景下非常好用:
- “提交后在新标签页展示收据/凭条”
- “导出报表,结果文件由新标签页触发下载”
- “提交数据后跳转到一个专门的打印页面”
我之前为了解决这个需求, 写过一套 window.open() 的逻辑, 还顺手踩了一地“浏览器拦截弹窗”的坑。
后来才发现:
一句
target="_blank",其实就足够优雅。
我终于看懂的真相:不是 HTML 太简单,而是我们学得太浅
这一连串发现之后,我突然意识到:
我犯的错误,是“一次性把 HTML 当成已经学完的东西”。
就好像你拿到一把瑞士军刀, 用了好多年,一直只用那一把主刀划东西, 从来没把它折开看过里面还有螺丝刀、剪刀、小锯子。
HTML 也是这样。
那些我以为已经用烂的属性:
nametitlevaluemultipledisabledtarget
其实都是会变身的角色。
它们在不同标签上, 有不同含义、不同行为,甚至引导着无障碍体验和语义结构。
而我们太习惯于:
“反正能跑就行,剩下的交给 JavaScript。”
于是写了一堆其实不用存在的代码。
你现在就能做的一件小事(但可能会删掉几十行废代码)
不需要一次性把这些全背下来。
我建议你现在只做一件事:
从上面挑一个你印象最深的属性, 打开你的项目,在代码里搜它。
然后认真看看:
- 你是不是永远只用它“一种用法”?
- 有没有地方可以改成:靠 HTML 自己的行为, 少写一半 JS?
我当时就是这么干的——
最后在项目里找到了 14 处, 是我以前手写了 JavaScript 去实现, 但其实浏览器原生就能做好的地方。
那天下午我删掉了 200 多行代码。 没有任何功能损失, 只有维护成本的下降和代码可读性的上升。
HTML 并没有“突然变强”。
真正变化的是—— 我们什么时候,愿意承认自己当初 只是学了“能跑起来的那一部分”, 而不是这门语言的全部。
你的代码,不一定要写得有多聪明。 它只需要:
先把现成能用的工具,真正用明白。全栈AI·探索:涵盖动效、React Hooks、Vue 技巧、LLM 应用、Python 脚本等专栏,案例驱动实战学习,点击二维码了解更多详情。
声明:来自JavaScript 每日一练,仅代表创作者观点。链接:https://eyangzhen.com/4444.html