[{"data":1,"prerenderedAt":8202},["ShallowReactive",2],{"\u002Ftutorials\u002Fhtml":3,"tutorials-search\u002Ftutorials\u002Fhtml":7841},{"id":4,"title":5,"body":6,"category":65,"cover":7831,"date":7832,"description":7833,"extension":7834,"meta":7835,"navigation":3041,"path":7836,"seo":7837,"sitemap":7838,"stem":7839,"__hash__":7840},"tutorials\u002Ftutorials\u002Fhtml.md","HTML 教程：从第一个标签到语义化页面",{"type":7,"value":8,"toc":7719},"minimark",[9,14,23,27,35,79,90,93,96,256,284,295,298,309,319,322,328,331,346,350,364,368,377,380,400,407,422,429,433,581,584,626,635,639,647,662,689,695,700,703,789,793,804,808,834,851,857,862,866,873,897,904,909,913,994,1015,1019,1022,1093,1097,1101,1107,1122,1147,1165,1168,1188,1270,1276,1280,1309,1314,1317,1354,1358,1367,1371,1377,1455,1472,1476,1487,1534,1547,1558,1562,1566,1606,1628,1631,1644,1647,1653,1688,1702,1706,1709,1713,1767,1806,1810,1813,1889,1904,2006,2025,2028,2059,2063,2072,2075,2248,2299,2304,2308,2315,2343,2352,2356,2373,2487,2490,2493,2528,2531,2535,2552,2556,2656,2677,2681,2685,2763,2766,2777,2780,2807,2811,2814,2919,2939,2943,2946,2949,3274,3282,3286,3292,3324,3333,3337,3344,3348,3368,3372,3443,3508,3512,3516,3524,3750,3754,3757,3793,3797,3893,3900,3912,3916,4170,4173,4228,4232,4239,4243,4288,4308,4317,4322,4326,4454,4463,4467,4470,4490,4618,4654,4657,4660,4687,4694,4722,4726,4736,4740,4817,4885,4889,4893,4992,5005,5027,5031,5152,5165,5217,5230,5234,5237,5241,5312,5326,5434,5455,5459,5473,5477,5480,5490,5557,5568,5572,5615,5619,5622,5625,5777,5781,5784,5790,5810,5813,5828,5839,5843,5956,5960,5973,5976,6049,6078,6082,6089,6236,6251,6255,6259,6369,6379,6383,6433,6437,6442,6446,6471,6507,6511,6515,6570,6592,6610,6614,6752,6765,6769,6822,6826,6829,6832,6867,6885,6888,6892,6895,6898,6972,6981,6985,6996,6999,7146,7149,7185,7189,7196,7199,7229,7233,7236,7258,7265,7268,7440,7443,7486,7490,7497,7500,7504,7516,7520,7524,7530,7604,7631,7634,7637,7709,7716],[10,11,13],"h2",{"id":12},"一html-是什么你的第一个页面","一、HTML 是什么：你的第一个页面",[15,16,17,18,22],"p",{},"HTML 不是编程语言，而是",[19,20,21],"strong",{},"标记语言","：它用一对对尖括号包裹的「标签」告诉浏览器，某一段文字是标题、某一段是段落、某一张图可以点开。理解这一点，后面所有标签的学习都会变成「这个标签表达什么含义」的问题，而不是「这个标签怎么写样式」的问题——样式是 CSS 的事。",[24,25,26],"h3",{"id":26},"浏览器把一个文件变成画面的过程",[15,28,29,30,34],{},"你在地址栏输入一个 ",[31,32,33],"code",{},".html"," 文件，浏览器大致做四件事：",[36,37,38,49,67,73],"ol",{},[39,40,41,44,45,48],"li",{},[19,42,43],{},"拿到字节流","：按 ",[31,46,47],{},"\u003Cmeta charset>"," 声明的编码（通常是 UTF-8）把字节解码成文字。声明写错，中文就会变成「æ¬å」这类乱码。",[39,50,51,54,55,58,59,62,63,66],{},[19,52,53],{},"解析成节点树","：逐字符读标签，遇到 ",[31,56,57],{},"\u003Cp>"," 就开一个节点，遇到 ",[31,60,61],{},"\u003C\u002Fp>"," 就闭合，层层嵌套形成「DOM 树」。树根是 ",[31,64,65],{},"html"," 节点。",[39,68,69,72],{},[19,70,71],{},"构建渲染树","：结合 CSS 决定哪些节点可见、以什么顺序和位置排布。",[39,74,75,78],{},[19,76,77],{},"绘制到屏幕","：按层叠顺序一笔画一画涂出来。",[15,80,81,82,85,86,89],{},"这条链路解释了后面会反复遇到的两个现象：标签",[19,83,84],{},"嵌套顺序","决定结构，而",[19,87,88],{},"书写顺序","不决定视觉层级（那是 CSS 定位的事）。",[24,91,92],{"id":92},"一个页面由三部分拼起来",[15,94,95],{},"任何 HTML 文件都可以拆成三层，先看最小可用版本：",[97,98,102],"pre",{"className":99,"code":100,"language":65,"meta":101,"style":101},"language-html shiki shiki-themes github-dark","\u003C!DOCTYPE html>\n\u003Chtml lang=\"zh-CN\">\n  \u003Chead>\n    \u003Cmeta charset=\"UTF-8\">\n    \u003Ctitle>我的第一个网页\u003C\u002Ftitle>\n  \u003C\u002Fhead>\n  \u003Cbody>\n    \u003Ch1>我的第一个网页\u003C\u002Fh1>\n    \u003Cp>这一行是段落。\u003C\u002Fp>\n  \u003C\u002Fbody>\n\u003C\u002Fhtml>\n","",[31,103,104,124,144,155,174,189,199,209,223,237,246],{"__ignoreMap":101},[105,106,109,113,117,121],"span",{"class":107,"line":108},"line",1,[105,110,112],{"class":111},"s95oV","\u003C!",[105,114,116],{"class":115},"s4JwU","DOCTYPE",[105,118,120],{"class":119},"svObZ"," html",[105,122,123],{"class":111},">\n",[105,125,127,130,132,135,138,142],{"class":107,"line":126},2,[105,128,129],{"class":111},"\u003C",[105,131,65],{"class":115},[105,133,134],{"class":119}," lang",[105,136,137],{"class":111},"=",[105,139,141],{"class":140},"sU2Wk","\"zh-CN\"",[105,143,123],{"class":111},[105,145,147,150,153],{"class":107,"line":146},3,[105,148,149],{"class":111},"  \u003C",[105,151,152],{"class":115},"head",[105,154,123],{"class":111},[105,156,158,161,164,167,169,172],{"class":107,"line":157},4,[105,159,160],{"class":111},"    \u003C",[105,162,163],{"class":115},"meta",[105,165,166],{"class":119}," charset",[105,168,137],{"class":111},[105,170,171],{"class":140},"\"UTF-8\"",[105,173,123],{"class":111},[105,175,177,179,182,185,187],{"class":107,"line":176},5,[105,178,160],{"class":111},[105,180,181],{"class":115},"title",[105,183,184],{"class":111},">我的第一个网页\u003C\u002F",[105,186,181],{"class":115},[105,188,123],{"class":111},[105,190,192,195,197],{"class":107,"line":191},6,[105,193,194],{"class":111},"  \u003C\u002F",[105,196,152],{"class":115},[105,198,123],{"class":111},[105,200,202,204,207],{"class":107,"line":201},7,[105,203,149],{"class":111},[105,205,206],{"class":115},"body",[105,208,123],{"class":111},[105,210,212,214,217,219,221],{"class":107,"line":211},8,[105,213,160],{"class":111},[105,215,216],{"class":115},"h1",[105,218,184],{"class":111},[105,220,216],{"class":115},[105,222,123],{"class":111},[105,224,226,228,230,233,235],{"class":107,"line":225},9,[105,227,160],{"class":111},[105,229,15],{"class":115},[105,231,232],{"class":111},">这一行是段落。\u003C\u002F",[105,234,15],{"class":115},[105,236,123],{"class":111},[105,238,240,242,244],{"class":107,"line":239},10,[105,241,194],{"class":111},[105,243,206],{"class":115},[105,245,123],{"class":111},[105,247,249,252,254],{"class":107,"line":248},11,[105,250,251],{"class":111},"\u003C\u002F",[105,253,65],{"class":115},[105,255,123],{"class":111},[257,258,259,265,275],"ul",{},[39,260,261,264],{},[31,262,263],{},"\u003C!DOCTYPE html>","：告诉浏览器「按现代标准模式渲染」，第二章细说。",[39,266,267,270,271,274],{},[31,268,269],{},"\u003Chead>","：",[19,272,273],{},"给浏览器和搜索引擎看的元信息","，标题、编码、样式表引用都放这里，不会显示在页面正文里。",[39,276,277,270,280,283],{},[31,278,279],{},"\u003Cbody>",[19,281,282],{},"给人看的全部内容","，页面上你看到的每一个字都在这个标签内部。",[15,285,286,287,290,291,294],{},"新手最容易混淆的是 ",[31,288,289],{},"\u003Ctitle>"," 与 ",[31,292,293],{},"\u003Ch1>","：前者出现在浏览器标签页和搜索结果里，后者出现在页面正文顶部。两者都要写，作用完全不同。",[24,296,297],{"id":297},"写下你的第一个文件",[15,299,300,301,304,305,308],{},"准备一个空文件夹，用任意文本编辑器（记事本、VS Code 都行）新建 ",[31,302,303],{},"index.html","，把上面那段代码粘进去，保存后",[19,306,307],{},"双击文件","，它就会在自己的浏览器里打开。",[15,310,311,312,314,315,318],{},"改一行 ",[31,313,57],{}," 里的文字，保存，回到浏览器按刷新——这就是 HTML 的开发循环，不需要任何编译工具。把文件拖进 VS Code 再按 ",[31,316,317],{},"F1"," 搜「Open with Live Server」（需装插件）可以省掉手动刷新。",[15,320,321],{},"下面这个框里就是上面那段代码的真实渲染结果，右边目录的小节、左侧的章节导航，以及正文里所有类似的可运行示例，都是独立文件，点「在新标签页打开」可以单独查看：",[323,324],"demo-html",{"height":325,"src":326,"title":327},320,"\u002Ftutorials\u002Fhtml\u002Fdemos\u002F01-first-page.html","示例 01：三个标签搭出的页面",[24,329,330],{"id":330},"接下来往哪走",[15,332,333,334,337,338,341,342,345],{},"从第二章开始按顺序推进：先解决「文件为什么这么开头」（文档结构），再补齐文字、列表、链接、图片、表格、表单这些",[19,335,336],{},"内容型标签","，然后进入",[19,339,340],{},"结构型标签","与语义化，最后是可访问性、SEO 与综合实战。每一章都配可运行示例，看懂了就用，忘了就回来查——正文左侧是章节目录，也可以在搜索框里直接输标签名（例如 ",[31,343,344],{},"table","）定位到讲它的那一节。",[10,347,349],{"id":348},"二文档结构与-doctype","二、文档结构与 DOCTYPE",[15,351,352,353,355,356,355,359,355,361,363],{},"第一章的最小示例里有四个容易被跳过但不该跳过的部分：",[31,354,263],{},"、",[31,357,358],{},"\u003Chtml lang>",[31,360,269],{},[31,362,47],{},"。它们不产出任何可见内容，却决定了页面「被怎么解读」。",[24,365,367],{"id":366},"doctype-决定浏览器用哪套规则排版","DOCTYPE 决定浏览器用哪套规则排版",[15,369,370,372,373,376],{},[31,371,263],{}," 不是标签，而是一条",[19,374,375],{},"处理指令","，作用只有一个：让浏览器进入「标准模式（standards mode）」。",[15,378,379],{},"在 2000 年前后，浏览器为了兼容当年写得很不规范的网页，保留了两套排版引擎行为。没有 DOCTYPE（或写法是老式的一长串）时，浏览器会退回「怪异模式（quirks mode）」，其中的差异至今还能测出来：",[257,381,382,393],{},[39,383,384,385,388,389,392],{},"怪异模式下元素的 ",[31,386,387],{},"box-sizing"," 行为、表格里的字体继承、",[31,390,391],{},"\u003Cimg>"," 的基线对齐都与标准模式不同；",[39,394,395,396,399],{},"最直观的一条：标准模式下 ",[31,397,398],{},"html { height: 100% }"," 才让百分比高度逐级生效，怪异模式里同样的写法可能得到 0 高度。",[15,401,402,403,406],{},"现代写法只有一种，且",[19,404,405],{},"必须放在文件最前面","（前面除了空白和注释不能有任何内容，否则浏览器会当成文本节点渲染到页面上）：",[97,408,410],{"className":99,"code":409,"language":65,"meta":101,"style":101},"\u003C!DOCTYPE html>\n",[31,411,412],{"__ignoreMap":101},[105,413,414,416,418,420],{"class":107,"line":108},[105,415,112],{"class":111},[105,417,116],{"class":115},[105,419,120],{"class":119},[105,421,123],{"class":111},[15,423,424,425,428],{},"看到搜索结果里有 ",[31,426,427],{},"\u003Cmeta name=\"generator\" content=\"FrontPage.Editor.1\">"," 这类古董 DOCTYPE 的教程，可以直接关掉那个页面——它们教的正是怪异模式。",[24,430,432],{"id":431},"htmlheadbody-各自负责什么","html、head、body 各自负责什么",[97,434,436],{"className":99,"code":435,"language":65,"meta":101,"style":101},"\u003Chtml lang=\"zh-CN\">\n  \u003Chead>\n    \u003Cmeta charset=\"UTF-8\">\n    \u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n    \u003Ctitle>页面标题，出现在标签页和搜索结果里\u003C\u002Ftitle>\n    \u003Clink rel=\"stylesheet\" href=\"style.css\">\n  \u003C\u002Fhead>\n  \u003Cbody>\n    \u003Ch1>正文从这开始\u003C\u002Fh1>\n  \u003C\u002Fbody>\n\u003C\u002Fhtml>\n",[31,437,438,452,460,474,498,511,536,544,552,565,573],{"__ignoreMap":101},[105,439,440,442,444,446,448,450],{"class":107,"line":108},[105,441,129],{"class":111},[105,443,65],{"class":115},[105,445,134],{"class":119},[105,447,137],{"class":111},[105,449,141],{"class":140},[105,451,123],{"class":111},[105,453,454,456,458],{"class":107,"line":126},[105,455,149],{"class":111},[105,457,152],{"class":115},[105,459,123],{"class":111},[105,461,462,464,466,468,470,472],{"class":107,"line":146},[105,463,160],{"class":111},[105,465,163],{"class":115},[105,467,166],{"class":119},[105,469,137],{"class":111},[105,471,171],{"class":140},[105,473,123],{"class":111},[105,475,476,478,480,483,485,488,491,493,496],{"class":107,"line":157},[105,477,160],{"class":111},[105,479,163],{"class":115},[105,481,482],{"class":119}," name",[105,484,137],{"class":111},[105,486,487],{"class":140},"\"viewport\"",[105,489,490],{"class":119}," content",[105,492,137],{"class":111},[105,494,495],{"class":140},"\"width=device-width, initial-scale=1\"",[105,497,123],{"class":111},[105,499,500,502,504,507,509],{"class":107,"line":176},[105,501,160],{"class":111},[105,503,181],{"class":115},[105,505,506],{"class":111},">页面标题，出现在标签页和搜索结果里\u003C\u002F",[105,508,181],{"class":115},[105,510,123],{"class":111},[105,512,513,515,518,521,523,526,529,531,534],{"class":107,"line":191},[105,514,160],{"class":111},[105,516,517],{"class":115},"link",[105,519,520],{"class":119}," rel",[105,522,137],{"class":111},[105,524,525],{"class":140},"\"stylesheet\"",[105,527,528],{"class":119}," href",[105,530,137],{"class":111},[105,532,533],{"class":140},"\"style.css\"",[105,535,123],{"class":111},[105,537,538,540,542],{"class":107,"line":201},[105,539,194],{"class":111},[105,541,152],{"class":115},[105,543,123],{"class":111},[105,545,546,548,550],{"class":107,"line":211},[105,547,149],{"class":111},[105,549,206],{"class":115},[105,551,123],{"class":111},[105,553,554,556,558,561,563],{"class":107,"line":225},[105,555,160],{"class":111},[105,557,216],{"class":115},[105,559,560],{"class":111},">正文从这开始\u003C\u002F",[105,562,216],{"class":115},[105,564,123],{"class":111},[105,566,567,569,571],{"class":107,"line":239},[105,568,194],{"class":111},[105,570,206],{"class":115},[105,572,123],{"class":111},[105,574,575,577,579],{"class":107,"line":248},[105,576,251],{"class":111},[105,578,65],{"class":115},[105,580,123],{"class":111},[15,582,583],{},"三条边界值得记住：",[36,585,586,602,613],{},[39,587,588,593,594,596,597,355,599,601],{},[19,589,590,592],{},[31,591,269],{}," 里放的是「关于页面的信息」","：编码、标题、样式表、图标、SEO 元信息。",[31,595,269],{}," 里出现 ",[31,598,57],{},[31,600,293],{}," 这类可见元素是不合法的，浏览器会把它「揪」到 body 开头，导致难以排查的错位。",[39,603,604,609,610,612],{},[19,605,606,608],{},[31,607,279],{}," 里放全部可见内容","，且一个文档只能有一个 ",[31,611,279],{},"。",[39,614,615,621,622,625],{},[19,616,617,620],{},[31,618,619],{},"\u003Chtml>"," 是根节点","，所有 CSS\u002FJS 操作 DOM 时面对的树根就是它；",[31,623,624],{},"lang"," 写在它上面才能被辅助技术和浏览器识别（第十七、十八章会再用到这一点）。",[15,627,628,629,631,632,634],{},"浏览器解析时会自动补全缺失标签：即使你只写 ",[31,630,279],{}," 内容而不写 ",[31,633,619],{},"，DevTools 里的 Elements 面板仍然显示完整的树。但「浏览器能补救」不等于「写错了没代价」——补救规则让结构推断变得不可预期，也让别人接手时读不出意图。",[24,636,638],{"id":637},"charset-与-lang两个必须显式声明的属性","charset 与 lang：两个必须显式声明的属性",[15,640,641,646],{},[19,642,643],{},[31,644,645],{},"\u003Cmeta charset=\"UTF-8\">"," 声明字节到文字的解码方式。它有两个实操要点：",[257,648,649,655],{},[39,650,651,652,654],{},"位置要尽量靠前，且在 ",[31,653,289],{}," 之前。浏览器为了省时间，只会读取文档开头的有限字节来找编码声明；放得太晚，标题就可能乱码。",[39,656,657,658,661],{},"声明只管「浏览器怎么读」，文件本身也得",[19,659,660],{},"真的存成 UTF-8","。VS Code 右下角能看到并修改当前编码；用记事本保存时选「UTF-8」。声明与实际编码不一致，改声明没用，改存储编码才有用。",[15,663,664,668,669,672,673,676,677,680,681,684,685,688],{},[19,665,666],{},[31,667,624],{}," 的取值是 BCP 47 语言标签：简体中文 ",[31,670,671],{},"zh-CN","、繁体中文 ",[31,674,675],{},"zh-TW","、英文 ",[31,678,679],{},"en","、日文 ",[31,682,683],{},"ja","。它影响屏幕阅读器发音、浏览器翻译提示、连字与换行规则（CSS 的 ",[31,686,687],{},":lang()"," 选择器也读它，CSS 教程的选择器一章里会用到）。",[15,690,691,692,694],{},"下面这个示例把本节要点都放进去了，右上角可以直接看它自己的 ",[31,693,269],{}," 写了什么：",[323,696],{"height":697,"src":698,"title":699},420,"\u002Ftutorials\u002Fhtml\u002Fdemos\u002F02-doctype.html","示例 02：完整文档骨架",[24,701,702],{"id":702},"常见报错与自查",[344,704,705,721],{},[706,707,708],"thead",{},[709,710,711,715,718],"tr",{},[712,713,714],"th",{},"现象",[712,716,717],{},"大概率原因",[712,719,720],{},"怎么确认",[722,723,724,736,749,771],"tbody",{},[709,725,726,730,733],{},[727,728,729],"td",{},"中文变乱码",[727,731,732],{},"文件实际编码不是 UTF-8",[727,734,735],{},"编辑器右下角看编码，另存为 UTF-8",[709,737,738,741,744],{},[727,739,740],{},"页面顶部多出一行文字",[727,742,743],{},"DOCTYPE 前后有内容，或首行漏写",[727,745,746,747],{},"查看源码第一行是否为 ",[31,748,263],{},[709,750,751,754,757],{},[727,752,753],{},"百分比高度不生效、间距莫名",[727,755,756],{},"浏览器进了怪异模式",[727,758,759,760,763,764,767,768],{},"DevTools Console 输入 ",[31,761,762],{},"document.compatMode","，标准模式返回 ",[31,765,766],{},"CSS1Compat","，怪异模式返回 ",[31,769,770],{},"BackCompat",[709,772,773,776,783],{},[727,774,775],{},"屏幕阅读器读中文很怪",[727,777,778,780,781],{},[31,779,619],{}," 上没有 ",[31,782,624],{},[727,784,785,786,788],{},"查看源码 ",[31,787,619],{}," 标签",[10,790,792],{"id":791},"三文本与段落标签","三、文本与段落标签",[15,794,795,796,799,800,803],{},"从这一章起进入「内容型标签」。判断一个标签该不该用，先问：它表达的是",[19,797,798],{},"结构含义","还是",[19,801,802],{},"视觉效果","？前者归 HTML，后者归 CSS。",[24,805,807],{"id":806},"pbrhr-的分工","p、br、hr 的分工",[257,809,810,815,821],{},[39,811,812,814],{},[31,813,57],{}," 是「一个语意段落」，浏览器默认给它上下外边距。段落之间空一行是排版结果，不是你把回车敲出来的。",[39,816,817,820],{},[31,818,819],{},"\u003Cbr>"," 只表示「同一句话内部必须换行」，典型场景是地址、诗歌、签名。它不产生新段落，所以段间距不会变大。",[39,822,823,826,827,830,831,612],{},[31,824,825],{},"\u003Chr>"," 表示「主题转换的分隔」，比如一章结束、话题跳到另一个。它语义上有意义，",[19,828,829],{},"不是装饰线","——想画一条线请交给 CSS 的 ",[31,832,833],{},"border",[15,835,836,837,840,841,843,844,847,848,612],{},"HTML 会把源码里连续的空白字符（空格、制表符、回车）",[19,838,839],{},"折叠成一个空格","，这叫空白折叠。所以你在 ",[31,842,57],{}," 里敲十个回车也不会出现空行。想保留原始空白要用 ",[31,845,846],{},"\u003Cpre>","（它还保留缩进，常用于代码块），想要真正的空格请用实体 ",[31,849,850],{},"&nbsp;",[15,852,853,854,856],{},"下面这个示例把这三条都做实了，包括「空白折叠验证」和 ",[31,855,846],{}," 的对比：",[323,858],{"height":859,"src":860,"title":861},460,"\u002Ftutorials\u002Fhtml\u002Fdemos\u002F03-text.html","示例 03：段落、换行与分隔线",[24,863,865],{"id":864},"h1h6层级不是字号","h1–h6：层级不是字号",[15,867,868,869,872],{},"六个标题标签构成文档的",[19,870,871],{},"大纲","，屏幕阅读器和浏览器的「页面概览」都读它。规则只有两条：",[36,874,875,885],{},[39,876,877,878,880,881,884],{},"一页可以有多个 ",[31,879,293],{},"，但通常只放一个（页面主标题），从 ",[31,882,883],{},"\u003Ch2>"," 开始往下分章节。",[39,886,887,270,890,892,893,896],{},[19,888,889],{},"不要跳层级",[31,891,10],{}," 之后直接写 ",[31,894,895],{},"h4"," 会在大纲里留下空洞。",[15,898,899,900,903],{},"新手最常见的错误是「觉得这行字要小一点，所以用 ",[31,901,902],{},"h5","」。层级和字号是两件事，字号交给 CSS 改；用错层级只是把可访问性弄坏了。",[323,905],{"height":906,"src":907,"title":908},520,"\u002Ftutorials\u002Fhtml\u002Fdemos\u002F03b-headings.html","示例 03b：标题层级与语义强调",[24,910,912],{"id":911},"strongem-与-bi-的语义差别","strong、em 与 b、i 的语义差别",[344,914,915,931],{},[706,916,917],{},[709,918,919,922,925,928],{},[712,920,921],{},"标签",[712,923,924],{},"含义",[712,926,927],{},"屏幕阅读器",[712,929,930],{},"默认视觉",[722,932,933,949,965,980],{},[709,934,935,940,943,946],{},[727,936,937],{},[31,938,939],{},"\u003Cstrong>",[727,941,942],{},"内容重要",[727,944,945],{},"加重语气播报",[727,947,948],{},"加粗",[709,950,951,956,959,962],{},[727,952,953],{},[31,954,955],{},"\u003Cem>",[727,957,958],{},"语气重音",[727,960,961],{},"换语调播报",[727,963,964],{},"斜体",[709,966,967,972,975,978],{},[727,968,969],{},[31,970,971],{},"\u003Cb>",[727,973,974],{},"仅视觉偏移（产品名、关键词）",[727,976,977],{},"不特别处理",[727,979,948],{},[709,981,982,987,990,992],{},[727,983,984],{},[31,985,986],{},"\u003Ci>",[727,988,989],{},"仅视觉偏移（术语、外文、物种拉丁名）",[727,991,977],{},[727,993,964],{},[15,995,996,997,1000,1001,1003,1004,1007,1008,1003,1011,1014],{},"判断办法：如果把这段文字的强调效果去掉，",[19,998,999],{},"意思有没有变","？变了就是 ",[31,1002,19],{},"\u002F",[31,1005,1006],{},"em","，没变就是 ",[31,1009,1010],{},"b",[31,1012,1013],{},"i","，而纯粹「想让它显眼」应该用 CSS。",[24,1016,1018],{"id":1017},"代码引用与其余文本标签","代码、引用与其余文本标签",[15,1020,1021],{},"正文里还有几个高频标签值得一次记牢：",[257,1023,1024,1036,1042,1056,1066,1075,1087],{},[39,1025,1026,1029,1030,1032,1033,612],{},[31,1027,1028],{},"\u003Ccode>"," 行内代码，通常包在 ",[31,1031,846],{}," 里做成代码块：",[31,1034,1035],{},"\u003Cpre>\u003Ccode>…\u003C\u002Fcode>\u003C\u002Fpre>",[39,1037,1038,1041],{},[31,1039,1040],{},"\u003Cabbr title=\"Document Object Model\">DOM\u003C\u002Fabbr>"," 首次出现的缩写给它全称，鼠标悬停和读屏都能拿到。",[39,1043,1044,1047,1048,1051,1052,1055],{},[31,1045,1046],{},"\u003Cblockquote>"," 引用整段，",[31,1049,1050],{},"\u003Cq>"," 引用句内；",[31,1053,1054],{},"\u003Ccite>"," 标出处。",[39,1057,1058,1061,1062,1065],{},[31,1059,1060],{},"\u003Cdel>"," \u002F ",[31,1063,1064],{},"\u003Cins>"," 标删除与插入（价格改动、修订记录），带语义而非单纯划线。",[39,1067,1068,1071,1072,1074],{},[31,1069,1070],{},"\u003Cs>"," 表示「已不准确」，跟 ",[31,1073,1060],{},"（内容被删除）不同。",[39,1076,1077,1061,1080,1083,1084,612],{},[31,1078,1079],{},"\u003Csub>",[31,1081,1082],{},"\u003Csup>"," 下标上标，",[31,1085,1086],{},"H\u003Csub>2\u003C\u002Fsub>O",[39,1088,1089,1092],{},[31,1090,1091],{},"\u003Ctime datetime=\"2026-10-03\">10 月 3 日\u003C\u002Ftime>"," 给人看中文日期，给机器读 ISO 格式，搜索引擎会用它。",[10,1094,1096],{"id":1095},"四列表","四、列表",[24,1098,1100],{"id":1099},"ul-与-ol顺序有没有意义","ul 与 ol：顺序有没有意义",[15,1102,1103,1104],{},"判断只需一个问题：",[19,1105,1106],{},"换一个顺序，内容还成立吗？",[257,1108,1109,1116],{},[39,1110,1111,1112,1115],{},"不成立（安装步骤、排行榜、法律条款编号）→ ",[31,1113,1114],{},"\u003Col>","，序号本身携带信息。",[39,1117,1118,1119,612],{},"成立（特性清单、导航项、标签云）→ ",[31,1120,1121],{},"\u003Cul>",[15,1123,1124,1126,1127,1130,1131,1134,1135,1138,1139,1142,1143,1146],{},[31,1125,1114],{}," 的三个属性很实用：",[31,1128,1129],{},"type=\"A|a|I|i|1\""," 换编号样式，",[31,1132,1133],{},"start=\"5\""," 指定起始序号，",[31,1136,1137],{},"reversed"," 倒序。给单个 ",[31,1140,1141],{},"\u003Cli>"," 写 ",[31,1144,1145],{},"value=\"10\""," 可以插入自定义序号。",[15,1148,1149,1150,1153,1154,1160,1161,1164],{},"圆点和数字都由 CSS 的 ",[31,1151,1152],{},"list-style"," 控制，",[19,1155,1156,1157,1159],{},"「不想要圆点」不是放弃 ",[31,1158,1121],{}," 的理由","——写 ",[31,1162,1163],{},"list-style: none"," 即可，语义保留。",[24,1166,1167],{"id":1167},"嵌套列表的正确位置",[15,1169,1170,1171,1173,1174,1176,1177,1179,1180,1183,1184,1187],{},"嵌套的唯一规则：新的 ",[31,1172,1121],{}," 或 ",[31,1175,1114],{}," 必须放在父 ",[31,1178,1141],{}," ",[19,1181,1182],{},"内部","、文本之后、",[31,1185,1186],{},"\u003C\u002Fli>"," 之前。",[97,1189,1191],{"className":99,"code":1190,"language":65,"meta":101,"style":101},"\u003Cul>\n  \u003Cli>支持 UTF-8\n    \u003Cul>\n      \u003Cli>中文标题不乱码\u003C\u002Fli>\n    \u003C\u002Ful>\n  \u003C\u002Fli>\n  \u003Cli>支持表格\u003C\u002Fli>\n\u003C\u002Ful>\n",[31,1192,1193,1201,1210,1218,1232,1241,1249,1262],{"__ignoreMap":101},[105,1194,1195,1197,1199],{"class":107,"line":108},[105,1196,129],{"class":111},[105,1198,257],{"class":115},[105,1200,123],{"class":111},[105,1202,1203,1205,1207],{"class":107,"line":126},[105,1204,149],{"class":111},[105,1206,39],{"class":115},[105,1208,1209],{"class":111},">支持 UTF-8\n",[105,1211,1212,1214,1216],{"class":107,"line":146},[105,1213,160],{"class":111},[105,1215,257],{"class":115},[105,1217,123],{"class":111},[105,1219,1220,1223,1225,1228,1230],{"class":107,"line":157},[105,1221,1222],{"class":111},"      \u003C",[105,1224,39],{"class":115},[105,1226,1227],{"class":111},">中文标题不乱码\u003C\u002F",[105,1229,39],{"class":115},[105,1231,123],{"class":111},[105,1233,1234,1237,1239],{"class":107,"line":176},[105,1235,1236],{"class":111},"    \u003C\u002F",[105,1238,257],{"class":115},[105,1240,123],{"class":111},[105,1242,1243,1245,1247],{"class":107,"line":191},[105,1244,194],{"class":111},[105,1246,39],{"class":115},[105,1248,123],{"class":111},[105,1250,1251,1253,1255,1258,1260],{"class":107,"line":201},[105,1252,149],{"class":111},[105,1254,39],{"class":115},[105,1256,1257],{"class":111},">支持表格\u003C\u002F",[105,1259,39],{"class":115},[105,1261,123],{"class":111},[105,1263,1264,1266,1268],{"class":107,"line":211},[105,1265,251],{"class":111},[105,1267,257],{"class":115},[105,1269,123],{"class":111},[15,1271,1272,1273,1275],{},"如果误把子列表写在 ",[31,1274,1186],{}," 之后，它会变成和父列表同级，缩进全乱——而且浏览器不会报错，这是最难自查的一类问题。",[24,1277,1279],{"id":1278},"dldtdd术语表专用","dl、dt、dd：术语表专用",[15,1281,1282,1285,1286,1289,1290,1293,1294,1296,1297,1299,1300,1302,1303,1305,1306,1308],{},[31,1283,1284],{},"\u003Cdl>"," 是「描述列表」，",[31,1287,1288],{},"\u003Cdt>"," 名词、",[31,1291,1292],{},"\u003Cdd>"," 解释，适合 API 参数表、FAQ、词典条目。一个 ",[31,1295,1288],{}," 可以跟多个 ",[31,1298,1292],{},"（一词多义），也可以多个 ",[31,1301,1288],{}," 共用一个 ",[31,1304,1292],{},"（多词一义）。用 ",[31,1307,57],{}," 假装术语表是常见错误，因为它丢掉了「这是配对关系」的结构信息。",[323,1310],{"height":1311,"src":1312,"title":1313},560,"\u002Ftutorials\u002Fhtml\u002Fdemos\u002F04-lists.html","示例 04：ul \u002F ol \u002F dl 三种列表",[24,1315,1316],{"id":1316},"列表的三处常见误用",[36,1318,1319,1328,1347],{},[39,1320,1321,1322,1324,1325,1327],{},"用 ",[31,1323,819],{}," 或连续 ",[31,1326,57],{}," 堆清单——读屏会播报「列表，共 3 项」，帮你数位置，堆出来的没有。",[39,1329,1330,1331,1334,1335,1338,1339,1338,1341,1338,1343,1346],{},"站内导航写成 ",[31,1332,1333],{},"\u003Cdiv>\u003Ca>","。导航的正确形状是 ",[31,1336,1337],{},"\u003Cnav>"," + ",[31,1340,1121],{},[31,1342,1141],{},[31,1344,1345],{},"\u003Ca>","，第九章展开。",[39,1348,1349,1350,1353],{},"表格数据用列表写、列表用 ",[31,1351,1352],{},"\u003Ctable>"," 写。判断标准：横竖都有对应关系、需要表头才能读懂 → 表格；否则列表。第七章细说。",[10,1355,1357],{"id":1356},"五超链接-a","五、超链接 a",[15,1359,1360,1362,1363,1366],{},[31,1361,1345],{}," 是 HTML 里唯一让「文档」变成「网络」的标签——",[31,1364,1365],{},"href"," 指向另一个位置，浏览器就把两张图连成一条边。",[24,1368,1370],{"id":1369},"href-的四种路径写法","href 的四种路径写法",[15,1372,1373,1374,270],{},"假设当前页面在 ",[31,1375,1376],{},"\u002Fblog\u002F2026\u002Fintro.html",[344,1378,1379,1392],{},[706,1380,1381],{},[709,1382,1383,1386,1389],{},[712,1384,1385],{},"写法",[712,1387,1388],{},"实际指向",[712,1390,1391],{},"什么时候用",[722,1393,1394,1409,1424,1442],{},[709,1395,1396,1401,1406],{},[727,1397,1398],{},[31,1399,1400],{},"other.html",[727,1402,1403],{},[31,1404,1405],{},"\u002Fblog\u002F2026\u002Fother.html",[727,1407,1408],{},"同目录跳转",[709,1410,1411,1416,1421],{},[727,1412,1413],{},[31,1414,1415],{},"..\u002Fimg\u002Fa.png",[727,1417,1418],{},[31,1419,1420],{},"\u002Fblog\u002Fimg\u002Fa.png",[727,1422,1423],{},"往上退一级",[709,1425,1426,1431,1436],{},[727,1427,1428],{},[31,1429,1430],{},"\u002Fimg\u002Fa.png",[727,1432,1433,1434],{},"站点根下的 ",[31,1435,1430],{},[727,1437,1438,1441],{},[19,1439,1440],{},"最推荐","，与当前页层级无关",[709,1443,1444,1449,1452],{},[727,1445,1446],{},[31,1447,1448],{},"https:\u002F\u002Fsite.com\u002Fx",[727,1450,1451],{},"站外",[727,1453,1454],{},"真的去站外时",[15,1456,1457,1460,1461,1467,1468,1471],{},[31,1458,1459],{},".\u002F"," 开头等于当前目录，可省略。",[19,1462,1463,1464,1466],{},"以 ",[31,1465,1003],{}," 开头的路径最稳","，因为「跟着目录跑」的相对路径在部署到子目录后极易断链。本站部署在 ",[31,1469,1470],{},"\u002Fnuxt\u002F"," 子路径下，教程里的示例统一用带前缀的站内绝对路径。",[24,1473,1475],{"id":1474},"页内锚点id-是跳转目标","页内锚点：id 是跳转目标",[15,1477,1478,1479,1482,1483,1486],{},"任何元素只要有 ",[31,1480,1481],{},"id","，就能被 ",[31,1484,1485],{},"#那个id"," 定位：",[97,1488,1490],{"className":99,"code":1489,"language":65,"meta":101,"style":101},"\u003Ch2 id=\"pricing\">价格\u003C\u002Fh2>\n\u003Ca href=\"#pricing\">跳到价格\u003C\u002Fa>\n",[31,1491,1492,1513],{"__ignoreMap":101},[105,1493,1494,1496,1498,1501,1503,1506,1509,1511],{"class":107,"line":108},[105,1495,129],{"class":111},[105,1497,10],{"class":115},[105,1499,1500],{"class":119}," id",[105,1502,137],{"class":111},[105,1504,1505],{"class":140},"\"pricing\"",[105,1507,1508],{"class":111},">价格\u003C\u002F",[105,1510,10],{"class":115},[105,1512,123],{"class":111},[105,1514,1515,1517,1520,1522,1524,1527,1530,1532],{"class":107,"line":126},[105,1516,129],{"class":111},[105,1518,1519],{"class":115},"a",[105,1521,528],{"class":119},[105,1523,137],{"class":111},[105,1525,1526],{"class":140},"\"#pricing\"",[105,1528,1529],{"class":111},">跳到价格\u003C\u002F",[105,1531,1519],{"class":115},[105,1533,123],{"class":111},[15,1535,1536,1537,1540,1541,1544,1545,612],{},"跨页锚点把两者拼起来：",[31,1538,1539],{},"\u002Ftutorials\u002Fhtml#表格","。旧教程里的 ",[31,1542,1543],{},"\u003Ca name=\"x\">"," 已过时，一律改用 ",[31,1546,1481],{},[15,1548,1549,1550,1553,1554,1557],{},"注意：锚点跳转是浏览器行为，CSS 的 ",[31,1551,1552],{},"scroll-behavior: smooth"," 可以让它平滑滚动，被跳转的标题需要 ",[31,1555,1556],{},"scroll-margin-top"," 才不会被吸顶导航遮住——这两条都属于 CSS，写在样式里而不是 JS。",[323,1559],{"height":1311,"src":1560,"title":1561},"\u002Ftutorials\u002Fhtml\u002Fdemos\u002F05-links.html","示例 05：路径写法与页内锚点",[24,1563,1565],{"id":1564},"target-与-rel新标签页必须带-noopener","target 与 rel：新标签页必须带 noopener",[97,1567,1569],{"className":99,"code":1568,"language":65,"meta":101,"style":101},"\u003Ca href=\"https:\u002F\u002Fexample.com\" target=\"_blank\" rel=\"noopener\">外部站点\u003C\u002Fa>\n",[31,1570,1571],{"__ignoreMap":101},[105,1572,1573,1575,1577,1579,1581,1584,1587,1589,1592,1594,1596,1599,1602,1604],{"class":107,"line":108},[105,1574,129],{"class":111},[105,1576,1519],{"class":115},[105,1578,528],{"class":119},[105,1580,137],{"class":111},[105,1582,1583],{"class":140},"\"https:\u002F\u002Fexample.com\"",[105,1585,1586],{"class":119}," target",[105,1588,137],{"class":111},[105,1590,1591],{"class":140},"\"_blank\"",[105,1593,520],{"class":119},[105,1595,137],{"class":111},[105,1597,1598],{"class":140},"\"noopener\"",[105,1600,1601],{"class":111},">外部站点\u003C\u002F",[105,1603,1519],{"class":115},[105,1605,123],{"class":111},[15,1607,1608,1611,1612,1615,1616,1619,1620,1623,1624,1627],{},[31,1609,1610],{},"target=\"_blank\""," 会让被打开的页面在旧浏览器里拿到 ",[31,1613,1614],{},"window.opener","，从而能反向修改你本页地址（tabnabbing）。现代浏览器已对 ",[31,1617,1618],{},"_blank"," 隐含 ",[31,1621,1622],{},"noopener","，但显式写出更保险。加上 ",[31,1625,1626],{},"noreferrer"," 还会禁止发送来源信息，代价是对方统计里看不到你。",[15,1629,1630],{},"两条实践约定：",[257,1632,1633,1641],{},[39,1634,1635,1640],{},[19,1636,1637,1638],{},"站内链接不要加 ",[31,1639,1610],{},"：它切断返回路径，也多占一个标签页。",[39,1642,1643],{},"下载链接、PDF 之类需要保留当前页时才用新标签页。",[24,1645,1646],{"id":1646},"链接文字要能被脱离上下文读懂",[15,1648,1649,1652],{},[31,1650,1651],{},"\u003Ca>点击这里\u003C\u002Fa>"," 是反例：读屏用户逐个链接浏览时只听到一串「点击这里」。把链接嵌在能独立表意的短语上：",[97,1654,1656],{"className":99,"code":1655,"language":65,"meta":101,"style":101},"\u003Cp>具体配置项见\u003Ca href=\"\u002Fdocs\u002Fconfig\">配置文件说明\u003C\u002Fa>。\u003C\u002Fp>\n",[31,1657,1658],{"__ignoreMap":101},[105,1659,1660,1662,1664,1667,1669,1671,1673,1676,1679,1681,1684,1686],{"class":107,"line":108},[105,1661,129],{"class":111},[105,1663,15],{"class":115},[105,1665,1666],{"class":111},">具体配置项见\u003C",[105,1668,1519],{"class":115},[105,1670,528],{"class":119},[105,1672,137],{"class":111},[105,1674,1675],{"class":140},"\"\u002Fdocs\u002Fconfig\"",[105,1677,1678],{"class":111},">配置文件说明\u003C\u002F",[105,1680,1519],{"class":115},[105,1682,1683],{"class":111},">。\u003C\u002F",[105,1685,15],{"class":115},[105,1687,123],{"class":111},[15,1689,1690,1691,1694,1695,1698,1699,1701],{},"同理，",[31,1692,1693],{},"href=\"#\""," 的空链接会让页面滚回顶部且没有任何语义，纯按钮行为应该用 ",[31,1696,1697],{},"\u003Cbutton>","（第十章），不要拿 ",[31,1700,1345],{}," 冒充。",[10,1703,1705],{"id":1704},"六图片-img","六、图片 img",[15,1707,1708],{},"图片是页面里最容易「看起来正常、实际上很糟」的标签：破图、跳动、体积超标、读屏念不出来，四类问题各对应一个属性没写。",[24,1710,1712],{"id":1711},"srcaltwidthheight四个都要写","src、alt、width、height：四个都要写",[97,1714,1716],{"className":99,"code":1715,"language":65,"meta":101,"style":101},"\u003Cimg src=\"\u002Fimg\u002Fhero.webp\" alt=\"首页主图：三人协作场景\" width=\"1200\" height=\"600\" loading=\"lazy\">\n",[31,1717,1718],{"__ignoreMap":101},[105,1719,1720,1722,1725,1728,1730,1733,1736,1738,1741,1744,1746,1749,1752,1754,1757,1760,1762,1765],{"class":107,"line":108},[105,1721,129],{"class":111},[105,1723,1724],{"class":115},"img",[105,1726,1727],{"class":119}," src",[105,1729,137],{"class":111},[105,1731,1732],{"class":140},"\"\u002Fimg\u002Fhero.webp\"",[105,1734,1735],{"class":119}," alt",[105,1737,137],{"class":111},[105,1739,1740],{"class":140},"\"首页主图：三人协作场景\"",[105,1742,1743],{"class":119}," width",[105,1745,137],{"class":111},[105,1747,1748],{"class":140},"\"1200\"",[105,1750,1751],{"class":119}," height",[105,1753,137],{"class":111},[105,1755,1756],{"class":140},"\"600\"",[105,1758,1759],{"class":119}," loading",[105,1761,137],{"class":111},[105,1763,1764],{"class":140},"\"lazy\"",[105,1766,123],{"class":111},[257,1768,1769,1775,1789],{},[39,1770,1771,1774],{},[31,1772,1773],{},"src"," 必填，路径规则同第五章。",[39,1776,1777,1780,1781,1788],{},[31,1778,1779],{},"alt"," 必填（",[19,1782,1783,1784,1787],{},"装饰图写空字符串 ",[31,1785,1786],{},"alt=\"\"","，但不能省略属性","）。它同时是三样东西：屏幕阅读器的播报文本、图片加载失败时显示的兜底文字、搜索引擎理解图片的唯一线索。别写「图片」「photo of」这类前缀，播报本身已带「图像」提示。",[39,1790,1791,1061,1794,1797,1798,1801,1802,1805],{},[31,1792,1793],{},"width",[31,1795,1796],{},"height"," 写",[19,1799,1800],{},"图片固有尺寸","（不是最终显示尺寸）。浏览器据此预先算出宽高比并占好位置，图下载完页面不会「跳一下」——这个跳动会打断用户阅读，也是 Google 衡量的 CLS 指标的主要来源。显示尺寸想用 CSS 改，直接写 ",[31,1803,1804],{},"img { width: 100%; height: auto }","，比例仍由那两个属性提供。",[323,1807],{"height":1311,"src":1808,"title":1809},"\u002Ftutorials\u002Fhtml\u002Fdemos\u002F06-image.html","示例 06：img 的四个必填属性",[24,1811,1812],{"id":1812},"格式怎么选",[344,1814,1815,1828],{},[706,1816,1817],{},[709,1818,1819,1822,1825],{},[712,1820,1821],{},"格式",[712,1823,1824],{},"用在哪",[712,1826,1827],{},"注意",[722,1829,1830,1841,1852,1863,1874],{},[709,1831,1832,1835,1838],{},[727,1833,1834],{},"SVG",[727,1836,1837],{},"图标、logo、图表、需要无损缩放的图形",[727,1839,1840],{},"文本型内容清晰；不适合照片；来自第三方上传的 SVG 有 XSS 风险，别直接内联",[709,1842,1843,1846,1849],{},[727,1844,1845],{},"WebP \u002F AVIF",[727,1847,1848],{},"照片、大图首选",[727,1850,1851],{},"同画质体积比 JPEG 小约三成；AVIF 更小但编码慢",[709,1853,1854,1857,1860],{},[727,1855,1856],{},"JPEG",[727,1858,1859],{},"需要兼容兜底的照片",[727,1861,1862],{},"无透明通道",[709,1864,1865,1868,1871],{},[727,1866,1867],{},"PNG",[727,1869,1870],{},"需要透明通道、或纯色大面积截图",[727,1872,1873],{},"照片用 PNG 体积通常是 JPEG 的 3–5 倍",[709,1875,1876,1879,1882],{},[727,1877,1878],{},"GIF",[727,1880,1881],{},"基本别用",[727,1883,1884,1885,1888],{},"动图改用 ",[31,1886,1887],{},"\u003Cvideo muted loop playsinline>","，体积能小一个数量级",[15,1890,1891,1892,1895,1896,1899,1900,1903],{},"需要多格式降级时套 ",[31,1893,1894],{},"\u003Cpicture>","（和第十二章 ",[31,1897,1898],{},"\u003Cvideo>"," 的多 ",[31,1901,1902],{},"\u003Csource>"," 降级是同一思路）：",[97,1905,1907],{"className":99,"code":1906,"language":65,"meta":101,"style":101},"\u003Cpicture>\n  \u003Csource srcset=\"\u002Fimg\u002Fhero.avif\" type=\"image\u002Favif\">\n  \u003Csource srcset=\"\u002Fimg\u002Fhero.webp\" type=\"image\u002Fwebp\">\n  \u003Cimg src=\"\u002Fimg\u002Fhero.jpg\" alt=\"首页主图\" width=\"1200\" height=\"600\">\n\u003C\u002Fpicture>\n",[31,1908,1909,1918,1943,1964,1998],{"__ignoreMap":101},[105,1910,1911,1913,1916],{"class":107,"line":108},[105,1912,129],{"class":111},[105,1914,1915],{"class":115},"picture",[105,1917,123],{"class":111},[105,1919,1920,1922,1925,1928,1930,1933,1936,1938,1941],{"class":107,"line":126},[105,1921,149],{"class":111},[105,1923,1924],{"class":115},"source",[105,1926,1927],{"class":119}," srcset",[105,1929,137],{"class":111},[105,1931,1932],{"class":140},"\"\u002Fimg\u002Fhero.avif\"",[105,1934,1935],{"class":119}," type",[105,1937,137],{"class":111},[105,1939,1940],{"class":140},"\"image\u002Favif\"",[105,1942,123],{"class":111},[105,1944,1945,1947,1949,1951,1953,1955,1957,1959,1962],{"class":107,"line":146},[105,1946,149],{"class":111},[105,1948,1924],{"class":115},[105,1950,1927],{"class":119},[105,1952,137],{"class":111},[105,1954,1732],{"class":140},[105,1956,1935],{"class":119},[105,1958,137],{"class":111},[105,1960,1961],{"class":140},"\"image\u002Fwebp\"",[105,1963,123],{"class":111},[105,1965,1966,1968,1970,1972,1974,1977,1979,1981,1984,1986,1988,1990,1992,1994,1996],{"class":107,"line":157},[105,1967,149],{"class":111},[105,1969,1724],{"class":115},[105,1971,1727],{"class":119},[105,1973,137],{"class":111},[105,1975,1976],{"class":140},"\"\u002Fimg\u002Fhero.jpg\"",[105,1978,1735],{"class":119},[105,1980,137],{"class":111},[105,1982,1983],{"class":140},"\"首页主图\"",[105,1985,1743],{"class":119},[105,1987,137],{"class":111},[105,1989,1748],{"class":140},[105,1991,1751],{"class":119},[105,1993,137],{"class":111},[105,1995,1756],{"class":140},[105,1997,123],{"class":111},[105,1999,2000,2002,2004],{"class":107,"line":176},[105,2001,251],{"class":111},[105,2003,1915],{"class":115},[105,2005,123],{"class":111},[15,2007,2008,2009,2012,2013,2015,2016,2018,2019,2021,2022,2024],{},"浏览器从上往下挑第一个",[19,2010,2011],{},"支持","的 ",[31,2014,1902],{},"，都不支持时落到 ",[31,2017,391],{},"——",[31,2020,391],{}," 及其 ",[31,2023,1779],{}," 必须留在最后，否则降级路径和替代文本一起丢。",[24,2026,2027],{"id":2027},"懒加载与破图排查",[257,2029,2030,2044,2050],{},[39,2031,2032,2035,2036,2039,2040,2043],{},[31,2033,2034],{},"loading=\"lazy\"","：首屏外的图片等到接近视口才下载，长列表页收益明显。",[19,2037,2038],{},"首屏主图不要加","，反而应写 ",[31,2041,2042],{},"fetchpriority=\"high\"","，否则会被自己的懒加载推迟。",[39,2045,2046,2049],{},[31,2047,2048],{},"decoding=\"async\"","：让图片解码不阻塞其它内容渲染。",[39,2051,2052,2053,2055,2056,2058],{},"破图排查按顺序四步：① DevTools Network 看请求状态码；② 路径是否漏了部署前缀（本站在 ",[31,2054,1470],{}," 子路径下，写 ",[31,2057,1430],{}," 会打到域名根）；③ 文件真实扩展名与大小写是否一致（Linux 服务器区分大小写）；④ 本地双击能显示、部署后不显示 → 几乎一定是相对路径层级问题。",[10,2060,2062],{"id":2061},"七表格","七、表格",[15,2064,2065,2067,2068,2071],{},[31,2066,1352],{}," 用来呈现",[19,2069,2070],{},"需要横竖对照才能读懂的数据","。用它做页面布局是九十年代的遗产，今天由 Flex\u002FGrid 负责（见 CSS 教程第十、十一章）。",[24,2073,2074],{"id":2074},"六个结构标签",[97,2076,2078],{"className":99,"code":2077,"language":65,"meta":101,"style":101},"\u003Ctable>\n  \u003Ccaption>季度开销\u003C\u002Fcaption>\n  \u003Cthead>\u003Ctr>\u003Cth scope=\"col\">项目\u003C\u002Fth>\u003Cth scope=\"col\">金额\u003C\u002Fth>\u003C\u002Ftr>\u003C\u002Fthead>\n  \u003Ctbody>\u003Ctr>\u003Cth scope=\"row\">计算\u003C\u002Fth>\u003Ctd>1280\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\n  \u003Ctfoot>\u003Ctr>\u003Ctd>合计\u003C\u002Ftd>\u003Ctd>1950\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftfoot>\n\u003C\u002Ftable>\n",[31,2079,2080,2088,2102,2156,2201,2240],{"__ignoreMap":101},[105,2081,2082,2084,2086],{"class":107,"line":108},[105,2083,129],{"class":111},[105,2085,344],{"class":115},[105,2087,123],{"class":111},[105,2089,2090,2092,2095,2098,2100],{"class":107,"line":126},[105,2091,149],{"class":111},[105,2093,2094],{"class":115},"caption",[105,2096,2097],{"class":111},">季度开销\u003C\u002F",[105,2099,2094],{"class":115},[105,2101,123],{"class":111},[105,2103,2104,2106,2108,2111,2113,2115,2117,2120,2122,2125,2128,2130,2132,2134,2136,2138,2140,2143,2145,2148,2150,2152,2154],{"class":107,"line":146},[105,2105,149],{"class":111},[105,2107,706],{"class":115},[105,2109,2110],{"class":111},">\u003C",[105,2112,709],{"class":115},[105,2114,2110],{"class":111},[105,2116,712],{"class":115},[105,2118,2119],{"class":119}," scope",[105,2121,137],{"class":111},[105,2123,2124],{"class":140},"\"col\"",[105,2126,2127],{"class":111},">项目\u003C\u002F",[105,2129,712],{"class":115},[105,2131,2110],{"class":111},[105,2133,712],{"class":115},[105,2135,2119],{"class":119},[105,2137,137],{"class":111},[105,2139,2124],{"class":140},[105,2141,2142],{"class":111},">金额\u003C\u002F",[105,2144,712],{"class":115},[105,2146,2147],{"class":111},">\u003C\u002F",[105,2149,709],{"class":115},[105,2151,2147],{"class":111},[105,2153,706],{"class":115},[105,2155,123],{"class":111},[105,2157,2158,2160,2162,2164,2166,2168,2170,2172,2174,2177,2180,2182,2184,2186,2189,2191,2193,2195,2197,2199],{"class":107,"line":157},[105,2159,149],{"class":111},[105,2161,722],{"class":115},[105,2163,2110],{"class":111},[105,2165,709],{"class":115},[105,2167,2110],{"class":111},[105,2169,712],{"class":115},[105,2171,2119],{"class":119},[105,2173,137],{"class":111},[105,2175,2176],{"class":140},"\"row\"",[105,2178,2179],{"class":111},">计算\u003C\u002F",[105,2181,712],{"class":115},[105,2183,2110],{"class":111},[105,2185,727],{"class":115},[105,2187,2188],{"class":111},">1280\u003C\u002F",[105,2190,727],{"class":115},[105,2192,2147],{"class":111},[105,2194,709],{"class":115},[105,2196,2147],{"class":111},[105,2198,722],{"class":115},[105,2200,123],{"class":111},[105,2202,2203,2205,2208,2210,2212,2214,2216,2219,2221,2223,2225,2228,2230,2232,2234,2236,2238],{"class":107,"line":176},[105,2204,149],{"class":111},[105,2206,2207],{"class":115},"tfoot",[105,2209,2110],{"class":111},[105,2211,709],{"class":115},[105,2213,2110],{"class":111},[105,2215,727],{"class":115},[105,2217,2218],{"class":111},">合计\u003C\u002F",[105,2220,727],{"class":115},[105,2222,2110],{"class":111},[105,2224,727],{"class":115},[105,2226,2227],{"class":111},">1950\u003C\u002F",[105,2229,727],{"class":115},[105,2231,2147],{"class":111},[105,2233,709],{"class":115},[105,2235,2147],{"class":111},[105,2237,2207],{"class":115},[105,2239,123],{"class":111},[105,2241,2242,2244,2246],{"class":107,"line":191},[105,2243,251],{"class":111},[105,2245,344],{"class":115},[105,2247,123],{"class":111},[257,2249,2250,2267,2288],{},[39,2251,2252,2254,2255,2258,2259,2262,2263,2266],{},[31,2253,1352],{}," 容器；",[31,2256,2257],{},"\u003Ctr>"," 一行；",[31,2260,2261],{},"\u003Ctd>"," 数据格；",[31,2264,2265],{},"\u003Cth>"," 表头格。",[39,2268,2269,1061,2272,1061,2275,2278,2279,2287],{},[31,2270,2271],{},"\u003Cthead>",[31,2273,2274],{},"\u003Ctbody>",[31,2276,2277],{},"\u003Ctfoot>"," 把行分组。",[19,2280,2281,2283,2284,2286],{},[31,2282,2277],{}," 建议写在 ",[31,2285,2274],{}," 之后","，但浏览器会把它绘制在表格末尾——历史上为了让多页打印时表头每页重复才这么规定。",[39,2289,2290,2293,2294,2296,2297,612],{},[31,2291,2292],{},"\u003Ccaption>"," 是这张表的标题，必须紧跟 ",[31,2295,1352],{}," 之后、且是第一个子元素。它对读屏用户的价值相当于图片的 ",[31,2298,1779],{},[323,2300],{"height":2301,"src":2302,"title":2303},620,"\u002Ftutorials\u002Fhtml\u002Fdemos\u002F07-table.html","示例 07：表格的完整结构",[24,2305,2307],{"id":2306},"caption-与-th-scope让读屏知道这一格是什么","caption 与 th scope：让读屏知道这一格是什么",[15,2309,2310,2311,2314],{},"屏幕阅读器逐格朗读时，靠 ",[31,2312,2313],{},"scope"," 建立「这一格属于哪一行、哪一列」的对应关系：",[257,2316,2317,2327,2333],{},[39,2318,2319,2320,2323,2324,612],{},"列表头写 ",[31,2321,2322],{},"\u003Cth scope=\"col\">","，行表头写 ",[31,2325,2326],{},"\u003Cth scope=\"row\">",[39,2328,2329,2330,2332],{},"没有 ",[31,2331,2313],{},"，用户念到「512」时只听得到一个孤立数字；有了它，播报是「存储，8 月，512」。",[39,2334,2335,2336,2339,2340,612],{},"复杂表头（一格横跨多列）用 ",[31,2337,2338],{},"headers=\"格 id 列表\""," 显式关联，配合 ",[31,2341,2342],{},"\u003Cth id=\"...\">",[15,2344,2345,2347,2348,2351],{},[31,2346,2261],{}," 也能当行标题用吗？可以（第一列写成 ",[31,2349,2350],{},"\u003Ctd>计算\u003C\u002Ftd>","），但它就失去了表头语义，读屏不会把它作为对应关系播报——这是最常见的表格可访问性缺失。",[24,2353,2355],{"id":2354},"colspan-与-rowspan","colspan 与 rowspan",[15,2357,2358,2359,2368,2369,2372],{},"合并单元格只有一条铁律：",[19,2360,2361,1061,2364,2367],{},[31,2362,2363],{},"colspan",[31,2365,2366],{},"rowspan"," 写在被合并的第一个格子上，被它盖住的格子要从源码里删掉","，不能留空 ",[31,2370,2371],{},"\u003Ctd>\u003C\u002Ftd>","，否则行列整体错位。",[97,2374,2376],{"className":99,"code":2375,"language":65,"meta":101,"style":101},"\u003Ctr>\n  \u003Cth scope=\"row\" rowspan=\"2\">Nuxt\u003C\u002Fth>\n  \u003Ctd>3.12\u003C\u002Ftd>\u003Ctd>2024-07\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n  \u003C!-- 这里没有第一个格子，被上面的 rowspan 占了 -->\n  \u003Ctd>4.0\u003C\u002Ftd>\u003Ctd>2025-07\u003C\u002Ftd>\n\u003C\u002Ftr>\n",[31,2377,2378,2386,2413,2435,2443,2451,2457,2479],{"__ignoreMap":101},[105,2379,2380,2382,2384],{"class":107,"line":108},[105,2381,129],{"class":111},[105,2383,709],{"class":115},[105,2385,123],{"class":111},[105,2387,2388,2390,2392,2394,2396,2398,2401,2403,2406,2409,2411],{"class":107,"line":126},[105,2389,149],{"class":111},[105,2391,712],{"class":115},[105,2393,2119],{"class":119},[105,2395,137],{"class":111},[105,2397,2176],{"class":140},[105,2399,2400],{"class":119}," rowspan",[105,2402,137],{"class":111},[105,2404,2405],{"class":140},"\"2\"",[105,2407,2408],{"class":111},">Nuxt\u003C\u002F",[105,2410,712],{"class":115},[105,2412,123],{"class":111},[105,2414,2415,2417,2419,2422,2424,2426,2428,2431,2433],{"class":107,"line":146},[105,2416,149],{"class":111},[105,2418,727],{"class":115},[105,2420,2421],{"class":111},">3.12\u003C\u002F",[105,2423,727],{"class":115},[105,2425,2110],{"class":111},[105,2427,727],{"class":115},[105,2429,2430],{"class":111},">2024-07\u003C\u002F",[105,2432,727],{"class":115},[105,2434,123],{"class":111},[105,2436,2437,2439,2441],{"class":107,"line":157},[105,2438,251],{"class":111},[105,2440,709],{"class":115},[105,2442,123],{"class":111},[105,2444,2445,2447,2449],{"class":107,"line":176},[105,2446,129],{"class":111},[105,2448,709],{"class":115},[105,2450,123],{"class":111},[105,2452,2453],{"class":107,"line":191},[105,2454,2456],{"class":2455},"sAwPA","  \u003C!-- 这里没有第一个格子，被上面的 rowspan 占了 -->\n",[105,2458,2459,2461,2463,2466,2468,2470,2472,2475,2477],{"class":107,"line":201},[105,2460,149],{"class":111},[105,2462,727],{"class":115},[105,2464,2465],{"class":111},">4.0\u003C\u002F",[105,2467,727],{"class":115},[105,2469,2110],{"class":111},[105,2471,727],{"class":115},[105,2473,2474],{"class":111},">2025-07\u003C\u002F",[105,2476,727],{"class":115},[105,2478,123],{"class":111},[105,2480,2481,2483,2485],{"class":107,"line":211},[105,2482,251],{"class":111},[105,2484,709],{"class":115},[105,2486,123],{"class":111},[24,2488,2489],{"id":2489},"宽表格与滚动",[15,2491,2492],{},"窄屏上表格溢出会撑破整页布局。处理办法是外套一层横向滚动容器，而不是把字号压到 10px：",[97,2494,2496],{"className":99,"code":2495,"language":65,"meta":101,"style":101},"\u003Cdiv style=\"overflow-x:auto\">\u003Ctable>…\u003C\u002Ftable>\u003C\u002Fdiv>\n",[31,2497,2498],{"__ignoreMap":101},[105,2499,2500,2502,2505,2508,2510,2513,2515,2517,2520,2522,2524,2526],{"class":107,"line":108},[105,2501,129],{"class":111},[105,2503,2504],{"class":115},"div",[105,2506,2507],{"class":119}," style",[105,2509,137],{"class":111},[105,2511,2512],{"class":140},"\"overflow-x:auto\"",[105,2514,2110],{"class":111},[105,2516,344],{"class":115},[105,2518,2519],{"class":111},">…\u003C\u002F",[105,2521,344],{"class":115},[105,2523,2147],{"class":111},[105,2525,2504],{"class":115},[105,2527,123],{"class":111},[15,2529,2530],{},"数据量很大时考虑改成「小屏幕用卡片列表、大屏幕用表格」两套结构按媒体查询切换——这属于 CSS 教程第十三章的响应式范畴。",[10,2532,2534],{"id":2533},"八divspan-与-classid","八、div、span 与 class、id",[15,2536,2537,2540,2541,2544,2545,2548,2549],{},[31,2538,2539],{},"\u003Cdiv>"," 和 ",[31,2542,2543],{},"\u003Cspan>"," 是「无内容的容器」：它们不表达任何含义，唯一作用是",[19,2546,2547],{},"把一组元素圈起来，好让 CSS 或 JS 找到这一块","。所以判断标准始终是：",[19,2550,2551],{},"有语义更准的标签吗？有就别用 div。",[24,2553,2555],{"id":2554},"块级与行内默认排版行为","块级与行内：默认排版行为",[344,2557,2558,2570],{},[706,2559,2560],{},[709,2561,2562,2564,2567],{},[712,2563],{},[712,2565,2566],{},"块级（block）",[712,2568,2569],{},"行内（inline）",[722,2571,2572,2618,2629,2640],{},[709,2573,2574,2577,2601],{},[727,2575,2576],{},"代表",[727,2578,2579,355,2581,355,2583,2585,2586,355,2589,355,2591,355,2593,355,2596,355,2598],{},[31,2580,2504],{},[31,2582,15],{},[31,2584,216],{},"–",[31,2587,2588],{},"h6",[31,2590,257],{},[31,2592,39],{},[31,2594,2595],{},"section",[31,2597,344],{},[31,2599,2600],{},"form",[727,2602,2603,355,2605,355,2607,355,2609,355,2611,355,2613,355,2615],{},[31,2604,105],{},[31,2606,1519],{},[31,2608,19],{},[31,2610,1006],{},[31,2612,31],{},[31,2614,1724],{},[31,2616,2617],{},"small",[709,2619,2620,2623,2626],{},[727,2621,2622],{},"占宽",[727,2624,2625],{},"独占一行，撑满可用宽度",[727,2627,2628],{},"只包住内容宽度",[709,2630,2631,2634,2637],{},[727,2632,2633],{},"宽高\u002Fmargin",[727,2635,2636],{},"可设宽高，上下 margin 生效",[727,2638,2639],{},"宽高无效，上下 margin 不生效（padding 生效但会溢出）",[709,2641,2642,2645,2648],{},[727,2643,2644],{},"能包含",[727,2646,2647],{},"可包含块级和行内",[727,2649,2650,2651,355,2653,2655],{},"默认只能放行内（",[31,2652,1345],{},[31,2654,1697],{}," 例外）",[15,2657,2658,2659,2662,2663,2666,2667,355,2670,355,2673,2676],{},"「块级和行内」是",[19,2660,2661],{},"默认显示行为","，不是标签的本质属性——CSS 的 ",[31,2664,2665],{},"display"," 可以互相转换（",[31,2668,2669],{},"display: inline-block",[31,2671,2672],{},"display: flex",[31,2674,2675],{},"display: grid","），这在 CSS 教程第九章专门讲。",[323,2678],{"height":1311,"src":2679,"title":2680},"\u002Ftutorials\u002Fhtml\u002Fdemos\u002F08-div-span.html","示例 08：块级与行内、class 与 id",[24,2682,2684],{"id":2683},"class-与-id-的边界","class 与 id 的边界",[257,2686,2687,2710,2723,2733,2743],{},[39,2688,2689,2694,2695,2698,2699,1061,2702,2705,2706,2709],{},[19,2690,2691,2693],{},[31,2692,1481],{}," 在整篇文档内必须唯一","，合法用途只有三类：锚点跳转目标（",[31,2696,2697],{},"#id","）、被 ",[31,2700,2701],{},"for",[31,2703,2704],{},"aria-labelledby"," 指向、被脚本 ",[31,2707,2708],{},"getElementById"," 抓取。",[39,2711,2712,2718,2719,2722],{},[19,2713,2714,2715,2717],{},"不要用 ",[31,2716,1481],{}," 写 CSS 选择器","。它的优先级是 100，远高于 class（10），后面临时覆盖只能靠 ",[31,2720,2721],{},"!important","，等于把样式的可维护性提前透支。",[39,2724,2725,2728,2729,2732],{},[31,2726,2727],{},"class"," 可以重复使用，也可以一个元素挂多个：",[31,2730,2731],{},"class=\"btn btn--primary is-disabled\"","——基础样式 + 变体 + 状态分开，本站所有组件都用这个命名法（BEM 风格）。",[39,2734,2735,2736,2739,2740,612],{},"命名带用途而不是带外观：",[31,2737,2738],{},"is-error"," 好于 ",[31,2741,2742],{},"red-text",[39,2744,2745,2751,2752,2755,2756,290,2759,2762],{},[19,2746,2747,2748,2750],{},"重复 ",[31,2749,1481],{}," 浏览器不报错","，但锚点和 ",[31,2753,2754],{},"label"," 绑定只命中第一个，属于最难排查的一类 bug。用 DevTools Console 执行 ",[31,2757,2758],{},"document.querySelectorAll('[id]').length",[31,2760,2761],{},"[...document.querySelectorAll('[id]')].map(e => e.id)"," 排序对比，可以一眼看到重复。",[24,2764,2765],{"id":2765},"嵌套规则与缩进",[15,2767,2768,2769,2772,2773,2776],{},"后开的标签必须先关（",[31,2770,2771],{},"\u003Cb>\u003Ci>文字\u003C\u002Fi>\u003C\u002Fb>","），交叉嵌套 ",[31,2774,2775],{},"\u003Cb>\u003Ci>文字\u003C\u002Fb>\u003C\u002Fi>"," 是非法的，浏览器会强行「就近闭合」，结果与你的预期完全不同。",[15,2778,2779],{},"三条实操约定：",[36,2781,2782,2789,2792],{},[39,2783,2784,2785,2788],{},"缩进体现层级，两个空格一档；VS Code 里 ",[31,2786,2787],{},"Shift+Alt+F"," 自动格式化整份 HTML。",[39,2790,2791],{},"块级里放块级、行内里放行内，是唯一安全的默认策略。",[39,2793,2794,2796,2797,355,2799,355,2801,2803,2804,2806],{},[31,2795,57],{}," 里不能再放 ",[31,2798,2539],{},[31,2800,57],{},[31,2802,1121],{}," 等块级元素——浏览器会自动把 ",[31,2805,57],{}," 提前闭合，导致结构意外分裂（第十八章排错清单里它是常客）。",[10,2808,2810],{"id":2809},"九语义化结构与布局标签","九、语义化结构与布局标签",[24,2812,2813],{"id":2813},"七个地标标签",[344,2815,2816,2827],{},[706,2817,2818],{},[709,2819,2820,2822,2824],{},[712,2821,921],{},[712,2823,924],{},[712,2825,2826],{},"数量约定",[722,2828,2829,2842,2854,2869,2882,2895,2907],{},[709,2830,2831,2836,2839],{},[727,2832,2833],{},[31,2834,2835],{},"\u003Cheader>",[727,2837,2838],{},"区块的介绍性内容（站点头、文章头）",[727,2840,2841],{},"每个区块各一个",[709,2843,2844,2848,2851],{},[727,2845,2846],{},[31,2847,1337],{},[727,2849,2850],{},"主导航区块",[727,2852,2853],{},"站内导航各一个",[709,2855,2856,2861,2864],{},[727,2857,2858],{},[31,2859,2860],{},"\u003Cmain>",[727,2862,2863],{},"页面主内容",[727,2865,2866],{},[19,2867,2868],{},"整页一个",[709,2870,2871,2876,2879],{},[727,2872,2873],{},[31,2874,2875],{},"\u003Csection>",[727,2877,2878],{},"需要标题的一个分区",[727,2880,2881],{},"任意，但每个都该有标题",[709,2883,2884,2889,2892],{},[727,2885,2886],{},[31,2887,2888],{},"\u003Carticle>",[727,2890,2891],{},"可独立分发的一段内容（博文、评论、卡片）",[727,2893,2894],{},"任意",[709,2896,2897,2902,2905],{},[727,2898,2899],{},[31,2900,2901],{},"\u003Caside>",[727,2903,2904],{},"与主内容相关但可剥离（侧栏、相关推荐）",[727,2906,2894],{},[709,2908,2909,2914,2917],{},[727,2910,2911],{},[31,2912,2913],{},"\u003Cfooter>",[727,2915,2916],{},"区块的收尾内容",[727,2918,2841],{},[15,2920,2921,2922,270,2925,2927,2928,2930,2931,2540,2933,2935,2936,2938],{},"唯一性说的是",[19,2923,2924],{},"嵌套层级",[31,2926,2860],{}," 整页只有一个；但 ",[31,2929,2888],{}," 内部的 ",[31,2932,2835],{},[31,2934,2913],{}," 与页面的 ",[31,2937,2835],{}," 不冲突，它们属于各自父区块。",[323,2940],{"height":906,"src":2941,"title":2942},"\u002Ftutorials\u002Fhtml\u002Fdemos\u002F09-semantic-layout.html","示例 09：语义化页面骨架",[24,2944,2945],{"id":2945},"整页骨架",[15,2947,2948],{},"一个可直接复制的起点，不含任何样式：",[97,2950,2952],{"className":99,"code":2951,"language":65,"meta":101,"style":101},"\u003Cbody>\n  \u003Cheader>\n    \u003Ch1>站点名\u003C\u002Fh1>\n    \u003Cnav>\u003Cul>\u003Cli>\u003Ca href=\"\u002F\">首页\u003C\u002Fa>\u003C\u002Fli>\u003C\u002Ful>\u003C\u002Fnav>\n  \u003C\u002Fheader>\n\n  \u003Cmain>\n    \u003Carticle>\n      \u003Cheader>\u003Ch2>文章标题\u003C\u002Fh2>\u003Cp>\u003Ctime datetime=\"2026-10-03\">10 月 3 日\u003C\u002Ftime>\u003C\u002Fp>\u003C\u002Fheader>\n      \u003Csection>\u003Ch3>小节\u003C\u002Fh3>\u003Cp>正文……\u003C\u002Fp>\u003C\u002Fsection>\n      \u003Cfooter>\u003Cp>标签、作者\u003C\u002Fp>\u003C\u002Ffooter>\n    \u003C\u002Farticle>\n  \u003C\u002Fmain>\n\n  \u003Caside>\u003Ch2>相关推荐\u003C\u002Fh2>\u003Cul>\u003Cli>\u003Ca href=\"#\">另一篇\u003C\u002Fa>\u003C\u002Fli>\u003C\u002Ful>\u003C\u002Faside>\n\n  \u003Cfooter>\u003Cp>版权信息\u003C\u002Fp>\u003C\u002Ffooter>\n\u003C\u002Fbody>\n",[31,2953,2954,2962,2971,2984,3029,3037,3043,3052,3061,3108,3138,3160,3169,3178,3183,3238,3243,3265],{"__ignoreMap":101},[105,2955,2956,2958,2960],{"class":107,"line":108},[105,2957,129],{"class":111},[105,2959,206],{"class":115},[105,2961,123],{"class":111},[105,2963,2964,2966,2969],{"class":107,"line":126},[105,2965,149],{"class":111},[105,2967,2968],{"class":115},"header",[105,2970,123],{"class":111},[105,2972,2973,2975,2977,2980,2982],{"class":107,"line":146},[105,2974,160],{"class":111},[105,2976,216],{"class":115},[105,2978,2979],{"class":111},">站点名\u003C\u002F",[105,2981,216],{"class":115},[105,2983,123],{"class":111},[105,2985,2986,2988,2991,2993,2995,2997,2999,3001,3003,3005,3007,3010,3013,3015,3017,3019,3021,3023,3025,3027],{"class":107,"line":157},[105,2987,160],{"class":111},[105,2989,2990],{"class":115},"nav",[105,2992,2110],{"class":111},[105,2994,257],{"class":115},[105,2996,2110],{"class":111},[105,2998,39],{"class":115},[105,3000,2110],{"class":111},[105,3002,1519],{"class":115},[105,3004,528],{"class":119},[105,3006,137],{"class":111},[105,3008,3009],{"class":140},"\"\u002F\"",[105,3011,3012],{"class":111},">首页\u003C\u002F",[105,3014,1519],{"class":115},[105,3016,2147],{"class":111},[105,3018,39],{"class":115},[105,3020,2147],{"class":111},[105,3022,257],{"class":115},[105,3024,2147],{"class":111},[105,3026,2990],{"class":115},[105,3028,123],{"class":111},[105,3030,3031,3033,3035],{"class":107,"line":176},[105,3032,194],{"class":111},[105,3034,2968],{"class":115},[105,3036,123],{"class":111},[105,3038,3039],{"class":107,"line":191},[105,3040,3042],{"emptyLinePlaceholder":3041},true,"\n",[105,3044,3045,3047,3050],{"class":107,"line":201},[105,3046,149],{"class":111},[105,3048,3049],{"class":115},"main",[105,3051,123],{"class":111},[105,3053,3054,3056,3059],{"class":107,"line":211},[105,3055,160],{"class":111},[105,3057,3058],{"class":115},"article",[105,3060,123],{"class":111},[105,3062,3063,3065,3067,3069,3071,3074,3076,3078,3080,3082,3085,3088,3090,3093,3096,3098,3100,3102,3104,3106],{"class":107,"line":225},[105,3064,1222],{"class":111},[105,3066,2968],{"class":115},[105,3068,2110],{"class":111},[105,3070,10],{"class":115},[105,3072,3073],{"class":111},">文章标题\u003C\u002F",[105,3075,10],{"class":115},[105,3077,2110],{"class":111},[105,3079,15],{"class":115},[105,3081,2110],{"class":111},[105,3083,3084],{"class":115},"time",[105,3086,3087],{"class":119}," datetime",[105,3089,137],{"class":111},[105,3091,3092],{"class":140},"\"2026-10-03\"",[105,3094,3095],{"class":111},">10 月 3 日\u003C\u002F",[105,3097,3084],{"class":115},[105,3099,2147],{"class":111},[105,3101,15],{"class":115},[105,3103,2147],{"class":111},[105,3105,2968],{"class":115},[105,3107,123],{"class":111},[105,3109,3110,3112,3114,3116,3118,3121,3123,3125,3127,3130,3132,3134,3136],{"class":107,"line":239},[105,3111,1222],{"class":111},[105,3113,2595],{"class":115},[105,3115,2110],{"class":111},[105,3117,24],{"class":115},[105,3119,3120],{"class":111},">小节\u003C\u002F",[105,3122,24],{"class":115},[105,3124,2110],{"class":111},[105,3126,15],{"class":115},[105,3128,3129],{"class":111},">正文……\u003C\u002F",[105,3131,15],{"class":115},[105,3133,2147],{"class":111},[105,3135,2595],{"class":115},[105,3137,123],{"class":111},[105,3139,3140,3142,3145,3147,3149,3152,3154,3156,3158],{"class":107,"line":248},[105,3141,1222],{"class":111},[105,3143,3144],{"class":115},"footer",[105,3146,2110],{"class":111},[105,3148,15],{"class":115},[105,3150,3151],{"class":111},">标签、作者\u003C\u002F",[105,3153,15],{"class":115},[105,3155,2147],{"class":111},[105,3157,3144],{"class":115},[105,3159,123],{"class":111},[105,3161,3163,3165,3167],{"class":107,"line":3162},12,[105,3164,1236],{"class":111},[105,3166,3058],{"class":115},[105,3168,123],{"class":111},[105,3170,3172,3174,3176],{"class":107,"line":3171},13,[105,3173,194],{"class":111},[105,3175,3049],{"class":115},[105,3177,123],{"class":111},[105,3179,3181],{"class":107,"line":3180},14,[105,3182,3042],{"emptyLinePlaceholder":3041},[105,3184,3186,3188,3191,3193,3195,3198,3200,3202,3204,3206,3208,3210,3212,3214,3216,3219,3222,3224,3226,3228,3230,3232,3234,3236],{"class":107,"line":3185},15,[105,3187,149],{"class":111},[105,3189,3190],{"class":115},"aside",[105,3192,2110],{"class":111},[105,3194,10],{"class":115},[105,3196,3197],{"class":111},">相关推荐\u003C\u002F",[105,3199,10],{"class":115},[105,3201,2110],{"class":111},[105,3203,257],{"class":115},[105,3205,2110],{"class":111},[105,3207,39],{"class":115},[105,3209,2110],{"class":111},[105,3211,1519],{"class":115},[105,3213,528],{"class":119},[105,3215,137],{"class":111},[105,3217,3218],{"class":140},"\"#\"",[105,3220,3221],{"class":111},">另一篇\u003C\u002F",[105,3223,1519],{"class":115},[105,3225,2147],{"class":111},[105,3227,39],{"class":115},[105,3229,2147],{"class":111},[105,3231,257],{"class":115},[105,3233,2147],{"class":111},[105,3235,3190],{"class":115},[105,3237,123],{"class":111},[105,3239,3241],{"class":107,"line":3240},16,[105,3242,3042],{"emptyLinePlaceholder":3041},[105,3244,3246,3248,3250,3252,3254,3257,3259,3261,3263],{"class":107,"line":3245},17,[105,3247,149],{"class":111},[105,3249,3144],{"class":115},[105,3251,2110],{"class":111},[105,3253,15],{"class":115},[105,3255,3256],{"class":111},">版权信息\u003C\u002F",[105,3258,15],{"class":115},[105,3260,2147],{"class":111},[105,3262,3144],{"class":115},[105,3264,123],{"class":111},[105,3266,3268,3270,3272],{"class":107,"line":3267},18,[105,3269,251],{"class":111},[105,3271,206],{"class":115},[105,3273,123],{"class":111},[15,3275,3276,3278,3279,3281],{},[31,3277,2901],{}," 放在 ",[31,3280,2860],{}," 外面还是里面，取决于它是不是「主内容的一部分」：相关推荐属于某篇文章就放进去，全站侧栏就放外面。",[24,3283,3285],{"id":3284},"为什么不能-div-全天下","为什么不能 div 全天下",[15,3287,3288,3289,3291],{},"同样一个页面，全用 ",[31,3290,2539],{}," 视觉上毫无区别，但你会丢掉三样东西：",[36,3293,3294,3300,3311],{},[39,3295,3296,3299],{},[19,3297,3298],{},"辅助技术的地标导航","。读屏软件（NVDA\u002FJAWS\u002FVoiceOver）有「地标列表」，用户靠它在导航、主内容、侧栏之间一键跳转。全是 div 时这个列表是空的，用户只能从头听到尾。",[39,3301,3302,3305,3306,1338,3308,3310],{},[19,3303,3304],{},"机器可读的结构","。搜索引擎、微信\u002F微博的抓取器、浏览器的「阅读模式」都靠标签推断正文。",[31,3307,2888],{},[31,3309,2860],{}," 的页面被正确抽取的概率明显更高。",[39,3312,3313,3316,3317,2540,3320,3323],{},[19,3314,3315],{},"可读的源码","。半年后回来看 ",[31,3318,3319],{},"\u003Carticle class=\"post-card\">",[31,3321,3322],{},"\u003Cdiv class=\"d3\">","，维护成本不是一个量级。",[15,3325,3326,3327,3329,3330,3332],{},"反过来说，语义标签不是越多越好：给一个既无标题也不能独立分发的包裹层硬套 ",[31,3328,2875],{},"，比 ",[31,3331,2539],{}," 更糟——你对读屏用户许下了一个兑现不了的承诺。",[24,3334,3336],{"id":3335},"什么时候仍然该用-div","什么时候仍然该用 div",[15,3338,3339,3340,3343],{},"三种情况：纯样式包裹层（为了 flex 布局多套一层）、组件的根容器、以及",[19,3341,3342],{},"找不到对应语义标签的抽象分组","。判断顺序永远是：先找语义标签 → 再找列表\u002F表格\u002F表单结构标签 → 最后才是 div\u002Fspan。",[10,3345,3347],{"id":3346},"十表单-form-与常见-input","十、表单 form 与常见 input",[15,3349,3350,3351,3354,3355,355,3358,3360,3361,3364,3365,612],{},"表单是页面把数据交给服务器的唯一标准通道。它的问题通常不在标签语法，而在",[19,3352,3353],{},"三个容易被忽略的细节","：控件没写 ",[31,3356,3357],{},"name",[31,3359,2754],{}," 没绑定、",[31,3362,3363],{},"type"," 用了默认的 ",[31,3366,3367],{},"text",[24,3369,3371],{"id":3370},"formactionmethod数据发去哪怎么发","form、action、method：数据发去哪、怎么发",[97,3373,3375],{"className":99,"code":3374,"language":65,"meta":101,"style":101},"\u003Cform action=\"\u002Fapi\u002Flogin\" method=\"post\" enctype=\"multipart\u002Fform-data\">\n  \u003C!-- 控件 -->\n  \u003Cbutton type=\"submit\">登录\u003C\u002Fbutton>\n\u003C\u002Fform>\n",[31,3376,3377,3409,3414,3435],{"__ignoreMap":101},[105,3378,3379,3381,3383,3386,3388,3391,3394,3396,3399,3402,3404,3407],{"class":107,"line":108},[105,3380,129],{"class":111},[105,3382,2600],{"class":115},[105,3384,3385],{"class":119}," action",[105,3387,137],{"class":111},[105,3389,3390],{"class":140},"\"\u002Fapi\u002Flogin\"",[105,3392,3393],{"class":119}," method",[105,3395,137],{"class":111},[105,3397,3398],{"class":140},"\"post\"",[105,3400,3401],{"class":119}," enctype",[105,3403,137],{"class":111},[105,3405,3406],{"class":140},"\"multipart\u002Fform-data\"",[105,3408,123],{"class":111},[105,3410,3411],{"class":107,"line":126},[105,3412,3413],{"class":2455},"  \u003C!-- 控件 -->\n",[105,3415,3416,3418,3421,3423,3425,3428,3431,3433],{"class":107,"line":146},[105,3417,149],{"class":111},[105,3419,3420],{"class":115},"button",[105,3422,1935],{"class":119},[105,3424,137],{"class":111},[105,3426,3427],{"class":140},"\"submit\"",[105,3429,3430],{"class":111},">登录\u003C\u002F",[105,3432,3420],{"class":115},[105,3434,123],{"class":111},[105,3436,3437,3439,3441],{"class":107,"line":157},[105,3438,251],{"class":111},[105,3440,2600],{"class":115},[105,3442,123],{"class":111},[257,3444,3445,3451,3468,3481,3495],{},[39,3446,3447,3450],{},[31,3448,3449],{},"action","：接收数据的地址，省略时提交到当前页面 URL。",[39,3452,3453,270,3456,3459,3460,3463,3464,3467],{},[31,3454,3455],{},"method",[31,3457,3458],{},"get"," 把数据拼在 URL 问号后（进浏览器历史、进服务器日志、长度受限），只用于搜索\u002F筛选这类",[19,3461,3462],{},"可分享、无副作用","的查询；",[31,3465,3466],{},"post"," 把数据放请求体里，登录、写入、上传一律用它。",[39,3469,3470,3473,3474,3477,3478,612],{},[31,3471,3472],{},"enctype","：默认 ",[31,3475,3476],{},"application\u002Fx-www-form-urlencoded","；上传文件必须改成 ",[31,3479,3480],{},"multipart\u002Fform-data",[39,3482,3483,612,3489,3491,3492,3494],{},[19,3484,3485,3486,3488],{},"只有带 ",[31,3487,3357],{}," 的控件才会被发送",[31,3490,1481],{}," 是给 ",[31,3493,2754],{}," 和 JS 用的，与提交无关——这是「后端说没收到数据」的第一大原因。",[39,3496,3497,3499,3500,3503,3504,3507],{},[31,3498,1697],{}," 在表单内默认就是 ",[31,3501,3502],{},"type=\"submit\"","。不想提交的按钮（清空、切换显示）必须显式写 ",[31,3505,3506],{},"type=\"button\"","，否则一点就触发提交。",[323,3509],{"height":1311,"src":3510,"title":3511},"\u002Ftutorials\u002Fhtml\u002Fdemos\u002F10a-form-basic.html","示例 10a：表单结构与提交内容",[24,3513,3515],{"id":3514},"input-的-type-决定一切","input 的 type 决定一切",[15,3517,3518,3520,3521,612],{},[31,3519,3363],{}," 同时决定三件事：",[19,3522,3523],{},"用什么控件呈现、手机弹出什么键盘、浏览器按哪套规则校验",[344,3525,3526,3538],{},[706,3527,3528],{},[709,3529,3530,3532,3535],{},[712,3531,3363],{},[712,3533,3534],{},"用途",[712,3536,3537],{},"附带校验",[722,3539,3540,3552,3565,3585,3607,3627,3640,3652,3669,3687,3700,3721,3738],{},[709,3541,3542,3546,3549],{},[727,3543,3544],{},[31,3545,3367],{},[727,3547,3548],{},"默认值，单行文本",[727,3550,3551],{},"无",[709,3553,3554,3559,3562],{},[727,3555,3556],{},[31,3557,3558],{},"email",[727,3560,3561],{},"邮箱",[727,3563,3564],{},"基本格式",[709,3566,3567,3572,3575],{},[727,3568,3569],{},[31,3570,3571],{},"password",[727,3573,3574],{},"密码",[727,3576,3577,3578,1061,3581,3584],{},"无（配 ",[31,3579,3580],{},"autocomplete=\"new-password\"",[31,3582,3583],{},"\"current-password\"","）",[709,3586,3587,3592,3595],{},[727,3588,3589],{},[31,3590,3591],{},"number",[727,3593,3594],{},"数字",[727,3596,3597,3598,1003,3601,1003,3604],{},"数字 + ",[31,3599,3600],{},"min",[31,3602,3603],{},"max",[31,3605,3606],{},"step",[709,3608,3609,3614,3617],{},[727,3610,3611],{},[31,3612,3613],{},"tel",[727,3615,3616],{},"电话",[727,3618,3619,3622,3623,3626],{},[19,3620,3621],{},"不校验","，配 ",[31,3624,3625],{},"inputmode=\"numeric\""," 换数字键盘",[709,3628,3629,3634,3637],{},[727,3630,3631],{},[31,3632,3633],{},"url",[727,3635,3636],{},"网址",[727,3638,3639],{},"需含协议名",[709,3641,3642,3647,3650],{},[727,3643,3644],{},[31,3645,3646],{},"search",[727,3648,3649],{},"搜索框",[727,3651,3551],{},[709,3653,3654,3664,3667],{},[727,3655,3656,1061,3659,1061,3661],{},[31,3657,3658],{},"date",[31,3660,3084],{},[31,3662,3663],{},"datetime-local",[727,3665,3666],{},"日期时间",[727,3668,1821],{},[709,3670,3671,3676,3679],{},[727,3672,3673],{},[31,3674,3675],{},"range",[727,3677,3678],{},"滑块",[727,3680,3681,1003,3683,1003,3685],{},[31,3682,3600],{},[31,3684,3603],{},[31,3686,3606],{},[709,3688,3689,3694,3697],{},[727,3690,3691],{},[31,3692,3693],{},"color",[727,3695,3696],{},"取色器",[727,3698,3699],{},"十六进制色值",[709,3701,3702,3710,3713],{},[727,3703,3704,1061,3707],{},[31,3705,3706],{},"checkbox",[31,3708,3709],{},"radio",[727,3711,3712],{},"多选 \u002F 单选",[727,3714,3715,3716,3718,3719],{},"同组 ",[31,3717,3709],{}," 必须同名 ",[31,3720,3357],{},[709,3722,3723,3728,3731],{},[727,3724,3725],{},[31,3726,3727],{},"file",[727,3729,3730],{},"文件",[727,3732,3733,3734,3737],{},"配 ",[31,3735,3736],{},"accept"," 筛选类型",[709,3739,3740,3745,3748],{},[727,3741,3742],{},[31,3743,3744],{},"hidden",[727,3746,3747],{},"随表单提交的不可见数据（如 CSRF token）",[727,3749,3551],{},[323,3751],{"height":2301,"src":3752,"title":3753},"\u002Ftutorials\u002Fhtml\u002Fdemos\u002F10b-input-types.html","示例 10b：input 类型速查",[15,3755,3756],{},"两个高频纠正：",[36,3758,3759,3770],{},[39,3760,3761,1179,3764,3769],{},[31,3762,3763],{},"placeholder",[19,3765,3766,3767],{},"不能替代 ",[31,3768,2754],{},"：它一输入就消失、对比度常常不达标、翻译后易截断。",[39,3771,3772,3773,3776,3777,3780,3781,3784,3785,3788,3789,3792],{},"读写控件的值要用",[19,3774,3775],{},"属性","而不是特征：",[31,3778,3779],{},"el.value","（复选框 ",[31,3782,3783],{},"el.checked","、文件 ",[31,3786,3787],{},"el.files","）。改 HTML 的 ",[31,3790,3791],{},"value"," 特征只改「初始值」，用户已输入的内容不受影响。",[24,3794,3796],{"id":3795},"label-的正确绑法","label 的正确绑法",[97,3798,3800],{"className":99,"code":3799,"language":65,"meta":101,"style":101},"\u003C!-- 显式绑定：for 与 id 成对，推荐 -->\n\u003Clabel for=\"email\">邮箱\u003C\u002Flabel>\n\u003Cinput id=\"email\" name=\"email\" type=\"email\">\n\n\u003C!-- 隐式包裹：合法，但标题和控件被拆散时不好维护 -->\n\u003Clabel>邮箱 \u003Cinput name=\"email\" type=\"email\">\u003C\u002Flabel>\n",[31,3801,3802,3807,3828,3855,3859,3864],{"__ignoreMap":101},[105,3803,3804],{"class":107,"line":108},[105,3805,3806],{"class":2455},"\u003C!-- 显式绑定：for 与 id 成对，推荐 -->\n",[105,3808,3809,3811,3813,3816,3818,3821,3824,3826],{"class":107,"line":126},[105,3810,129],{"class":111},[105,3812,2754],{"class":115},[105,3814,3815],{"class":119}," for",[105,3817,137],{"class":111},[105,3819,3820],{"class":140},"\"email\"",[105,3822,3823],{"class":111},">邮箱\u003C\u002F",[105,3825,2754],{"class":115},[105,3827,123],{"class":111},[105,3829,3830,3832,3835,3837,3839,3841,3843,3845,3847,3849,3851,3853],{"class":107,"line":146},[105,3831,129],{"class":111},[105,3833,3834],{"class":115},"input",[105,3836,1500],{"class":119},[105,3838,137],{"class":111},[105,3840,3820],{"class":140},[105,3842,482],{"class":119},[105,3844,137],{"class":111},[105,3846,3820],{"class":140},[105,3848,1935],{"class":119},[105,3850,137],{"class":111},[105,3852,3820],{"class":140},[105,3854,123],{"class":111},[105,3856,3857],{"class":107,"line":157},[105,3858,3042],{"emptyLinePlaceholder":3041},[105,3860,3861],{"class":107,"line":176},[105,3862,3863],{"class":2455},"\u003C!-- 隐式包裹：合法，但标题和控件被拆散时不好维护 -->\n",[105,3865,3866,3868,3870,3873,3875,3877,3879,3881,3883,3885,3887,3889,3891],{"class":107,"line":191},[105,3867,129],{"class":111},[105,3869,2754],{"class":115},[105,3871,3872],{"class":111},">邮箱 \u003C",[105,3874,3834],{"class":115},[105,3876,482],{"class":119},[105,3878,137],{"class":111},[105,3880,3820],{"class":140},[105,3882,1935],{"class":119},[105,3884,137],{"class":111},[105,3886,3820],{"class":140},[105,3888,2147],{"class":111},[105,3890,2754],{"class":115},[105,3892,123],{"class":111},[15,3894,3895,3896,3899],{},"显式绑定换来三个实际收益：点标题文字即聚焦（复选框是点标题即勾选，命中区域大得多，移动端体验差距明显）、读屏软件把标题播报成这个控件的名字、浏览器推断 ",[31,3897,3898],{},"autocomplete"," 也更准。",[15,3901,3902,3904,3905,3907,3908,3911],{},[31,3903,2701],{}," 指向的 ",[31,3906,1481],{}," 必须存在且不重复。一个控件允许被多个 ",[31,3909,3910],{},"\u003Clabel>"," 指向，但读屏会把两段都念出来。",[24,3913,3915],{"id":3914},"selecttextareaoptgroup-与-datalist","select、textarea、optgroup 与 datalist",[97,3917,3919],{"className":99,"code":3918,"language":65,"meta":101,"style":101},"\u003Cselect id=\"city\" name=\"city\">\n  \u003Coption value=\"\">请选择城市\u003C\u002Foption>\n  \u003Coptgroup label=\"华东\">\n    \u003Coption value=\"sh\">上海\u003C\u002Foption>\n    \u003Coption value=\"hz\">杭州\u003C\u002Foption>\n  \u003C\u002Foptgroup>\n\u003C\u002Fselect>\n\n\u003Ctextarea id=\"note\" name=\"note\" rows=\"4\">\u003C\u002Ftextarea>\n\n\u003Cinput list=\"langs\" id=\"lang\" name=\"lang\">\n\u003Cdatalist id=\"langs\">\n  \u003Coption value=\"HTML\">\u003C\u002Foption>\n  \u003Coption value=\"CSS\">\u003C\u002Foption>\n\u003C\u002Fdatalist>\n",[31,3920,3921,3943,3965,3982,4002,4022,4030,4038,4042,4076,4080,4109,4124,4143,4162],{"__ignoreMap":101},[105,3922,3923,3925,3928,3930,3932,3935,3937,3939,3941],{"class":107,"line":108},[105,3924,129],{"class":111},[105,3926,3927],{"class":115},"select",[105,3929,1500],{"class":119},[105,3931,137],{"class":111},[105,3933,3934],{"class":140},"\"city\"",[105,3936,482],{"class":119},[105,3938,137],{"class":111},[105,3940,3934],{"class":140},[105,3942,123],{"class":111},[105,3944,3945,3947,3950,3953,3955,3958,3961,3963],{"class":107,"line":126},[105,3946,149],{"class":111},[105,3948,3949],{"class":115},"option",[105,3951,3952],{"class":119}," value",[105,3954,137],{"class":111},[105,3956,3957],{"class":140},"\"\"",[105,3959,3960],{"class":111},">请选择城市\u003C\u002F",[105,3962,3949],{"class":115},[105,3964,123],{"class":111},[105,3966,3967,3969,3972,3975,3977,3980],{"class":107,"line":146},[105,3968,149],{"class":111},[105,3970,3971],{"class":115},"optgroup",[105,3973,3974],{"class":119}," label",[105,3976,137],{"class":111},[105,3978,3979],{"class":140},"\"华东\"",[105,3981,123],{"class":111},[105,3983,3984,3986,3988,3990,3992,3995,3998,4000],{"class":107,"line":157},[105,3985,160],{"class":111},[105,3987,3949],{"class":115},[105,3989,3952],{"class":119},[105,3991,137],{"class":111},[105,3993,3994],{"class":140},"\"sh\"",[105,3996,3997],{"class":111},">上海\u003C\u002F",[105,3999,3949],{"class":115},[105,4001,123],{"class":111},[105,4003,4004,4006,4008,4010,4012,4015,4018,4020],{"class":107,"line":176},[105,4005,160],{"class":111},[105,4007,3949],{"class":115},[105,4009,3952],{"class":119},[105,4011,137],{"class":111},[105,4013,4014],{"class":140},"\"hz\"",[105,4016,4017],{"class":111},">杭州\u003C\u002F",[105,4019,3949],{"class":115},[105,4021,123],{"class":111},[105,4023,4024,4026,4028],{"class":107,"line":191},[105,4025,194],{"class":111},[105,4027,3971],{"class":115},[105,4029,123],{"class":111},[105,4031,4032,4034,4036],{"class":107,"line":201},[105,4033,251],{"class":111},[105,4035,3927],{"class":115},[105,4037,123],{"class":111},[105,4039,4040],{"class":107,"line":211},[105,4041,3042],{"emptyLinePlaceholder":3041},[105,4043,4044,4046,4049,4051,4053,4056,4058,4060,4062,4065,4067,4070,4072,4074],{"class":107,"line":225},[105,4045,129],{"class":111},[105,4047,4048],{"class":115},"textarea",[105,4050,1500],{"class":119},[105,4052,137],{"class":111},[105,4054,4055],{"class":140},"\"note\"",[105,4057,482],{"class":119},[105,4059,137],{"class":111},[105,4061,4055],{"class":140},[105,4063,4064],{"class":119}," rows",[105,4066,137],{"class":111},[105,4068,4069],{"class":140},"\"4\"",[105,4071,2147],{"class":111},[105,4073,4048],{"class":115},[105,4075,123],{"class":111},[105,4077,4078],{"class":107,"line":239},[105,4079,3042],{"emptyLinePlaceholder":3041},[105,4081,4082,4084,4086,4089,4091,4094,4096,4098,4101,4103,4105,4107],{"class":107,"line":248},[105,4083,129],{"class":111},[105,4085,3834],{"class":115},[105,4087,4088],{"class":119}," list",[105,4090,137],{"class":111},[105,4092,4093],{"class":140},"\"langs\"",[105,4095,1500],{"class":119},[105,4097,137],{"class":111},[105,4099,4100],{"class":140},"\"lang\"",[105,4102,482],{"class":119},[105,4104,137],{"class":111},[105,4106,4100],{"class":140},[105,4108,123],{"class":111},[105,4110,4111,4113,4116,4118,4120,4122],{"class":107,"line":3162},[105,4112,129],{"class":111},[105,4114,4115],{"class":115},"datalist",[105,4117,1500],{"class":119},[105,4119,137],{"class":111},[105,4121,4093],{"class":140},[105,4123,123],{"class":111},[105,4125,4126,4128,4130,4132,4134,4137,4139,4141],{"class":107,"line":3171},[105,4127,149],{"class":111},[105,4129,3949],{"class":115},[105,4131,3952],{"class":119},[105,4133,137],{"class":111},[105,4135,4136],{"class":140},"\"HTML\"",[105,4138,2147],{"class":111},[105,4140,3949],{"class":115},[105,4142,123],{"class":111},[105,4144,4145,4147,4149,4151,4153,4156,4158,4160],{"class":107,"line":3180},[105,4146,149],{"class":111},[105,4148,3949],{"class":115},[105,4150,3952],{"class":119},[105,4152,137],{"class":111},[105,4154,4155],{"class":140},"\"CSS\"",[105,4157,2147],{"class":111},[105,4159,3949],{"class":115},[105,4161,123],{"class":111},[105,4163,4164,4166,4168],{"class":107,"line":3185},[105,4165,251],{"class":111},[105,4167,4115],{"class":115},[105,4169,123],{"class":111},[15,4171,4172],{},"五条约定：",[257,4174,4175,4186,4192,4207,4215],{},[39,4176,4177,1179,4180,4185],{},[31,4178,4179],{},"\u003Ctextarea>",[19,4181,2329,4182,4184],{},[31,4183,3791],{}," 属性","，初始内容写在开闭标签之间，第一个字符前不要留缩进（空白会真的显示出来），也不可自闭合。",[39,4187,4188,4191],{},[31,4189,4190],{},"\u003Cselect multiple>"," 在移动端体验很差，多数场景应改用一组复选框。",[39,4193,4194,4197,4198,4200,4201,4203,4204,4206],{},[31,4195,4196],{},"\u003Coption>"," 不带 ",[31,4199,3791],{}," 时提交的是它的文本，带 ",[31,4202,3791],{}," 时提交 ",[31,4205,3791],{},"——这个默认差异常造成前后端读到的值不一致。",[39,4208,4209,4211,4212,4214],{},[31,4210,4115],{}," 是「自由输入 + 建议」，不强制只能选；",[31,4213,3927],{}," 是「只能选」。",[39,4216,4217,4220,4221,1338,4224,4227],{},[31,4218,4219],{},"\u003Coptgroup>"," 给下拉里的选项分组，",[31,4222,4223],{},"\u003Cfieldset>",[31,4225,4226],{},"\u003Clegend>"," 给页面上的控件分组，两者作用域完全不同。",[10,4229,4231],{"id":4230},"十一表单校验属性","十一、表单校验属性",[15,4233,4234,4235,4238],{},"HTML 原生校验（Constraint Validation API）的价值是",[19,4236,4237],{},"在服务端返回之前就先给用户反馈","，成本为零。",[24,4240,4242],{"id":4241},"required-与-type最常用的两个","required 与 type：最常用的两个",[97,4244,4246],{"className":99,"code":4245,"language":65,"meta":101,"style":101},"\u003Cinput name=\"nick\" required>\n\u003Cinput name=\"email\" type=\"email\" required>\n",[31,4247,4248,4266],{"__ignoreMap":101},[105,4249,4250,4252,4254,4256,4258,4261,4264],{"class":107,"line":108},[105,4251,129],{"class":111},[105,4253,3834],{"class":115},[105,4255,482],{"class":119},[105,4257,137],{"class":111},[105,4259,4260],{"class":140},"\"nick\"",[105,4262,4263],{"class":119}," required",[105,4265,123],{"class":111},[105,4267,4268,4270,4272,4274,4276,4278,4280,4282,4284,4286],{"class":107,"line":126},[105,4269,129],{"class":111},[105,4271,3834],{"class":115},[105,4273,482],{"class":119},[105,4275,137],{"class":111},[105,4277,3820],{"class":140},[105,4279,1935],{"class":119},[105,4281,137],{"class":111},[105,4283,3820],{"class":140},[105,4285,4263],{"class":119},[105,4287,123],{"class":111},[15,4289,4290,4293,4294,1061,4297,4300,4301,4304,4305,612],{},[31,4291,4292],{},"required"," 拦空值，",[31,4295,4296],{},"type=\"email\"",[31,4298,4299],{},"\"url\""," 附带格式检查，两者叠加才能既「不能不填」又「不能乱填」。",[31,4302,4303],{},"type=\"number\""," 已隐含「必须是数字」，一般不需要额外 ",[31,4306,4307],{},"pattern",[15,4309,4310,4311,4313,4314,4316],{},"复选框的必填有个坑：「必须同意条款」要给那一个复选框自己加 ",[31,4312,4292],{},"；一组同名复选框里只要任意一个带 ",[31,4315,4292],{},"，就只要求其中一个被勾选。",[323,4318],{"height":4319,"src":4320,"title":4321},600,"\u002Ftutorials\u002Fhtml\u002Fdemos\u002F11-form-validation.html","示例 11：原生校验与提示接管",[24,4323,4325],{"id":4324},"patternminmaxstepminlength","pattern、min、max、step、minlength",[344,4327,4328,4340],{},[706,4329,4330],{},[709,4331,4332,4334,4337],{},[712,4333,3775],{},[712,4335,4336],{},"规则",[712,4338,4339],{},"备注",[722,4341,4342,4364,4385,4402,4417,4436],{},[709,4343,4344,4348,4351],{},[727,4345,4346],{},[31,4347,4307],{},[727,4349,4350],{},"正则",[727,4352,4353,4356,4357,4360,4361,4363],{},[19,4354,4355],{},"隐含整串匹配","，别自己加 ",[31,4358,4359],{},"^…$","；必须同时给 ",[31,4362,181],{}," 说明规则，否则用户只看到「与此格式不符」",[709,4365,4366,4374,4377],{},[727,4367,4368,1061,4371],{},[31,4369,4370],{},"minlength",[31,4372,4373],{},"maxlength",[727,4375,4376],{},"文本长度",[727,4378,4379,4381,4382,4384],{},[31,4380,4373],{}," 直接禁止继续输入（硬限制），",[31,4383,4370],{}," 在提交时校验（软限制）",[709,4386,4387,4393,4396],{},[727,4388,4389,1061,4391],{},[31,4390,3600],{},[31,4392,3603],{},[727,4394,4395],{},"数值或日期边界",[727,4397,4398,4399,4401],{},"只对数字\u002F日期类 ",[31,4400,3363],{}," 生效",[709,4403,4404,4408,4411],{},[727,4405,4406],{},[31,4407,3606],{},[727,4409,4410],{},"步长倍数",[727,4412,4413,4416],{},[31,4414,4415],{},"step=\"2\""," 时输入 3 不合法",[709,4418,4419,4424,4427],{},[727,4420,4421],{},[31,4422,4423],{},"multiple",[727,4425,4426],{},"允许多值",[727,4428,4429,4430,4432,4433],{},"配合 ",[31,4431,4296],{},"（逗号分隔）或 ",[31,4434,4435],{},"\"file\"",[709,4437,4438,4442,4445],{},[727,4439,4440],{},[31,4441,3736],{},[727,4443,4444],{},"文件类型",[727,4446,4447,1173,4450,4453],{},[31,4448,4449],{},"accept=\"image\u002F*\"",[31,4451,4452],{},".pdf,.doc","；只是筛选，不是安全限制",[15,4455,4456,4459,4460,4462],{},[31,4457,4458],{},"pattern=\"[A-Z]{2}\\d{4}\" title=\"两位大写字母加四位数字\""," 会让浏览器气泡直接显示这段 ",[31,4461,181],{},"，比默认提示有用得多。",[24,4464,4466],{"id":4465},"校验提示novalidate-与接管","校验提示、novalidate 与接管",[15,4468,4469],{},"原生气泡的文案语言跟随浏览器界面语言，中文站会出现「Please fill out this field」。接管的标准写法：",[97,4471,4473],{"className":99,"code":4472,"language":65,"meta":101,"style":101},"\u003Cform novalidate>…\u003C\u002Fform>\n",[31,4474,4475],{"__ignoreMap":101},[105,4476,4477,4479,4481,4484,4486,4488],{"class":107,"line":108},[105,4478,129],{"class":111},[105,4480,2600],{"class":115},[105,4482,4483],{"class":119}," novalidate",[105,4485,2519],{"class":111},[105,4487,2600],{"class":115},[105,4489,123],{"class":111},[97,4491,4495],{"className":4492,"code":4493,"language":4494,"meta":101,"style":101},"language-js shiki shiki-themes github-dark","const form = document.querySelector('form')\nform.addEventListener('submit', (e) => {\n  if (!form.checkValidity()) {\n    e.preventDefault()\n    form.reportValidity()   \u002F\u002F 仍然想用浏览器气泡时就调这个\n    return\n  }\n  \u002F\u002F 校验通过，再发请求\n})\n","js",[31,4496,4497,4525,4554,4573,4584,4598,4603,4608,4613],{"__ignoreMap":101},[105,4498,4499,4503,4507,4510,4513,4516,4519,4522],{"class":107,"line":108},[105,4500,4502],{"class":4501},"snl16","const",[105,4504,4506],{"class":4505},"sDLfK"," form",[105,4508,4509],{"class":4501}," =",[105,4511,4512],{"class":111}," document.",[105,4514,4515],{"class":119},"querySelector",[105,4517,4518],{"class":111},"(",[105,4520,4521],{"class":140},"'form'",[105,4523,4524],{"class":111},")\n",[105,4526,4527,4530,4533,4535,4538,4541,4545,4548,4551],{"class":107,"line":126},[105,4528,4529],{"class":111},"form.",[105,4531,4532],{"class":119},"addEventListener",[105,4534,4518],{"class":111},[105,4536,4537],{"class":140},"'submit'",[105,4539,4540],{"class":111},", (",[105,4542,4544],{"class":4543},"s9osk","e",[105,4546,4547],{"class":111},") ",[105,4549,4550],{"class":4501},"=>",[105,4552,4553],{"class":111}," {\n",[105,4555,4556,4559,4562,4565,4567,4570],{"class":107,"line":146},[105,4557,4558],{"class":4501},"  if",[105,4560,4561],{"class":111}," (",[105,4563,4564],{"class":4501},"!",[105,4566,4529],{"class":111},[105,4568,4569],{"class":119},"checkValidity",[105,4571,4572],{"class":111},"()) {\n",[105,4574,4575,4578,4581],{"class":107,"line":157},[105,4576,4577],{"class":111},"    e.",[105,4579,4580],{"class":119},"preventDefault",[105,4582,4583],{"class":111},"()\n",[105,4585,4586,4589,4592,4595],{"class":107,"line":176},[105,4587,4588],{"class":111},"    form.",[105,4590,4591],{"class":119},"reportValidity",[105,4593,4594],{"class":111},"()   ",[105,4596,4597],{"class":2455},"\u002F\u002F 仍然想用浏览器气泡时就调这个\n",[105,4599,4600],{"class":107,"line":191},[105,4601,4602],{"class":4501},"    return\n",[105,4604,4605],{"class":107,"line":201},[105,4606,4607],{"class":111},"  }\n",[105,4609,4610],{"class":107,"line":211},[105,4611,4612],{"class":2455},"  \u002F\u002F 校验通过，再发请求\n",[105,4614,4615],{"class":107,"line":225},[105,4616,4617],{"class":111},"})\n",[15,4619,4620,4621,4624,4625,4628,4629,4632,4633,4636,4637,1061,4640,1061,4643,1061,4646,4649,4650,4653],{},"单个控件的三个 API：",[31,4622,4623],{},"el.checkValidity()"," 返回布尔，",[31,4626,4627],{},"el.validationMessage"," 是浏览器准备说的话，",[31,4630,4631],{},"el.setCustomValidity('文案')"," 替换那句话——",[19,4634,4635],{},"不成立时记得设回空字符串","，否则该字段永久无效。CSS 侧还有 ",[31,4638,4639],{},":valid",[31,4641,4642],{},":invalid",[31,4644,4645],{},":required",[31,4647,4648],{},":optional"," 四个伪类可以做即时描边，注意它们在用户还没输入完时就已变红，通常要配合 ",[31,4651,4652],{},":not(:placeholder-shown)"," 收窄条件。",[24,4655,4656],{"id":4656},"原生校验不能替代服务端校验",[15,4658,4659],{},"原生校验属于用户体验层，不是安全层。绕过它只要一行：",[97,4661,4665],{"className":4662,"code":4663,"language":4664,"meta":101,"style":101},"language-bash shiki shiki-themes github-dark","curl -X POST https:\u002F\u002F你的域名\u002Fapi\u002Fregister -d 'email=不是邮箱&nick='\n","bash",[31,4666,4667],{"__ignoreMap":101},[105,4668,4669,4672,4675,4678,4681,4684],{"class":107,"line":108},[105,4670,4671],{"class":119},"curl",[105,4673,4674],{"class":4505}," -X",[105,4676,4677],{"class":140}," POST",[105,4679,4680],{"class":140}," https:\u002F\u002F你的域名\u002Fapi\u002Fregister",[105,4682,4683],{"class":4505}," -d",[105,4685,4686],{"class":140}," 'email=不是邮箱&nick='\n",[15,4688,4689,4690,4693],{},"三条边界：前端校验负责即时反馈、可以宽松；",[19,4691,4692],{},"服务端校验是唯一可信的一层","，所有字段必查；数据库约束是最后一道防线。",[15,4695,4696,4697,290,4700,270,4703,4705,4706,4709,4710,4705,4712,4715,4716,4718,4719,612],{},"顺带厘清 ",[31,4698,4699],{},"disabled",[31,4701,4702],{},"readonly",[31,4704,4702],{}," 的值",[19,4707,4708],{},"会","提交、可聚焦可复制；",[31,4711,4699],{},[19,4713,4714],{},"不会","提交、不可聚焦、通常显示为灰。只想「不让改但仍要有值」用 ",[31,4717,4702],{},"，纯展示结果更应改用 ",[31,4720,4721],{},"\u003Coutput>",[10,4723,4725],{"id":4724},"十二audio-与-video","十二、audio 与 video",[15,4727,4728,4729,4732,4733,612],{},"音视频标签的难点不在属性多，而在",[19,4730,4731],{},"浏览器的自动播放策略","和",[19,4734,4735],{},"多格式降级",[24,4737,4739],{"id":4738},"controlsautoplay-与浏览器的自动播放限制","controls、autoplay 与浏览器的自动播放限制",[97,4741,4743],{"className":99,"code":4742,"language":65,"meta":101,"style":101},"\u003Cvideo controls preload=\"metadata\" poster=\"\u002Fimg\u002Fposter.jpg\" width=\"1280\" height=\"720\">\n  你的浏览器不支持 video，\u003Ca href=\"\u002Fmedia\u002Fdemo.mp4\">下载视频\u003C\u002Fa>。\n\u003C\u002Fvideo>\n",[31,4744,4745,4787,4809],{"__ignoreMap":101},[105,4746,4747,4749,4752,4755,4758,4760,4763,4766,4768,4771,4773,4775,4778,4780,4782,4785],{"class":107,"line":108},[105,4748,129],{"class":111},[105,4750,4751],{"class":115},"video",[105,4753,4754],{"class":119}," controls",[105,4756,4757],{"class":119}," preload",[105,4759,137],{"class":111},[105,4761,4762],{"class":140},"\"metadata\"",[105,4764,4765],{"class":119}," poster",[105,4767,137],{"class":111},[105,4769,4770],{"class":140},"\"\u002Fimg\u002Fposter.jpg\"",[105,4772,1743],{"class":119},[105,4774,137],{"class":111},[105,4776,4777],{"class":140},"\"1280\"",[105,4779,1751],{"class":119},[105,4781,137],{"class":111},[105,4783,4784],{"class":140},"\"720\"",[105,4786,123],{"class":111},[105,4788,4789,4792,4794,4796,4798,4801,4804,4806],{"class":107,"line":126},[105,4790,4791],{"class":111},"  你的浏览器不支持 video，\u003C",[105,4793,1519],{"class":115},[105,4795,528],{"class":119},[105,4797,137],{"class":111},[105,4799,4800],{"class":140},"\"\u002Fmedia\u002Fdemo.mp4\"",[105,4802,4803],{"class":111},">下载视频\u003C\u002F",[105,4805,1519],{"class":115},[105,4807,4808],{"class":111},">。\n",[105,4810,4811,4813,4815],{"class":107,"line":146},[105,4812,251],{"class":111},[105,4814,4751],{"class":115},[105,4816,123],{"class":111},[257,4818,4819,4825,4841,4847,4860,4879],{},[39,4820,4821,4824],{},[31,4822,4823],{},"controls"," 不写就没有播放条，用户什么都点不了——自绘播放器要几十行 JS，默认用它。",[39,4826,4827,1179,4830,4836,4837,4840],{},[31,4828,4829],{},"autoplay",[19,4831,4832,4833],{},"必须同时写 ",[31,4834,4835],{},"muted"," 才可能生效。带声音的自动播放会被浏览器拦截（这是 Chrome\u002FSafari\u002FFirefox 共同策略，为了不把用户突然吓一跳，也为了省流量）。用户与页面有过交互（点击、按键）之后，脚本调用 ",[31,4838,4839],{},"play()"," 才不受限制。",[39,4842,4843,4846],{},[31,4844,4845],{},"playsinline"," 在 iPhone 上必写，漏了它 Safari 会强制切换到全屏播放器，你在视频上方叠的字幕、水印、按钮全部失效。",[39,4848,4849,1338,4852,1338,4854,1338,4856,4859],{},[31,4850,4851],{},"loop",[31,4853,4835],{},[31,4855,4845],{},[31,4857,4858],{},"preload=\"none\""," 是背景装饰视频的标准组合。",[39,4861,4862,4865,4866,1061,4869,1061,4872,4875,4876,4878],{},[31,4863,4864],{},"preload"," 取 ",[31,4867,4868],{},"none",[31,4870,4871],{},"metadata",[31,4873,4874],{},"auto","：列表页里的多个视频绝不能一律 ",[31,4877,4874],{},"，否则首屏带宽被视频吃光。",[39,4880,4881,4884],{},[31,4882,4883],{},"poster"," 给首帧占位图，既省流量也防止布局塌陷。",[323,4886],{"height":2301,"src":4887,"title":4888},"\u002Ftutorials\u002Fhtml\u002Fdemos\u002F12-media.html","示例 12：audio 与 video 的完整结构",[24,4890,4892],{"id":4891},"source-多格式降级","source 多格式降级",[97,4894,4896],{"className":99,"code":4895,"language":65,"meta":101,"style":101},"\u003Cvideo controls poster=\"\u002Fimg\u002Fposter.jpg\">\n  \u003Csource src=\"\u002Fmedia\u002Fdemo.av1.mp4\" type=\"video\u002Fmp4;codecs=av01.0.09M.08\">\n  \u003Csource src=\"\u002Fmedia\u002Fdemo.webm\" type=\"video\u002Fwebm\">\n  \u003Csource src=\"\u002Fmedia\u002Fdemo.mp4\" type=\"video\u002Fmp4\">\n  降级文字与下载链接\n\u003C\u002Fvideo>\n",[31,4897,4898,4914,4936,4958,4979,4984],{"__ignoreMap":101},[105,4899,4900,4902,4904,4906,4908,4910,4912],{"class":107,"line":108},[105,4901,129],{"class":111},[105,4903,4751],{"class":115},[105,4905,4754],{"class":119},[105,4907,4765],{"class":119},[105,4909,137],{"class":111},[105,4911,4770],{"class":140},[105,4913,123],{"class":111},[105,4915,4916,4918,4920,4922,4924,4927,4929,4931,4934],{"class":107,"line":126},[105,4917,149],{"class":111},[105,4919,1924],{"class":115},[105,4921,1727],{"class":119},[105,4923,137],{"class":111},[105,4925,4926],{"class":140},"\"\u002Fmedia\u002Fdemo.av1.mp4\"",[105,4928,1935],{"class":119},[105,4930,137],{"class":111},[105,4932,4933],{"class":140},"\"video\u002Fmp4;codecs=av01.0.09M.08\"",[105,4935,123],{"class":111},[105,4937,4938,4940,4942,4944,4946,4949,4951,4953,4956],{"class":107,"line":146},[105,4939,149],{"class":111},[105,4941,1924],{"class":115},[105,4943,1727],{"class":119},[105,4945,137],{"class":111},[105,4947,4948],{"class":140},"\"\u002Fmedia\u002Fdemo.webm\"",[105,4950,1935],{"class":119},[105,4952,137],{"class":111},[105,4954,4955],{"class":140},"\"video\u002Fwebm\"",[105,4957,123],{"class":111},[105,4959,4960,4962,4964,4966,4968,4970,4972,4974,4977],{"class":107,"line":157},[105,4961,149],{"class":111},[105,4963,1924],{"class":115},[105,4965,1727],{"class":119},[105,4967,137],{"class":111},[105,4969,4800],{"class":140},[105,4971,1935],{"class":119},[105,4973,137],{"class":111},[105,4975,4976],{"class":140},"\"video\u002Fmp4\"",[105,4978,123],{"class":111},[105,4980,4981],{"class":107,"line":176},[105,4982,4983],{"class":111},"  降级文字与下载链接\n",[105,4985,4986,4988,4990],{"class":107,"line":191},[105,4987,251],{"class":111},[105,4989,4751],{"class":115},[105,4991,123],{"class":111},[15,4993,4994,4995,4998,4999,5001,5002,5004],{},"浏览器",[19,4996,4997],{},"从上往下","逐个试，用第一个它能解码的 ",[31,5000,1902],{},"，所以顺序应当是「体积最优 → 兼容兜底」。全部失败时渲染 ",[31,5003,1898],{}," 内部的降级内容——这一句是必须保留的，否则旧浏览器只剩一个空白框。",[15,5006,5007,5008,5011,5012,5015,5016,5019,5020,5022,5023,5026],{},"编码格式的现实选择：H.264（",[31,5009,5010],{},"video\u002Fmp4","）兼容最好、效率一般；VP9\u002FAV1（",[31,5013,5014],{},"video\u002Fwebm","）同画质明显更小，Safari 对 AV1 的支持较晚。因此常见做法是",[19,5017,5018],{},"同时给 mp4 和 webm","，或用 ",[31,5021,1902],{}," 列出三种让新浏览器优先取小体积的那份。音频同理，",[31,5024,5025],{},"\u003Caudio>"," 可以只给一个 MP3，兼容性风险最小。",[24,5028,5030],{"id":5029},"track-字幕","track 字幕",[97,5032,5034],{"className":99,"code":5033,"language":65,"meta":101,"style":101},"\u003Cvideo controls>\n  \u003Csource src=\"\u002Fmedia\u002Fdemo.mp4\" type=\"video\u002Fmp4\">\n  \u003Ctrack kind=\"subtitles\" src=\"\u002Fmedia\u002Fdemo.zh.vtt\" srclang=\"zh\" label=\"中文\" default>\n  \u003Ctrack kind=\"captions\" src=\"\u002Fmedia\u002Fdemo.en.vtt\" srclang=\"en\" label=\"English\">\n\u003C\u002Fvideo>\n",[31,5035,5036,5046,5066,5108,5144],{"__ignoreMap":101},[105,5037,5038,5040,5042,5044],{"class":107,"line":108},[105,5039,129],{"class":111},[105,5041,4751],{"class":115},[105,5043,4754],{"class":119},[105,5045,123],{"class":111},[105,5047,5048,5050,5052,5054,5056,5058,5060,5062,5064],{"class":107,"line":126},[105,5049,149],{"class":111},[105,5051,1924],{"class":115},[105,5053,1727],{"class":119},[105,5055,137],{"class":111},[105,5057,4800],{"class":140},[105,5059,1935],{"class":119},[105,5061,137],{"class":111},[105,5063,4976],{"class":140},[105,5065,123],{"class":111},[105,5067,5068,5070,5073,5076,5078,5081,5083,5085,5088,5091,5093,5096,5098,5100,5103,5106],{"class":107,"line":146},[105,5069,149],{"class":111},[105,5071,5072],{"class":115},"track",[105,5074,5075],{"class":119}," kind",[105,5077,137],{"class":111},[105,5079,5080],{"class":140},"\"subtitles\"",[105,5082,1727],{"class":119},[105,5084,137],{"class":111},[105,5086,5087],{"class":140},"\"\u002Fmedia\u002Fdemo.zh.vtt\"",[105,5089,5090],{"class":119}," srclang",[105,5092,137],{"class":111},[105,5094,5095],{"class":140},"\"zh\"",[105,5097,3974],{"class":119},[105,5099,137],{"class":111},[105,5101,5102],{"class":140},"\"中文\"",[105,5104,5105],{"class":119}," default",[105,5107,123],{"class":111},[105,5109,5110,5112,5114,5116,5118,5121,5123,5125,5128,5130,5132,5135,5137,5139,5142],{"class":107,"line":157},[105,5111,149],{"class":111},[105,5113,5072],{"class":115},[105,5115,5075],{"class":119},[105,5117,137],{"class":111},[105,5119,5120],{"class":140},"\"captions\"",[105,5122,1727],{"class":119},[105,5124,137],{"class":111},[105,5126,5127],{"class":140},"\"\u002Fmedia\u002Fdemo.en.vtt\"",[105,5129,5090],{"class":119},[105,5131,137],{"class":111},[105,5133,5134],{"class":140},"\"en\"",[105,5136,3974],{"class":119},[105,5138,137],{"class":111},[105,5140,5141],{"class":140},"\"English\"",[105,5143,123],{"class":111},[105,5145,5146,5148,5150],{"class":107,"line":176},[105,5147,251],{"class":111},[105,5149,4751],{"class":115},[105,5151,123],{"class":111},[15,5153,5154,5155,5158,5159,355,5161,5164],{},"字幕文件是 WebVTT 格式：首行必须 ",[31,5156,5157],{},"WEBVTT","，之后是「起止时间 → 文本」的块，可含 ",[31,5160,986],{},[31,5162,5163],{},"\u003Cc.class>"," 之类的轻量标记。",[257,5166,5167,5189,5198,5205],{},[39,5168,5169,5172,5173,5176,5177,5180,5181,5184,5185,5188],{},[31,5170,5171],{},"kind=\"subtitles\""," 是给",[19,5174,5175],{},"听不见声音的用户以外的人","用的翻译；",[31,5178,5179],{},"kind=\"captions\""," 是给听障用户的，包含「",[105,5182,5183],{},"掌声","」这类音效说明；",[31,5186,5187],{},"kind=\"descriptions\""," 是给视障用户的画面描述音轨。",[39,5190,5191,5194,5195,5197],{},[31,5192,5193],{},"default"," 让该轨默认开启，",[31,5196,2754],{}," 是播放器字幕菜单里的名字。",[39,5199,5200,5201,5204],{},"字幕文件与页面不同源时必须返回 CORS 头，否则浏览器静默不加载——表现为「字幕点了没反应」，Network 面板里那条请求是 ",[31,5202,5203],{},"(failed) net::ERR_…"," 或被 CORS 策略拦下。",[39,5206,5207,5208,5211,5212,1061,5214,5216],{},"用 JS 操作轨道：",[31,5209,5210],{},"video.textTracks[0].mode = 'showing'","（其它值 ",[31,5213,3744],{},[31,5215,4699],{},"）。",[15,5218,5219,5221,5222,5225,5226,5229],{},[31,5220,5025],{}," 也支持 ",[31,5223,5224],{},"\u003Ctrack>","，但没有可视区域来显示，实际用途很小——给音频配",[19,5227,5228],{},"文字稿","才是对用户真正有帮助的做法。",[10,5231,5233],{"id":5232},"十三iframe-与内嵌内容","十三、iframe 与内嵌内容",[15,5235,5236],{},"iframe 是「页面里另一个完整文档」：独立的 CSS、独立的 JS 运行环境、独立的滚动条。本站教程里每一个示例框都是它。",[24,5238,5240],{"id":5239},"src-与-sandbox-的边界","src 与 sandbox 的边界",[97,5242,5244],{"className":99,"code":5243,"language":65,"meta":101,"style":101},"\u003Ciframe\n  src=\"\u002Fembed\u002Freport\"\n  title=\"季度报表\"\n  loading=\"lazy\"\n  sandbox=\"allow-scripts allow-forms\"\n  referrerpolicy=\"no-referrer-when-downgrade\"\n>\u003C\u002Fiframe>\n",[31,5245,5246,5253,5263,5273,5283,5293,5303],{"__ignoreMap":101},[105,5247,5248,5250],{"class":107,"line":108},[105,5249,129],{"class":111},[105,5251,5252],{"class":115},"iframe\n",[105,5254,5255,5258,5260],{"class":107,"line":126},[105,5256,5257],{"class":119},"  src",[105,5259,137],{"class":111},[105,5261,5262],{"class":140},"\"\u002Fembed\u002Freport\"\n",[105,5264,5265,5268,5270],{"class":107,"line":146},[105,5266,5267],{"class":119},"  title",[105,5269,137],{"class":111},[105,5271,5272],{"class":140},"\"季度报表\"\n",[105,5274,5275,5278,5280],{"class":107,"line":157},[105,5276,5277],{"class":119},"  loading",[105,5279,137],{"class":111},[105,5281,5282],{"class":140},"\"lazy\"\n",[105,5284,5285,5288,5290],{"class":107,"line":176},[105,5286,5287],{"class":119},"  sandbox",[105,5289,137],{"class":111},[105,5291,5292],{"class":140},"\"allow-scripts allow-forms\"\n",[105,5294,5295,5298,5300],{"class":107,"line":191},[105,5296,5297],{"class":119},"  referrerpolicy",[105,5299,137],{"class":111},[105,5301,5302],{"class":140},"\"no-referrer-when-downgrade\"\n",[105,5304,5305,5307,5310],{"class":107,"line":201},[105,5306,2147],{"class":111},[105,5308,5309],{"class":115},"iframe",[105,5311,123],{"class":111},[15,5313,5314,5317,5318,5321,5322,5325],{},[31,5315,5316],{},"sandbox"," 是",[19,5319,5320],{},"白名单","：不写这个属性表示什么都不限制；写了但值为空串（",[31,5323,5324],{},"sandbox=\"\"","）表示最严格的封锁——脚本不能跑、表单不能提交、不能弹窗、不能把外层页面跳走。需要哪项就显式列哪项：",[344,5327,5328,5340],{},[706,5329,5330],{},[709,5331,5332,5335,5338],{},[712,5333,5334],{},"值",[712,5336,5337],{},"放开什么",[712,5339,4339],{},[722,5341,5342,5355,5368,5386,5401,5418],{},[709,5343,5344,5349,5352],{},[727,5345,5346],{},[31,5347,5348],{},"allow-scripts",[727,5350,5351],{},"内部 JS",[727,5353,5354],{},"演示代码必须",[709,5356,5357,5362,5365],{},[727,5358,5359],{},[31,5360,5361],{},"allow-forms",[727,5363,5364],{},"内部表单提交",[727,5366,5367],{},"表单示例",[709,5369,5370,5375,5383],{},[727,5371,5372],{},[31,5373,5374],{},"allow-modals",[727,5376,5377,1003,5380],{},[31,5378,5379],{},"alert",[31,5381,5382],{},"confirm",[727,5384,5385],{},"讲解弹窗时",[709,5387,5388,5393,5398],{},[727,5389,5390],{},[31,5391,5392],{},"allow-popups",[727,5394,5395],{},[31,5396,5397],{},"window.open",[727,5399,5400],{},"少用",[709,5402,5403,5408,5415],{},[727,5404,5405],{},[31,5406,5407],{},"allow-same-origin",[727,5409,5410,5411,5414],{},"保留同源身份，可读自己的 ",[31,5412,5413],{},"localStorage","\u002Fcookie",[727,5416,5417],{},"需要存储的示例",[709,5419,5420,5425,5428],{},[727,5421,5422],{},[31,5423,5424],{},"allow-top-navigation",[727,5426,5427],{},"允许内部把外层跳走",[727,5429,5430,5433],{},[19,5431,5432],{},"基本别开","，是钓鱼入口",[15,5435,5436,5437,290,5439,1179,5441,5444,5445,5448,5449,5451,5452,5454],{},"有个必须知道的陷阱：",[31,5438,5348],{},[31,5440,5407],{},[19,5442,5443],{},"同时","给、且嵌的是同源文档时，沙箱等于形同虚设（内部脚本能拿到自己的 window 再爬 ",[31,5446,5447],{},"parent","），浏览器控制台会直接警告这一组合。本站的示例框故意不给 ",[31,5450,5407],{},"，代价是涉及 ",[31,5453,5413],{}," 的例子必须在「新标签页」里看。",[24,5456,5458],{"id":5457},"title-不是可选项","title 不是可选项",[15,5460,5461,5462,5464,5465,5467,5468,1003,5470,5472],{},"读屏用户通过「框架列表」在 iframe 间跳转，",[31,5463,181],{}," 就是列表里的名称。缺 ",[31,5466,181],{}," 的 iframe 在 Lighthouse \u002F axe 的无障碍审计里是确定的错误项。同理，",[31,5469,1793],{},[31,5471,1796],{}," 或 CSS 尺寸要写清楚，否则 iframe 会塌陷成 150px 高的默认值。",[323,5474],{"height":2301,"src":5475,"title":5476},"\u002Ftutorials\u002Fhtml\u002Fdemos\u002F13-iframe.html","示例 13：iframe、sandbox 与响应式内嵌",[24,5478,5479],{"id":5479},"响应式内嵌容器",[15,5481,5482,5483,5486,5487,270],{},"第三方给的嵌入代码通常写死 ",[31,5484,5485],{},"width=\"560\" height=\"315\"","，手机上直接横向溢出。现代写法用 ",[31,5488,5489],{},"aspect-ratio",[97,5491,5493],{"className":99,"code":5492,"language":65,"meta":101,"style":101},"\u003Cdiv style=\"aspect-ratio:16\u002F9;position:relative\">\n  \u003Ciframe style=\"position:absolute;inset:0;width:100%;height:100%;border:0\" src=\"…\" title=\"…\" loading=\"lazy\">\u003C\u002Fiframe>\n\u003C\u002Fdiv>\n",[31,5494,5495,5510,5549],{"__ignoreMap":101},[105,5496,5497,5499,5501,5503,5505,5508],{"class":107,"line":108},[105,5498,129],{"class":111},[105,5500,2504],{"class":115},[105,5502,2507],{"class":119},[105,5504,137],{"class":111},[105,5506,5507],{"class":140},"\"aspect-ratio:16\u002F9;position:relative\"",[105,5509,123],{"class":111},[105,5511,5512,5514,5516,5518,5520,5523,5525,5527,5530,5533,5535,5537,5539,5541,5543,5545,5547],{"class":107,"line":126},[105,5513,149],{"class":111},[105,5515,5309],{"class":115},[105,5517,2507],{"class":119},[105,5519,137],{"class":111},[105,5521,5522],{"class":140},"\"position:absolute;inset:0;width:100%;height:100%;border:0\"",[105,5524,1727],{"class":119},[105,5526,137],{"class":111},[105,5528,5529],{"class":140},"\"…\"",[105,5531,5532],{"class":119}," title",[105,5534,137],{"class":111},[105,5536,5529],{"class":140},[105,5538,1759],{"class":119},[105,5540,137],{"class":111},[105,5542,1764],{"class":140},[105,5544,2147],{"class":111},[105,5546,5309],{"class":115},[105,5548,123],{"class":111},[105,5550,5551,5553,5555],{"class":107,"line":146},[105,5552,251],{"class":111},[105,5554,2504],{"class":115},[105,5556,123],{"class":111},[15,5558,5559,5560,5563,5564,5567],{},"老浏览器兼容写法是 ",[31,5561,5562],{},"padding-top: 56.25%"," 撑出比例（原理相同：百分比 padding 相对",[19,5565,5566],{},"宽度","计算），CSS 教程第六章会解释这条规则。",[24,5569,5571],{"id":5570},"性能代价与-facade-模式","性能代价与 facade 模式",[257,5573,5574,5581,5587,5601],{},[39,5575,5576,5577,5580],{},"iframe 内容与主页面",[19,5578,5579],{},"不共享","渲染资源，一份 iframe 就是一次独立的网络与解析开销，是 Lighthouse「减少第三方代码」告警的主要来源。",[39,5582,5583,5584,5586],{},"iframe 内的内容",[19,5585,4714],{},"被搜索引擎当作本页内容收录——把正文放 iframe 里等于放弃 SEO。",[39,5588,5589,5590,5593,5594,5596,5597,5600],{},"视频、地图、社交组件建议用 ",[19,5591,5592],{},"facade 模式","：先渲染一张海报图 + 播放按钮（成本几乎为零），用户点击后才写入 ",[31,5595,1773],{}," 真正加载。YouTube 的 ",[31,5598,5599],{},"youtube-nocookie.com"," 组件、多数静态站的视频方案都是这个思路。",[39,5602,5603,5604,5607,5608,1179,5611,5614],{},"跨 iframe 通信只能用 ",[31,5605,5606],{},"postMessage","，两侧",[19,5609,5610],{},"都必须校验",[31,5612,5613],{},"event.origin","，否则任意页面都能给你发消息。",[10,5616,5618],{"id":5617},"十四实体注释与书写约定","十四、实体、注释与书写约定",[24,5620,5621],{"id":5621},"常用实体",[15,5623,5624],{},"只要内容里要显示标签字符本身，就必须转义：",[344,5626,5627,5638],{},[706,5628,5629],{},[709,5630,5631,5633,5636],{},[712,5632,1385],{},[712,5634,5635],{},"渲染",[712,5637,3534],{},[722,5639,5640,5656,5672,5685,5701,5713,5732,5748,5761],{},[709,5641,5642,5650,5653],{},[727,5643,5644,1179,5647],{},[31,5645,5646],{},"&lt;",[31,5648,5649],{},"&gt;",[727,5651,5652],{},"\u003C >",[727,5654,5655],{},"写代码示例，教程页最高频",[709,5657,5658,5663,5666],{},[727,5659,5660],{},[31,5661,5662],{},"&amp;",[727,5664,5665],{},"&",[727,5667,5668,5669,5671],{},"与符号；URL 里的 ",[31,5670,5665],{}," 也要写成它",[709,5673,5674,5679,5682],{},[727,5675,5676],{},[31,5677,5678],{},"&quot;",[727,5680,5681],{},"\"",[727,5683,5684],{},"属性值里嵌套双引号",[709,5686,5687,5695,5698],{},[727,5688,5689,1173,5692],{},[31,5690,5691],{},"&apos;",[31,5693,5694],{},"&#39;",[727,5696,5697],{},"'",[727,5699,5700],{},"单引号（后者兼容性更稳）",[709,5702,5703,5707,5710],{},[727,5704,5705],{},[31,5706,850],{},[727,5708,5709],{},"空格",[727,5711,5712],{},"不换行空格，强制不在此处折行",[709,5714,5715,5726,5729],{},[727,5716,5717,1179,5720,1179,5723],{},[31,5718,5719],{},"&copy;",[31,5721,5722],{},"&reg;",[31,5724,5725],{},"&trade;",[727,5727,5728],{},"© ® ™",[727,5730,5731],{},"版权、注册、商标",[709,5733,5734,5742,5745],{},[727,5735,5736,1179,5739],{},[31,5737,5738],{},"&mdash;",[31,5740,5741],{},"&ndash;",[727,5743,5744],{},"— –",[727,5746,5747],{},"长破折号、短连接号",[709,5749,5750,5755,5758],{},[727,5751,5752],{},[31,5753,5754],{},"&middot;",[727,5756,5757],{},"·",[727,5759,5760],{},"面包屑分隔符",[709,5762,5763,5771,5774],{},[727,5764,5765,1061,5768],{},[31,5766,5767],{},"&#20195;",[31,5769,5770],{},"&#x4EE3;",[727,5772,5773],{},"代",[727,5775,5776],{},"任意 Unicode 的十进制 \u002F 十六进制写法",[323,5778],{"height":2301,"src":5779,"title":5780},"\u002Ftutorials\u002Fhtml\u002Fdemos\u002F14-entities-conventions.html","示例 14：实体表与书写约定",[24,5782,5783],{"id":5783},"中文还需要实体吗",[15,5785,5786,5787,5789],{},"不需要。声明了 ",[31,5788,645],{}," 且文件确实存成 UTF-8，中文、emoji（😀）、数学符号（∑）都能直接写。实体的现代用途只剩两类：展示标签语法的教程页，以及编码不确定的旧环境。",[15,5791,5792,5793,5796,5797,5800,5801,1061,5804,1061,5807,612],{},"反过来要警惕的是",[19,5794,5795],{},"该转义时没转义","：把用户输入直接拼进 HTML 是 XSS 的标准成因。转义发生在「输出到 HTML」这一步，而不是存储时；主流框架的插值语法（Vue 的 ",[31,5798,5799],{},"{{ }}","、React 的 JSX）已默认转义，真正要小心的是主动使用 ",[31,5802,5803],{},"v-html",[31,5805,5806],{},"innerHTML",[31,5808,5809],{},"dangerouslySetInnerHTML",[24,5811,5812],{"id":5812},"注释的现状与风险",[97,5814,5816],{"className":99,"code":5815,"language":65,"meta":101,"style":101},"\u003C!-- 普通注释 -->\n\u003C!-- 注释里不能出现连续两个减号，会被提前当成结束 -->\n",[31,5817,5818,5823],{"__ignoreMap":101},[105,5819,5820],{"class":107,"line":108},[105,5821,5822],{"class":2455},"\u003C!-- 普通注释 -->\n",[105,5824,5825],{"class":107,"line":126},[105,5826,5827],{"class":2455},"\u003C!-- 注释里不能出现连续两个减号，会被提前当成结束 -->\n",[15,5829,5830,5831,5834,5835,5838],{},"三条现状：① 注释",[19,5832,5833],{},"会随 HTML 一起发给用户","，内部接口地址、密钥、客户名字都不能写；② 浏览器不执行注释里的脚本，但「把密码注释掉」这种写法每年仍会被扫描器捡出来；③ 条件注释（",[31,5836,5837],{},"\u003C!--[if IE]>","）在 IE 10+ 已彻底失效，别再用来做浏览器分支。",[24,5840,5842],{"id":5841},"大小写引号与布尔属性","大小写、引号与布尔属性",[344,5844,5845,5858],{},[706,5846,5847],{},[709,5848,5849,5852,5855],{},[712,5850,5851],{},"项目",[712,5853,5854],{},"现代约定",[712,5856,5857],{},"原因",[722,5859,5860,5871,5885,5912,5945],{},[709,5861,5862,5865,5868],{},[727,5863,5864],{},"标签大小写",[727,5866,5867],{},"全部小写",[727,5869,5870],{},"HTML 不区分，但 XHTML\u002FVue 模板区分，混写最容易出问题",[709,5872,5873,5876,5879],{},[727,5874,5875],{},"属性值引号",[727,5877,5878],{},"始终加",[727,5880,5881,5882,5884],{},"值里含空格或 ",[31,5883,5665],{}," 时不加就错",[709,5886,5887,5890,5898],{},[727,5888,5889],{},"布尔属性",[727,5891,5892,5894,5895],{},[31,5893,4292],{}," 而非 ",[31,5896,5897],{},"required=\"required\"",[727,5899,5900,5901,5904,5905,5908,5909],{},"JS 里 ",[31,5902,5903],{},"getAttribute('disabled')"," 拿到的是空串而不是 ",[31,5906,5907],{},"\"true\"","，判断要用 ",[31,5910,5911],{},"el.disabled",[709,5913,5914,5917,5925],{},[727,5915,5916],{},"自闭合",[727,5918,5919,5921,5922],{},[31,5920,645],{},"，不写 ",[31,5923,5924],{},"\u002F>",[727,5926,5927,5928,1003,5931,1003,5933,1003,5935,1003,5938,1003,5940,1003,5942,5944],{},"void 元素（",[31,5929,5930],{},"br",[31,5932,1724],{},[31,5934,3834],{},[31,5936,5937],{},"hr",[31,5939,163],{},[31,5941,517],{},[31,5943,1924],{},"）本来就没有结束标签",[709,5946,5947,5950,5953],{},[727,5948,5949],{},"缩进",[727,5951,5952],{},"两个空格，项目内统一",[727,5954,5955],{},"交给编辑器\u002FPrettier 格式化，别手对齐",[10,5957,5959],{"id":5958},"十五data-自定义属性","十五、data-* 自定义属性",[15,5961,5962,5965,5966,5969,5970,612],{},[31,5963,5964],{},"data-*"," 是「给元素挂自定义数据」的合法位置，值永远是字符串。命名规则：必须以 ",[31,5967,5968],{},"data-"," 开头，后面只允许小写字母、数字和连字符——",[19,5971,5972],{},"不能有大写、不能有点",[24,5974,5975],{"id":5975},"语法与命名",[97,5977,5979],{"className":99,"code":5978,"language":65,"meta":101,"style":101},"\u003Carticle id=\"a12\"\n         data-level=\"hot\"\n         data-user-id=\"1024\"\n         data-max-width=\"480\"\n         data-config='{\"sortable\":true,\"page\":1}'>\n  …\n\u003C\u002Farticle>\n",[31,5980,5981,5994,6004,6014,6024,6036,6041],{"__ignoreMap":101},[105,5982,5983,5985,5987,5989,5991],{"class":107,"line":108},[105,5984,129],{"class":111},[105,5986,3058],{"class":115},[105,5988,1500],{"class":119},[105,5990,137],{"class":111},[105,5992,5993],{"class":140},"\"a12\"\n",[105,5995,5996,5999,6001],{"class":107,"line":126},[105,5997,5998],{"class":119},"         data-level",[105,6000,137],{"class":111},[105,6002,6003],{"class":140},"\"hot\"\n",[105,6005,6006,6009,6011],{"class":107,"line":146},[105,6007,6008],{"class":119},"         data-user-id",[105,6010,137],{"class":111},[105,6012,6013],{"class":140},"\"1024\"\n",[105,6015,6016,6019,6021],{"class":107,"line":157},[105,6017,6018],{"class":119},"         data-max-width",[105,6020,137],{"class":111},[105,6022,6023],{"class":140},"\"480\"\n",[105,6025,6026,6029,6031,6034],{"class":107,"line":176},[105,6027,6028],{"class":119},"         data-config",[105,6030,137],{"class":111},[105,6032,6033],{"class":140},"'{\"sortable\":true,\"page\":1}'",[105,6035,123],{"class":111},[105,6037,6038],{"class":107,"line":191},[105,6039,6040],{"class":111},"  …\n",[105,6042,6043,6045,6047],{"class":107,"line":201},[105,6044,251],{"class":111},[105,6046,3058],{"class":115},[105,6048,123],{"class":111},[257,6050,6051,6062,6071],{},[39,6052,6053,6054,6057,6058,6061],{},"名字用 kebab-case（",[31,6055,6056],{},"data-max-width","），驼峰只出现在 JS 的 ",[31,6059,6060],{},"dataset"," 访问端。",[39,6063,6064,6065,2739,6068,612],{},"名字要有语义：",[31,6066,6067],{},"data-role=\"price\"",[31,6069,6070],{},"data-x=\"3\"",[39,6072,6073,6074,6077],{},"JSON 值用",[19,6075,6076],{},"单引号","包裹，内部保持标准双引号 JSON。",[24,6079,6081],{"id":6080},"配合-css-属性选择器","配合 CSS 属性选择器",[15,6083,6084,6085,6088],{},"CSS 能直接按属性匹配，这带来一种很实用的写法：",[19,6086,6087],{},"把「内容属性」交给 data，样式响应它","，JS 只改属性不碰类名。",[97,6090,6094],{"className":6091,"code":6092,"language":6093,"meta":101,"style":101},"language-css shiki shiki-themes github-dark",".card[data-level=\"hot\"] { border-color: #cf222e; }   \u002F* 精确匹配 *\u002F\n.card[data-level]       { cursor: pointer; }          \u002F* 只要属性存在 *\u002F\n[data-level^=\"h\"]       { … }                         \u002F* 前缀，还有 $= *= ~= |= *\u002F\n.card[data-level]::after { content: attr(data-level); } \u002F* 把值当文本显示出来 *\u002F\n.grid-item[data-span=\"2\"] { grid-column: span 2; }\n","css",[31,6095,6096,6130,6155,6173,6208],{"__ignoreMap":101},[105,6097,6098,6101,6104,6107,6109,6112,6115,6118,6121,6124,6127],{"class":107,"line":108},[105,6099,6100],{"class":119},".card",[105,6102,6103],{"class":111},"[",[105,6105,6106],{"class":119},"data-level",[105,6108,137],{"class":4501},[105,6110,6111],{"class":140},"\"hot\"",[105,6113,6114],{"class":111},"] { ",[105,6116,6117],{"class":4505},"border-color",[105,6119,6120],{"class":111},": ",[105,6122,6123],{"class":4505},"#cf222e",[105,6125,6126],{"class":111},"; }   ",[105,6128,6129],{"class":2455},"\u002F* 精确匹配 *\u002F\n",[105,6131,6132,6134,6136,6138,6141,6144,6146,6149,6152],{"class":107,"line":126},[105,6133,6100],{"class":119},[105,6135,6103],{"class":111},[105,6137,6106],{"class":119},[105,6139,6140],{"class":111},"]       { ",[105,6142,6143],{"class":4505},"cursor",[105,6145,6120],{"class":111},[105,6147,6148],{"class":4505},"pointer",[105,6150,6151],{"class":111},"; }          ",[105,6153,6154],{"class":2455},"\u002F* 只要属性存在 *\u002F\n",[105,6156,6157,6159,6161,6164,6167,6170],{"class":107,"line":146},[105,6158,6103],{"class":111},[105,6160,6106],{"class":119},[105,6162,6163],{"class":4501},"^=",[105,6165,6166],{"class":140},"\"h\"",[105,6168,6169],{"class":111},"]       { … }                         ",[105,6171,6172],{"class":2455},"\u002F* 前缀，还有 $= *= ~= |= *\u002F\n",[105,6174,6175,6177,6179,6181,6184,6187,6190,6193,6195,6198,6200,6202,6205],{"class":107,"line":157},[105,6176,6100],{"class":119},[105,6178,6103],{"class":111},[105,6180,6106],{"class":119},[105,6182,6183],{"class":111},"]",[105,6185,6186],{"class":119},"::after",[105,6188,6189],{"class":111}," { ",[105,6191,6192],{"class":4505},"content",[105,6194,6120],{"class":111},[105,6196,6197],{"class":4505},"attr",[105,6199,4518],{"class":111},[105,6201,6106],{"class":4543},[105,6203,6204],{"class":111},"); } ",[105,6206,6207],{"class":2455},"\u002F* 把值当文本显示出来 *\u002F\n",[105,6209,6210,6213,6215,6218,6220,6222,6224,6227,6230,6233],{"class":107,"line":176},[105,6211,6212],{"class":119},".grid-item",[105,6214,6103],{"class":111},[105,6216,6217],{"class":119},"data-span",[105,6219,137],{"class":4501},[105,6221,2405],{"class":140},[105,6223,6114],{"class":111},[105,6225,6226],{"class":4505},"grid-column",[105,6228,6229],{"class":111},": span ",[105,6231,6232],{"class":4505},"2",[105,6234,6235],{"class":111},"; }\n",[15,6237,6238,6241,6242,6244,6245,6247,6248,6250],{},[31,6239,6240],{},"attr()"," 在 ",[31,6243,6192],{}," 里取值非常省事（做角标、单位后缀），但它目前只能可靠地用在 ",[31,6246,6192],{}," 上，不能拿去当宽度、颜色等任意属性值使用——那条能力（",[31,6249,6240],{}," 的通用用法）还在逐步落地中，写生产样式前请查一次基线支持。",[323,6252],{"height":2301,"src":6253,"title":6254},"\u002Ftutorials\u002Fhtml\u002Fdemos\u002F15-data-attr.html","示例 15：data-* 与 CSS\u002FJS 的配合",[24,6256,6258],{"id":6257},"dataset-读取","dataset 读取",[97,6260,6262],{"className":4492,"code":6261,"language":4494,"meta":101,"style":101},"const el = document.querySelector('[data-user-id]')\n\nel.dataset.userId      \u002F\u002F '1024' —— 连字符名自动映射成驼峰\nel.dataset.maxWidth    \u002F\u002F '480'，注意仍是字符串，要自己 parseInt\nel.dataset.state = 'ok'\u002F\u002F 写入会同步反映到 HTML 属性，CSS 选择器立刻生效\n\nObject.entries(el.dataset)          \u002F\u002F 遍历该元素上全部 data-*\ndelete el.dataset.clicks            \u002F\u002F 删除属性\n\nel.getAttribute('data-user-id')     \u002F\u002F 等价读法；setAttribute 是等价写法\n",[31,6263,6264,6284,6288,6296,6304,6317,6321,6335,6346,6350],{"__ignoreMap":101},[105,6265,6266,6268,6271,6273,6275,6277,6279,6282],{"class":107,"line":108},[105,6267,4502],{"class":4501},[105,6269,6270],{"class":4505}," el",[105,6272,4509],{"class":4501},[105,6274,4512],{"class":111},[105,6276,4515],{"class":119},[105,6278,4518],{"class":111},[105,6280,6281],{"class":140},"'[data-user-id]'",[105,6283,4524],{"class":111},[105,6285,6286],{"class":107,"line":126},[105,6287,3042],{"emptyLinePlaceholder":3041},[105,6289,6290,6293],{"class":107,"line":146},[105,6291,6292],{"class":111},"el.dataset.userId      ",[105,6294,6295],{"class":2455},"\u002F\u002F '1024' —— 连字符名自动映射成驼峰\n",[105,6297,6298,6301],{"class":107,"line":157},[105,6299,6300],{"class":111},"el.dataset.maxWidth    ",[105,6302,6303],{"class":2455},"\u002F\u002F '480'，注意仍是字符串，要自己 parseInt\n",[105,6305,6306,6309,6311,6314],{"class":107,"line":176},[105,6307,6308],{"class":111},"el.dataset.state ",[105,6310,137],{"class":4501},[105,6312,6313],{"class":140}," 'ok'",[105,6315,6316],{"class":2455},"\u002F\u002F 写入会同步反映到 HTML 属性，CSS 选择器立刻生效\n",[105,6318,6319],{"class":107,"line":191},[105,6320,3042],{"emptyLinePlaceholder":3041},[105,6322,6323,6326,6329,6332],{"class":107,"line":201},[105,6324,6325],{"class":111},"Object.",[105,6327,6328],{"class":119},"entries",[105,6330,6331],{"class":111},"(el.dataset)          ",[105,6333,6334],{"class":2455},"\u002F\u002F 遍历该元素上全部 data-*\n",[105,6336,6337,6340,6343],{"class":107,"line":211},[105,6338,6339],{"class":4501},"delete",[105,6341,6342],{"class":111}," el.dataset.clicks            ",[105,6344,6345],{"class":2455},"\u002F\u002F 删除属性\n",[105,6347,6348],{"class":107,"line":225},[105,6349,3042],{"emptyLinePlaceholder":3041},[105,6351,6352,6355,6358,6360,6363,6366],{"class":107,"line":239},[105,6353,6354],{"class":111},"el.",[105,6356,6357],{"class":119},"getAttribute",[105,6359,4518],{"class":111},[105,6361,6362],{"class":140},"'data-user-id'",[105,6364,6365],{"class":111},")     ",[105,6367,6368],{"class":2455},"\u002F\u002F 等价读法；setAttribute 是等价写法\n",[15,6370,6371,290,6373,6375,6376,612],{},[31,6372,6060],{},[31,6374,6357],{}," 的区别只在可读性，操作的是同一个属性。要监听变化用 ",[31,6377,6378],{},"MutationObserver",[24,6380,6382],{"id":6381},"什么时候不该用-data","什么时候不该用 data-*",[257,6384,6385,6395,6401,6426],{},[39,6386,6387,6390,6391,6394],{},[19,6388,6389],{},"外观开关用 class，不用 data-","*。",[31,6392,6393],{},"data-color=\"red\""," 通常说明你真正想要的是一个语义类名。",[39,6396,6397,6400],{},[19,6398,6399],{},"敏感信息不要放","：和所有属性一样，它会出现在源码里，任何人可见。",[39,6402,6403,270,6406,355,6409,355,6412,6415,6416,6418,6419,6422,6423,6425],{},[19,6404,6405],{},"可访问性状态必须用 ARIA",[31,6407,6408],{},"aria-expanded",[31,6410,6411],{},"aria-current",[31,6413,6414],{},"aria-hidden"," 会被读屏播报，",[31,6417,5964],{}," 完全不会。把 ",[31,6420,6421],{},"data-open"," 当成 ",[31,6424,6408],{}," 用是常见错误。",[39,6427,6428,6429,6432],{},"需要传结构化配置时，集中成一个 ",[31,6430,6431],{},"data-config"," JSON 比散成十几个属性更好维护。",[10,6434,6436],{"id":6435},"十六响应式视口head-元信息与-seo","十六、响应式视口、head 元信息与 SEO",[15,6438,6439,6441],{},[31,6440,269],{}," 里的内容决定三件事：手机上能不能正常排版、搜索结果长什么样、分享到社交平台时有没有卡片图。",[24,6443,6445],{"id":6444},"viewport-那一行到底在说什么","viewport 那一行到底在说什么",[97,6447,6449],{"className":99,"code":6448,"language":65,"meta":101,"style":101},"\u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n",[31,6450,6451],{"__ignoreMap":101},[105,6452,6453,6455,6457,6459,6461,6463,6465,6467,6469],{"class":107,"line":108},[105,6454,129],{"class":111},[105,6456,163],{"class":115},[105,6458,482],{"class":119},[105,6460,137],{"class":111},[105,6462,487],{"class":140},[105,6464,490],{"class":119},[105,6466,137],{"class":111},[105,6468,495],{"class":140},[105,6470,123],{"class":111},[257,6472,6473,6487,6494,6504],{},[39,6474,6475,6478,6479,6482,6483,6486],{},[31,6476,6477],{},"width=device-width","：按设备的",[19,6480,6481],{},"逻辑宽度","排版；",[31,6484,6485],{},"initial-scale=1","：初始不缩放。",[39,6488,6489,6490,6493],{},"漏写这一行，手机会按 980px 宽排版再整体缩小，文字小到看不清，并且",[19,6491,6492],{},"所有媒体查询失效","——它看到的宽度永远是 980 而不是 375。",[39,6495,6496,6497,1173,6500,6503],{},"不要再写 ",[31,6498,6499],{},"user-scalable=no",[31,6501,6502],{},"maximum-scale=1","：禁止双指放大是可访问性错误，主流浏览器也已不再遵守。",[39,6505,6506],{},"与它配套的 CSS 是「流式布局 + 少量断点」，见 CSS 教程第十三章。",[323,6508],{"height":2301,"src":6509,"title":6510},"\u002Ftutorials\u002Fhtml\u002Fdemos\u002F16-head-seo.html","示例 16：head 元信息与分享卡片效果",[24,6512,6514],{"id":6513},"titledescriptioncanonical三件必写","title、description、canonical：三件必写",[344,6516,6517,6530],{},[706,6518,6519],{},[709,6520,6521,6524,6527],{},[712,6522,6523],{},"项",[712,6525,6526],{},"建议",[712,6528,6529],{},"常见失误",[722,6531,6532,6544,6557],{},[709,6533,6534,6538,6541],{},[727,6535,6536],{},[31,6537,289],{},[727,6539,6540],{},"每页唯一，30 字以内，核心词靠前，「文章名 - 站点名」格式",[727,6542,6543],{},"全站同一个标题；被截断；标题堆砌关键词",[709,6545,6546,6551,6554],{},[727,6547,6548],{},[31,6549,6550],{},"\u003Cmeta name=\"description\">",[727,6552,6553],{},"80–120 字，写给用户看的一句话摘要",[727,6555,6556],{},"缺失（引擎自己拼，通常很难看）；把整段正文粘进去",[709,6558,6559,6564,6567],{},[727,6560,6561],{},[31,6562,6563],{},"\u003Clink rel=\"canonical\">",[727,6565,6566],{},"同一内容的多个 URL 指向唯一规范地址",[727,6568,6569],{},"分页\u002F排序参数各自一条规范地址，导致权重分散",[15,6571,6572,6575,6576,6579,6580,6583,6584,6587,6588,6591],{},[31,6573,6574],{},"description"," 不直接提升排名，但它决定用户在你的摘要里看到什么，影响点击率。",[31,6577,6578],{},"robots"," 相关：内部搜索结果页、预览页写 ",[31,6581,6582],{},"noindex","；",[31,6585,6586],{},"hreflang"," 用于多语言版本互相声明，必须双向；",[31,6589,6590],{},"sitemap.xml"," 提交到搜索引擎站长平台。",[15,6593,6594,6595,6598,6599,6602,6603,6606,6607,6609],{},"静态站框架通常能自动产出这几项。本站在 Nuxt 里对应 ",[31,6596,6597],{},"useSeoMeta()","（页面级）与 ",[31,6600,6601],{},"nuxt.config.ts"," 的 ",[31,6604,6605],{},"site.url","——注意 ",[31,6608,6605],{}," 上线前必须换成真实域名，否则 canonical 和 sitemap 都会指向占位地址。",[24,6611,6613],{"id":6612},"open-graph-与分享卡片","Open Graph 与分享卡片",[97,6615,6617],{"className":99,"code":6616,"language":65,"meta":101,"style":101},"\u003Cmeta property=\"og:type\" content=\"article\">\n\u003Cmeta property=\"og:title\" content=\"HTML 教程：从第一个标签到语义化页面\">\n\u003Cmeta property=\"og:description\" content=\"19 章循序渐进，每章配可直接运行的示例。\">\n\u003Cmeta property=\"og:image\" content=\"https:\u002F\u002F你的域名\u002Fimg\u002Fhtml-cover.svg\">\n\u003Cmeta property=\"og:url\" content=\"https:\u002F\u002F你的域名\u002Ftutorials\u002Fhtml\">\n\u003Cmeta name=\"twitter:card\" content=\"summary_large_image\">\n",[31,6618,6619,6642,6664,6686,6708,6730],{"__ignoreMap":101},[105,6620,6621,6623,6625,6628,6630,6633,6635,6637,6640],{"class":107,"line":108},[105,6622,129],{"class":111},[105,6624,163],{"class":115},[105,6626,6627],{"class":119}," property",[105,6629,137],{"class":111},[105,6631,6632],{"class":140},"\"og:type\"",[105,6634,490],{"class":119},[105,6636,137],{"class":111},[105,6638,6639],{"class":140},"\"article\"",[105,6641,123],{"class":111},[105,6643,6644,6646,6648,6650,6652,6655,6657,6659,6662],{"class":107,"line":126},[105,6645,129],{"class":111},[105,6647,163],{"class":115},[105,6649,6627],{"class":119},[105,6651,137],{"class":111},[105,6653,6654],{"class":140},"\"og:title\"",[105,6656,490],{"class":119},[105,6658,137],{"class":111},[105,6660,6661],{"class":140},"\"HTML 教程：从第一个标签到语义化页面\"",[105,6663,123],{"class":111},[105,6665,6666,6668,6670,6672,6674,6677,6679,6681,6684],{"class":107,"line":146},[105,6667,129],{"class":111},[105,6669,163],{"class":115},[105,6671,6627],{"class":119},[105,6673,137],{"class":111},[105,6675,6676],{"class":140},"\"og:description\"",[105,6678,490],{"class":119},[105,6680,137],{"class":111},[105,6682,6683],{"class":140},"\"19 章循序渐进，每章配可直接运行的示例。\"",[105,6685,123],{"class":111},[105,6687,6688,6690,6692,6694,6696,6699,6701,6703,6706],{"class":107,"line":157},[105,6689,129],{"class":111},[105,6691,163],{"class":115},[105,6693,6627],{"class":119},[105,6695,137],{"class":111},[105,6697,6698],{"class":140},"\"og:image\"",[105,6700,490],{"class":119},[105,6702,137],{"class":111},[105,6704,6705],{"class":140},"\"https:\u002F\u002F你的域名\u002Fimg\u002Fhtml-cover.svg\"",[105,6707,123],{"class":111},[105,6709,6710,6712,6714,6716,6718,6721,6723,6725,6728],{"class":107,"line":176},[105,6711,129],{"class":111},[105,6713,163],{"class":115},[105,6715,6627],{"class":119},[105,6717,137],{"class":111},[105,6719,6720],{"class":140},"\"og:url\"",[105,6722,490],{"class":119},[105,6724,137],{"class":111},[105,6726,6727],{"class":140},"\"https:\u002F\u002F你的域名\u002Ftutorials\u002Fhtml\"",[105,6729,123],{"class":111},[105,6731,6732,6734,6736,6738,6740,6743,6745,6747,6750],{"class":107,"line":191},[105,6733,129],{"class":111},[105,6735,163],{"class":115},[105,6737,482],{"class":119},[105,6739,137],{"class":111},[105,6741,6742],{"class":140},"\"twitter:card\"",[105,6744,490],{"class":119},[105,6746,137],{"class":111},[105,6748,6749],{"class":140},"\"summary_large_image\"",[105,6751,123],{"class":111},[15,6753,6754,6755,1179,6758,6761,6762,6764],{},"要点：",[31,6756,6757],{},"og:image",[19,6759,6760],{},"必须是绝对 URL","，比例约 1.91:1（1200×630）；相对路径不会报错，只会让卡片没有图。部分平台不支持 SVG 作为 ",[31,6763,6757],{},"，稳妥做法是额外准备一份 JPG\u002FPNG。各平台都有官方校验工具（微信\u002F微博、Facebook Sharing Debugger、Twitter Card Validator），发布新模板时逐个跑一次。",[24,6766,6768],{"id":6767},"其它常用-head-元信息","其它常用 head 元信息",[257,6770,6771,6781,6791,6797,6807,6816],{},[39,6772,6773,6776,6777,6780],{},[31,6774,6775],{},"theme-color","：手机浏览器地址栏配色；给深色模式再补一条带 ",[31,6778,6779],{},"media=\"(prefers-color-scheme: dark)\""," 的。",[39,6782,6783,6786,6787,6790],{},[31,6784,6785],{},"link rel=\"icon\"","：标签页图标，同时保留 ",[31,6788,6789],{},"\u002Ffavicon.ico"," 兜底最省事。",[39,6792,6793,6796],{},[31,6794,6795],{},"link rel=\"alternate\" type=\"application\u002Frss+xml\"","：RSS 订阅地址。",[39,6798,6799,6802,6803,6806],{},[31,6800,6801],{},"link rel=\"preconnect\"","：只对",[19,6804,6805],{},"确定要请求","的第三方域名握手，加多了适得其反。",[39,6808,6809,1179,6812,6815],{},[31,6810,6811],{},"format-detection",[31,6813,6814],{},"telephone=no","：禁止 iOS 把一串数字变成电话链接。",[39,6817,6818,6821],{},[31,6819,6820],{},"\u003Cmeta http-equiv=\"refresh\">","：除非做跳转兜底，否则别用——它打断用户且读屏不友好。",[10,6823,6825],{"id":6824},"十七可访问性","十七、可访问性",[15,6827,6828],{},"可访问性（a11y）不是「给残障人士的额外功能」，而是键盘用户、色觉差异用户、强光下阅读、临时受伤（手腕扭伤只能用键盘）都能正常使用的底线要求。它也是 Lighthouse 审计里最容易批量失分的一项。",[24,6830,6831],{"id":6831},"键盘可达与焦点顺序",[15,6833,6834,6835,355,6837,355,6840,355,6843,355,6846,6848,6849,6851,6852,355,6855,6858,6859,6862,6863,6866],{},"原生可交互元素——",[31,6836,1697],{},[31,6838,6839],{},"\u003Ca href>",[31,6841,6842],{},"\u003Cinput>",[31,6844,6845],{},"\u003Cselect>",[31,6847,4179],{},"——天然能 Tab 到、天然响应 Enter\u002F空格。用 ",[31,6850,2539],{}," 冒充按钮时你需要自己补齐三件事：",[31,6853,6854],{},"tabindex=\"0\"",[31,6856,6857],{},"role=\"button\"","、以及监听 ",[31,6860,6861],{},"keydown"," 处理 Enter 与空格。",[19,6864,6865],{},"只要能用原生标签就用原生标签","，这是最省事也最不容易出错的做法。",[15,6868,6869,6872,6873,6876,6877,6880,6881,6884],{},[31,6870,6871],{},"tabindex"," 的三个取值要知道：",[31,6874,6875],{},"0"," 按 DOM 顺序入列；",[31,6878,6879],{},"-1"," 不能被 Tab 到但可被脚本聚焦（弹窗背景常用）；",[19,6882,6883],{},"正数","会打乱全局顺序，绝大多数场景都不该出现。",[15,6886,6887],{},"另外两条：模态弹窗打开时应把焦点移入并在内部循环（focus trap），关闭后归还到触发元素；「跳到主内容」的隐藏链接（首个焦点时露出）能替键盘用户省掉每次遍历整条导航。",[323,6889],{"height":2301,"src":6890,"title":6891},"\u002Ftutorials\u002Fhtml\u002Fdemos\u002F17-a11y.html","示例 17：键盘遍历、焦点样式与对比度",[24,6893,6894],{"id":6894},"焦点必须看得见",[15,6896,6897],{},"最严重也最常见的问题是抹掉焦点环：",[97,6899,6901],{"className":6091,"code":6900,"language":6093,"meta":101,"style":101},"\u002F* 错误：键盘用户直接失去定位能力 *\u002F\nbutton:focus { outline: none; }\n\n\u002F* 正确：保留焦点，但换成符合设计的外观 *\u002F\n:focus-visible { outline: 3px solid #0969da; outline-offset: 2px; }\n",[31,6902,6903,6908,6926,6930,6935],{"__ignoreMap":101},[105,6904,6905],{"class":107,"line":108},[105,6906,6907],{"class":2455},"\u002F* 错误：键盘用户直接失去定位能力 *\u002F\n",[105,6909,6910,6912,6915,6917,6920,6922,6924],{"class":107,"line":126},[105,6911,3420],{"class":115},[105,6913,6914],{"class":119},":focus",[105,6916,6189],{"class":111},[105,6918,6919],{"class":4505},"outline",[105,6921,6120],{"class":111},[105,6923,4868],{"class":4505},[105,6925,6235],{"class":111},[105,6927,6928],{"class":107,"line":146},[105,6929,3042],{"emptyLinePlaceholder":3041},[105,6931,6932],{"class":107,"line":157},[105,6933,6934],{"class":2455},"\u002F* 正确：保留焦点，但换成符合设计的外观 *\u002F\n",[105,6936,6937,6940,6942,6944,6946,6949,6952,6955,6958,6961,6964,6966,6968,6970],{"class":107,"line":176},[105,6938,6939],{"class":119},":focus-visible",[105,6941,6189],{"class":111},[105,6943,6919],{"class":4505},[105,6945,6120],{"class":111},[105,6947,6948],{"class":4505},"3",[105,6950,6951],{"class":4501},"px",[105,6953,6954],{"class":4505}," solid",[105,6956,6957],{"class":4505}," #0969da",[105,6959,6960],{"class":111},"; ",[105,6962,6963],{"class":4505},"outline-offset",[105,6965,6120],{"class":111},[105,6967,6232],{"class":4505},[105,6969,6951],{"class":4501},[105,6971,6235],{"class":111},[15,6973,6974,6976,6977,6980],{},[31,6975,6939],{}," 的好处是只在",[19,6978,6979],{},"键盘聚焦","时显示，鼠标点击不会留环，设计和可访问性都不用让步。本站的目录链接、搜索框用的就是这个伪类。",[24,6982,6984],{"id":6983},"aria-的第一定律与常用属性","aria 的第一定律与常用属性",[15,6986,6987,1179,6990,6992,6993,6995],{},[19,6988,6989],{},"第一定律：能用原生语义标签和属性做到的，就不要用 ARIA。",[31,6991,1697],{}," 已经是按钮，写 ",[31,6994,6857],{}," 只会增加出错面积。",[15,6997,6998],{},"确实需要 ARIA 的典型场景：",[344,7000,7001,7011],{},[706,7002,7003],{},[709,7004,7005,7007,7009],{},[712,7006,3775],{},[712,7008,3534],{},[712,7010,1827],{},[722,7012,7013,7032,7048,7063,7076,7093,7109,7126],{},[709,7014,7015,7022,7025],{},[727,7016,7017,1061,7020],{},[31,7018,7019],{},"aria-label",[31,7021,2704],{},[727,7023,7024],{},"给无名控件命名",[727,7026,7027,7028,7031],{},"图标按钮必用；",[31,7029,7030],{},"labelledby"," 指向已有可见文本更稳",[709,7033,7034,7038,7041],{},[727,7035,7036],{},[31,7037,6408],{},[727,7039,7040],{},"展开\u002F折叠状态",[727,7042,7043,7044,7047],{},"自绘折叠组件必须自己维护，原生 ",[31,7045,7046],{},"\u003Cdetails>"," 自动带",[709,7049,7050,7055,7058],{},[727,7051,7052],{},[31,7053,7054],{},"aria-controls",[727,7056,7057],{},"按钮与被控区块的关联",[727,7059,7060,7061],{},"值为目标 ",[31,7062,1481],{},[709,7064,7065,7070,7073],{},[727,7066,7067],{},[31,7068,7069],{},"aria-current=\"page\"",[727,7071,7072],{},"标出导航里的当前项",[727,7074,7075],{},"比只靠变色更明确",[709,7077,7078,7083,7086],{},[727,7079,7080],{},[31,7081,7082],{},"aria-live=\"polite\"",[727,7084,7085],{},"局部异步更新播报",[727,7087,7088,7089,7092],{},"表单校验结果、加载中提示；用 ",[31,7090,7091],{},"role=\"status\""," 更简洁",[709,7094,7095,7103,7106],{},[727,7096,7097,1338,7100],{},[31,7098,7099],{},"aria-invalid",[31,7101,7102],{},"aria-describedby",[727,7104,7105],{},"把错误信息与控件关联",[727,7107,7108],{},"不要只用红色描边表达错误",[709,7110,7111,7116,7119],{},[727,7112,7113],{},[31,7114,7115],{},"aria-hidden=\"true\"",[727,7117,7118],{},"从无障碍树里移除纯装饰元素",[727,7120,7121,7122,7125],{},"装饰性图标字体、内联 SVG 用；",[19,7123,7124],{},"绝不能","用在可聚焦元素上",[709,7127,7128,7134,7137],{},[727,7129,7130,7133],{},[31,7131,7132],{},"role=\"region\""," \u002F 地标",[727,7135,7136],{},"给长页面分区",[727,7138,7139,1003,7141,1003,7143,7145],{},[31,7140,2860],{},[31,7142,1337],{},[31,7144,2901],{}," 已自带，重复声明是冗余",[24,7147,7148],{"id":7148},"对比度与其它底线",[257,7150,7151,7162,7169,7172,7178],{},[39,7152,7153,7154,7157,7158,7161],{},"正文与背景对比度至少 ",[19,7155,7156],{},"4.5:1","（大号字 3:1）。把提示文字统一设成 ",[31,7159,7160],{},"#aaa"," 是很多站点批量失分的原因；图片上的文字要么加遮罩要么换到纯色区域。",[39,7163,7164,7165,7168],{},"尊重 ",[31,7166,7167],{},"prefers-reduced-motion","，为前庭功能敏感的用户关掉大幅位移动画（写法见 CSS 教程第十三章）。",[39,7170,7171],{},"移动端可点区域不小于 44×44 像素。",[39,7173,7174,7177],{},[31,7175,7176],{},"\u003Chtml lang=\"zh-CN\">"," 决定读屏的发音引擎（第二章）。",[39,7179,7180,7181,7184],{},"交付前自己走两遍：",[19,7182,7183],{},"只用 Tab 完成一次核心操作","；把宽度压到 375px 读一遍。这两步能发现九成问题，成本远低于任何自动化工具。",[10,7186,7188],{"id":7187},"十八常见坑与排错清单","十八、常见坑与排错清单",[15,7190,7191,7192,7195],{},"HTML 的错误绝大多数",[19,7193,7194],{},"不会报错","——它的设计目标就是「尽力理解你写的东西」。所以排查靠的是「对比实际 DOM 与预期 DOM」，而不是等控制台提示。",[24,7197,7198],{"id":7198},"块级与行内的混用",[15,7200,7201,7202,7204,7205,355,7207,355,7209,7211,7212,7217,7218,7204,7220,7222,7223,7225,7226,7228],{},"最高频的一类：在 ",[31,7203,57],{}," 里放 ",[31,7206,2539],{},[31,7208,57],{},[31,7210,1121],{}," 等块级元素。浏览器会",[19,7213,7214,7215],{},"自动闭合前面的 ",[31,7216,57],{},"，于是你以为的一段变成三段，缩进和间距全乱。同类问题还有 ",[31,7219,2543],{},[31,7221,2539],{},"（不合法但会被纠正）、",[31,7224,1345],{}," 里套 ",[31,7227,1345],{},"（内层被踢出）。",[323,7230],{"height":2301,"src":7231,"title":7232},"\u002Ftutorials\u002Fhtml\u002Fdemos\u002F18-pitfalls.html","示例 18：坑清单与排错路径",[24,7234,7235],{"id":7235},"未闭合标签与浏览器的就近纠正",[15,7237,7238,7240,7241,7243,7244,1173,7246,7249,7250,355,7252,7254,7255,7257],{},[31,7239,1141],{}," 省略 ",[31,7242,1186],{}," 在 HTML 里是合法的（下一个 ",[31,7245,1141],{},[31,7247,7248],{},"\u003C\u002Ful>"," 会隐式闭合它），但 ",[31,7251,2539],{},[31,7253,2875],{}," 这类没有隐式闭合规则的标签漏掉闭合，就会吞掉后续一大块内容——表现为「下半页莫名其妙变成了一列」。交叉嵌套 ",[31,7256,2771],{}," 会被就近纠正成两对，结果与预期完全不同。",[15,7259,7260,7261,7264],{},"发现办法：Elements 面板看",[19,7262,7263],{},"实际","层级（与源码对不上就是被纠正过），或者把文件交给 W3C Nu 校验器，它会直接指出「element not allowed as child of …」。",[24,7266,7267],{"id":7267},"表格与表单的典型错误",[344,7269,7270,7282],{},[706,7271,7272],{},[709,7273,7274,7276,7279],{},[712,7275,714],{},[712,7277,7278],{},"真实原因",[712,7280,7281],{},"修法",[722,7283,7284,7302,7324,7346,7368,7390,7408,7422],{},[709,7285,7286,7289,7294],{},[727,7287,7288],{},"表格列整体错位",[727,7290,7291,7292],{},"合并单元格后没删被盖住的 ",[31,7293,2261],{},[727,7295,7296,7297,1003,7299,7301],{},"数每行列数，",[31,7298,2363],{},[31,7300,2366],{}," 计入被占位的那几列",[709,7303,7304,7307,7314],{},[727,7305,7306],{},"读屏只念数字",[727,7308,7309,7311,7312],{},[31,7310,2265],{}," 缺 ",[31,7313,2313],{},[727,7315,7316,7317,7320,7321],{},"列表头 ",[31,7318,7319],{},"scope=\"col\"","，行表头 ",[31,7322,7323],{},"scope=\"row\"",[709,7325,7326,7329,7337],{},[727,7327,7328],{},"后端收不到某字段",[727,7330,7331,7332,7334,7335],{},"控件只有 ",[31,7333,1481],{}," 没有 ",[31,7336,3357],{},[727,7338,7339,7340,7342,7343,7345],{},"补 ",[31,7341,3357],{},"，",[31,7344,1481],{}," 与提交无关",[709,7347,7348,7351,7362],{},[727,7349,7350],{},"点标题不聚焦",[727,7352,7353,290,7356,7358,7359,7361],{},[31,7354,7355],{},"label for",[31,7357,1481],{}," 不一致，或 ",[31,7360,1481],{}," 重复",[727,7363,7364,7365,7367],{},"成对检查；重复 ",[31,7366,1481],{}," 只命中第一个",[709,7369,7370,7373,7381],{},[727,7371,7372],{},"按钮一点就整页刷新",[727,7374,7375,7376,7378,7379],{},"表单里 ",[31,7377,1697],{}," 默认 ",[31,7380,3502],{},[727,7382,7383,7384,7386,7387],{},"非提交按钮写 ",[31,7385,3506],{},"，JS 里 ",[31,7388,7389],{},"preventDefault()",[709,7391,7392,7395,7402],{},[727,7393,7394],{},"单选组能同时选两个",[727,7396,3715,7397,6602,7399,7401],{},[31,7398,3709],{},[31,7400,3357],{}," 不同",[727,7403,7404,7405,7407],{},"一组同名，",[31,7406,3791],{}," 区分选项",[709,7409,7410,7413,7416],{},[727,7411,7412],{},"图片不显示",[727,7414,7415],{},"路径 404 \u002F 漏了部署子路径前缀",[727,7417,7418,7419],{},"先看 Network，再看是不是写了 ",[31,7420,7421],{},"..\u002F..\u002F",[709,7423,7424,7427,7430],{},[727,7425,7426],{},"百分比高度为 0",[727,7428,7429],{},"父级没有确定高度，或进了怪异模式",[727,7431,1321,7432,1003,7435,6583,7437,7439],{},[31,7433,7434],{},"min-height",[31,7436,5489],{},[31,7438,762],{}," 检查",[24,7441,7442],{"id":7442},"排错的固定路径",[36,7444,7445,7451,7457,7463,7469,7480],{},[39,7446,7447,7450],{},[19,7448,7449],{},"关掉浏览器扩展","再刷新（广告拦截、隐私插件是「本地正常别人打不开」的第一大来源）。",[39,7452,7453,7456],{},[19,7454,7455],{},"Console 看红字，Network 看红条","：路径、接口、CORS 问题一次现形。",[39,7458,7459,7462],{},[19,7460,7461],{},"Elements 看实际 DOM，Computed 看最终样式","：你在源码里写的顺序不是浏览器的理解结果。",[39,7464,7465,7468],{},[19,7466,7467],{},"最小复现","：把可疑片段复制到独立的空白 HTML 里，只留最小结构，逐步加回。",[39,7470,7471,7474,7475,7477,7478,612],{},[19,7472,7473],{},"跑一遍校验与审计","：Nu 校验器查结构，Lighthouse 查对比度、缺 ",[31,7476,2754],{},"、缺 ",[31,7479,181],{},[39,7481,7482,7485],{},[19,7483,7484],{},"真机验证","：375px 宽度 + 只用键盘走一遍核心流程。",[10,7487,7489],{"id":7488},"十九综合实战用语义标签重写一个页面","十九、综合实战：用语义标签重写一个页面",[15,7491,7492,7493,7496],{},"前面十八章的知识在这一章一次性用掉。下面两个示例是",[19,7494,7495],{},"同一个页面的两个版本","，视觉效果一致，差别全在结构上。先用浏览器打开看两边是否真的长得差不多，再对照源码。",[24,7498,7499],{"id":7499},"先看清旧结构",[323,7501],{"height":2301,"src":7502,"title":7503},"\u002Ftutorials\u002Fhtml\u002Fdemos\u002F19-refactor-before.html","示例 19：改造前的 div 布局",[15,7505,7506,7507,1003,7509,7511,7512,7515],{},"这份代码的可见问题是：整页只有 ",[31,7508,2504],{},[31,7510,105],{},"；导航和「阅读全文」是上了色的文字，",[19,7513,7514],{},"没有任何可聚焦元素","；日期是纯文本；样式全部内联。它在视觉上完全正常，但读屏用户拿不到地标列表，键盘用户一步都走不了，搜索引擎抽正文时会把友情链接一起抽走。",[24,7517,7519],{"id":7518},"重写从地标开始","重写：从地标开始",[323,7521],{"height":2301,"src":7522,"title":7523},"\u002Ftutorials\u002Fhtml\u002Fdemos\u002F19b-refactor-after.html","示例 19b：改造后的语义结构",[15,7525,7526,7527,270],{},"重写的顺序值得记住，它和写 CSS 的顺序相反——",[19,7528,7529],{},"先结构后外观",[36,7531,7532,7550,7566,7578,7585,7597],{},[39,7533,7534,7535,7537,7538,7540,7541,355,7544,355,7546,355,7548,612],{},"先放五个地标：",[31,7536,2968],{},"（含 ",[31,7539,216],{},"）、",[31,7542,7543],{},"nav aria-label=\"主导航\"",[31,7545,3049],{},[31,7547,3190],{},[31,7549,3144],{},[39,7551,7552,7554,7555,7558,7559,7561,7562,7565],{},[31,7553,2990],{}," 里放真正的 ",[31,7556,7557],{},"ul > li > a","，当前页给 ",[31,7560,7069],{},"；链接必须是 ",[31,7563,7564],{},"a[href]","，不要用 div 上色冒充。",[39,7567,7568,7569,7571,7572,7574,7575,7577],{},"每一篇可独立分发的内容给一个 ",[31,7570,3058],{},"，标题用 ",[31,7573,24],{}," 并把整条标题做成链接（",[31,7576,1519],{}," 可以包块级内容，卡片整体可点就是这么实现的）。",[39,7579,7580,7581,7584],{},"日期用 ",[31,7582,7583],{},"\u003Ctime datetime=\"2026-09-20\">","，机器读得到、人看到的是中文。",[39,7586,7587,7588,1003,7590,7592,7593,1338,7595,612],{},"并列的目录项用 ",[31,7589,257],{},[31,7591,36],{},"，并给承载它的分区加 ",[31,7594,10],{},[31,7596,2704],{},[39,7598,7599,7600,7603],{},"样式全部收进类名（BEM 风格），去掉内联 ",[31,7601,7602],{},"style","——这一步属于 CSS 教程，但改造时顺手做完最省事。",[15,7605,7606,7607,270,7613,7615,7616,7342,7619,7615,7621,7624,7625,355,7627,355,7629,5216],{},"注意改造后",[19,7608,7609,7610],{},"没有新增一个 ",[31,7611,7612],{},"role",[31,7614,2860],{}," 已隐含 ",[31,7617,7618],{},"role=\"main\"",[31,7620,1337],{},[31,7622,7623],{},"role=\"navigation\"","，重复声明是冗余。ARIA 只用来补原生标签补不了的信息（",[31,7626,6411],{},[31,7628,7019],{},[31,7630,2704],{},[24,7632,7633],{"id":7633},"对照检查清单",[15,7635,7636],{},"改完一个页面，按这七条逐项打勾：",[257,7638,7641,7648,7654,7668,7680,7691,7703],{"className":7639},[7640],"contains-task-list",[39,7642,7645,7647],{"className":7643},[7644],"task-list-item",[3834,7646],{"disabled":3041,"type":3706}," 只用 Tab 能从页头走到页尾，每一步都看得见焦点环。",[39,7649,7651,7653],{"className":7650},[7644],[3834,7652],{"disabled":3041,"type":3706}," 读屏的地标列表里能看到 导航 \u002F 主内容 \u002F 侧栏 \u002F 内容信息 四项。",[39,7655,7657,7659,7660,7662,7663,7665,7666,5216],{"className":7656},[7644],[3834,7658],{"disabled":3041,"type":3706}," 页面只有一个 ",[31,7661,293],{},"，标题层级不跳跃（",[31,7664,10],{}," 之后不直接出现 ",[31,7667,895],{},[39,7669,7671,7673,7674,7676,7677,7679],{"className":7670},[7644],[3834,7672],{"disabled":3041,"type":3706}," 所有图片都有 ",[31,7675,1779],{},"，装饰图的 ",[31,7678,1786],{}," 而不是省略。",[39,7681,7683,7685,7686,7688,7689,612],{"className":7682},[7644],[3834,7684],{"disabled":3041,"type":3706}," 所有图标按钮都有 ",[31,7687,7019],{},"，所有表单控件都有绑定的 ",[31,7690,3910],{},[39,7692,7694,7696,7697,7700,7701,612],{"className":7693},[7644],[3834,7695],{"disabled":3041,"type":3706}," 每个 ",[31,7698,7699],{},"\u003Ciframe>"," 都有 ",[31,7702,181],{},[39,7704,7706,7708],{"className":7705},[7644],[3834,7707],{"disabled":3041,"type":3706}," 正文文字与背景对比度 ≥ 4.5:1。",[15,7710,7711,7712,7715],{},"到这里 HTML 教程完结。接下来建议直接进入 ",[19,7713,7714],{},"CSS 教程","：本教程里所有「交给 CSS」的部分（布局、单位、颜色、响应式、主题变量）都在那边展开。",[7602,7717,7718],{},"html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .s4JwU, html code.shiki .s4JwU{--shiki-default:#85E89D}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":101,"searchDepth":126,"depth":126,"links":7720},[7721,7727,7733,7739,7745,7751,7756,7762,7767,7773,7779,7785,7790,7796,7802,7808,7814,7820,7826],{"id":12,"depth":126,"text":13,"children":7722},[7723,7724,7725,7726],{"id":26,"depth":146,"text":26},{"id":92,"depth":146,"text":92},{"id":297,"depth":146,"text":297},{"id":330,"depth":146,"text":330},{"id":348,"depth":126,"text":349,"children":7728},[7729,7730,7731,7732],{"id":366,"depth":146,"text":367},{"id":431,"depth":146,"text":432},{"id":637,"depth":146,"text":638},{"id":702,"depth":146,"text":702},{"id":791,"depth":126,"text":792,"children":7734},[7735,7736,7737,7738],{"id":806,"depth":146,"text":807},{"id":864,"depth":146,"text":865},{"id":911,"depth":146,"text":912},{"id":1017,"depth":146,"text":1018},{"id":1095,"depth":126,"text":1096,"children":7740},[7741,7742,7743,7744],{"id":1099,"depth":146,"text":1100},{"id":1167,"depth":146,"text":1167},{"id":1278,"depth":146,"text":1279},{"id":1316,"depth":146,"text":1316},{"id":1356,"depth":126,"text":1357,"children":7746},[7747,7748,7749,7750],{"id":1369,"depth":146,"text":1370},{"id":1474,"depth":146,"text":1475},{"id":1564,"depth":146,"text":1565},{"id":1646,"depth":146,"text":1646},{"id":1704,"depth":126,"text":1705,"children":7752},[7753,7754,7755],{"id":1711,"depth":146,"text":1712},{"id":1812,"depth":146,"text":1812},{"id":2027,"depth":146,"text":2027},{"id":2061,"depth":126,"text":2062,"children":7757},[7758,7759,7760,7761],{"id":2074,"depth":146,"text":2074},{"id":2306,"depth":146,"text":2307},{"id":2354,"depth":146,"text":2355},{"id":2489,"depth":146,"text":2489},{"id":2533,"depth":126,"text":2534,"children":7763},[7764,7765,7766],{"id":2554,"depth":146,"text":2555},{"id":2683,"depth":146,"text":2684},{"id":2765,"depth":146,"text":2765},{"id":2809,"depth":126,"text":2810,"children":7768},[7769,7770,7771,7772],{"id":2813,"depth":146,"text":2813},{"id":2945,"depth":146,"text":2945},{"id":3284,"depth":146,"text":3285},{"id":3335,"depth":146,"text":3336},{"id":3346,"depth":126,"text":3347,"children":7774},[7775,7776,7777,7778],{"id":3370,"depth":146,"text":3371},{"id":3514,"depth":146,"text":3515},{"id":3795,"depth":146,"text":3796},{"id":3914,"depth":146,"text":3915},{"id":4230,"depth":126,"text":4231,"children":7780},[7781,7782,7783,7784],{"id":4241,"depth":146,"text":4242},{"id":4324,"depth":146,"text":4325},{"id":4465,"depth":146,"text":4466},{"id":4656,"depth":146,"text":4656},{"id":4724,"depth":126,"text":4725,"children":7786},[7787,7788,7789],{"id":4738,"depth":146,"text":4739},{"id":4891,"depth":146,"text":4892},{"id":5029,"depth":146,"text":5030},{"id":5232,"depth":126,"text":5233,"children":7791},[7792,7793,7794,7795],{"id":5239,"depth":146,"text":5240},{"id":5457,"depth":146,"text":5458},{"id":5479,"depth":146,"text":5479},{"id":5570,"depth":146,"text":5571},{"id":5617,"depth":126,"text":5618,"children":7797},[7798,7799,7800,7801],{"id":5621,"depth":146,"text":5621},{"id":5783,"depth":146,"text":5783},{"id":5812,"depth":146,"text":5812},{"id":5841,"depth":146,"text":5842},{"id":5958,"depth":126,"text":5959,"children":7803},[7804,7805,7806,7807],{"id":5975,"depth":146,"text":5975},{"id":6080,"depth":146,"text":6081},{"id":6257,"depth":146,"text":6258},{"id":6381,"depth":146,"text":6382},{"id":6435,"depth":126,"text":6436,"children":7809},[7810,7811,7812,7813],{"id":6444,"depth":146,"text":6445},{"id":6513,"depth":146,"text":6514},{"id":6612,"depth":146,"text":6613},{"id":6767,"depth":146,"text":6768},{"id":6824,"depth":126,"text":6825,"children":7815},[7816,7817,7818,7819],{"id":6831,"depth":146,"text":6831},{"id":6894,"depth":146,"text":6894},{"id":6983,"depth":146,"text":6984},{"id":7148,"depth":146,"text":7148},{"id":7187,"depth":126,"text":7188,"children":7821},[7822,7823,7824,7825],{"id":7198,"depth":146,"text":7198},{"id":7235,"depth":146,"text":7235},{"id":7267,"depth":146,"text":7267},{"id":7442,"depth":146,"text":7442},{"id":7488,"depth":126,"text":7489,"children":7827},[7828,7829,7830],{"id":7499,"depth":146,"text":7499},{"id":7518,"depth":146,"text":7519},{"id":7633,"depth":146,"text":7633},"\u002Fimg\u002Fhtml-cover.svg","2026-10-03","零基础入门 HTML：常用标签、表单、语义化结构与可访问性，19 章循序渐进，每章配可运行示例。","md",{},"\u002Ftutorials\u002Fhtml",{"title":5,"description":7833},{"loc":7836},"tutorials\u002Fhtml","xbi6vPA4SqQAQKTKllHLKIavNNepz5BrWgvqIolj6qw",[7842,7844,7848,7852,7856,7860,7864,7868,7872,7876,7880,7884,7888,7892,7896,7900,7904,7907,7911,7915,7919,7923,7927,7931,7935,7939,7943,7946,7950,7954,7958,7962,7966,7970,7974,7978,7982,7986,7990,7994,7997,8001,8005,8009,8013,8017,8021,8025,8029,8033,8037,8041,8045,8049,8053,8057,8061,8065,8069,8072,8076,8080,8084,8088,8091,8095,8099,8103,8107,8111,8115,8119,8123,8127,8131,8135,8139,8143,8147,8150,8154,8158,8162,8166,8170,8174,8178,8182,8186,8190,8194,8198],{"id":7836,"title":5,"titles":7843,"content":7833,"level":108},[],{"id":7845,"title":13,"titles":7846,"content":7847,"level":126},"\u002Ftutorials\u002Fhtml#一html-是什么你的第一个页面",[5],"HTML 不是编程语言，而是标记语言：它用一对对尖括号包裹的「标签」告诉浏览器，某一段文字是标题、某一段是段落、某一张图可以点开。理解这一点，后面所有标签的学习都会变成「这个标签表达什么含义」的问题，而不是「这个标签怎么写样式」的问题——样式是 CSS 的事。",{"id":7849,"title":26,"titles":7850,"content":7851,"level":146},"\u002Ftutorials\u002Fhtml#浏览器把一个文件变成画面的过程",[5,13],"你在地址栏输入一个 .html 文件，浏览器大致做四件事： 拿到字节流：按 \u003Cmeta charset> 声明的编码（通常是 UTF-8）把字节解码成文字。声明写错，中文就会变成「æ¬å」这类乱码。 解析成节点树：逐字符读标签，遇到 \u003Cp> 就开一个节点，遇到 \u003C\u002Fp> 就闭合，层层嵌套形成「DOM 树」。树根是 html 节点。 构建渲染树：结合 CSS 决定哪些节点可见、以什么顺序和位置排布。 绘制到屏幕：按层叠顺序一笔画一画涂出来。 这条链路解释了后面会反复遇到的两个现象：标签嵌套顺序决定结构，而书写顺序不决定视觉层级（那是 CSS 定位的事）。",{"id":7853,"title":92,"titles":7854,"content":7855,"level":146},"\u002Ftutorials\u002Fhtml#一个页面由三部分拼起来",[5,13],"任何 HTML 文件都可以拆成三层，先看最小可用版本： \u003C!DOCTYPE html>\n\u003Chtml lang=\"zh-CN\">\n  \u003Chead>\n    \u003Cmeta charset=\"UTF-8\">\n    \u003Ctitle>我的第一个网页\u003C\u002Ftitle>\n  \u003C\u002Fhead>\n  \u003Cbody>\n    \u003Ch1>我的第一个网页\u003C\u002Fh1>\n    \u003Cp>这一行是段落。\u003C\u002Fp>\n  \u003C\u002Fbody>\n\u003C\u002Fhtml> \u003C!DOCTYPE html>：告诉浏览器「按现代标准模式渲染」，第二章细说。 \u003Chead>：给浏览器和搜索引擎看的元信息，标题、编码、样式表引用都放这里，不会显示在页面正文里。 \u003Cbody>：给人看的全部内容，页面上你看到的每一个字都在这个标签内部。 新手最容易混淆的是 \u003Ctitle> 与 \u003Ch1>：前者出现在浏览器标签页和搜索结果里，后者出现在页面正文顶部。两者都要写，作用完全不同。",{"id":7857,"title":297,"titles":7858,"content":7859,"level":146},"\u002Ftutorials\u002Fhtml#写下你的第一个文件",[5,13],"准备一个空文件夹，用任意文本编辑器（记事本、VS Code 都行）新建 index.html，把上面那段代码粘进去，保存后双击文件，它就会在自己的浏览器里打开。 改一行 \u003Cp> 里的文字，保存，回到浏览器按刷新——这就是 HTML 的开发循环，不需要任何编译工具。把文件拖进 VS Code 再按 F1 搜「Open with Live Server」（需装插件）可以省掉手动刷新。 下面这个框里就是上面那段代码的真实渲染结果，右边目录的小节、左侧的章节导航，以及正文里所有类似的可运行示例，都是独立文件，点「在新标签页打开」可以单独查看：",{"id":7861,"title":330,"titles":7862,"content":7863,"level":146},"\u002Ftutorials\u002Fhtml#接下来往哪走",[5,13],"从第二章开始按顺序推进：先解决「文件为什么这么开头」（文档结构），再补齐文字、列表、链接、图片、表格、表单这些内容型标签，然后进入结构型标签与语义化，最后是可访问性、SEO 与综合实战。每一章都配可运行示例，看懂了就用，忘了就回来查——正文左侧是章节目录，也可以在搜索框里直接输标签名（例如 table）定位到讲它的那一节。",{"id":7865,"title":349,"titles":7866,"content":7867,"level":126},"\u002Ftutorials\u002Fhtml#二文档结构与-doctype",[5],"第一章的最小示例里有四个容易被跳过但不该跳过的部分：\u003C!DOCTYPE html>、\u003Chtml lang>、\u003Chead>、\u003Cmeta charset>。它们不产出任何可见内容，却决定了页面「被怎么解读」。",{"id":7869,"title":367,"titles":7870,"content":7871,"level":146},"\u002Ftutorials\u002Fhtml#doctype-决定浏览器用哪套规则排版",[5,349],"\u003C!DOCTYPE html> 不是标签，而是一条处理指令，作用只有一个：让浏览器进入「标准模式（standards mode）」。 在 2000 年前后，浏览器为了兼容当年写得很不规范的网页，保留了两套排版引擎行为。没有 DOCTYPE（或写法是老式的一长串）时，浏览器会退回「怪异模式（quirks mode）」，其中的差异至今还能测出来： 怪异模式下元素的 box-sizing 行为、表格里的字体继承、\u003Cimg> 的基线对齐都与标准模式不同； 最直观的一条：标准模式下 html { height: 100% } 才让百分比高度逐级生效，怪异模式里同样的写法可能得到 0 高度。 现代写法只有一种，且必须放在文件最前面（前面除了空白和注释不能有任何内容，否则浏览器会当成文本节点渲染到页面上）： \u003C!DOCTYPE html> 看到搜索结果里有 \u003Cmeta name=\"generator\" content=\"FrontPage.Editor.1\"> 这类古董 DOCTYPE 的教程，可以直接关掉那个页面——它们教的正是怪异模式。",{"id":7873,"title":432,"titles":7874,"content":7875,"level":146},"\u002Ftutorials\u002Fhtml#htmlheadbody-各自负责什么",[5,349],"\u003Chtml lang=\"zh-CN\">\n  \u003Chead>\n    \u003Cmeta charset=\"UTF-8\">\n    \u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n    \u003Ctitle>页面标题，出现在标签页和搜索结果里\u003C\u002Ftitle>\n    \u003Clink rel=\"stylesheet\" href=\"style.css\">\n  \u003C\u002Fhead>\n  \u003Cbody>\n    \u003Ch1>正文从这开始\u003C\u002Fh1>\n  \u003C\u002Fbody>\n\u003C\u002Fhtml> 三条边界值得记住： \u003Chead> 里放的是「关于页面的信息」：编码、标题、样式表、图标、SEO 元信息。\u003Chead> 里出现 \u003Cp>、\u003Ch1> 这类可见元素是不合法的，浏览器会把它「揪」到 body 开头，导致难以排查的错位。 \u003Cbody> 里放全部可见内容，且一个文档只能有一个 \u003Cbody>。 \u003Chtml> 是根节点，所有 CSS\u002FJS 操作 DOM 时面对的树根就是它；lang 写在它上面才能被辅助技术和浏览器识别（第十七、十八章会再用到这一点）。 浏览器解析时会自动补全缺失标签：即使你只写 \u003Cbody> 内容而不写 \u003Chtml>，DevTools 里的 Elements 面板仍然显示完整的树。但「浏览器能补救」不等于「写错了没代价」——补救规则让结构推断变得不可预期，也让别人接手时读不出意图。",{"id":7877,"title":638,"titles":7878,"content":7879,"level":146},"\u002Ftutorials\u002Fhtml#charset-与-lang两个必须显式声明的属性",[5,349],"\u003Cmeta charset=\"UTF-8\"> 声明字节到文字的解码方式。它有两个实操要点： 位置要尽量靠前，且在 \u003Ctitle> 之前。浏览器为了省时间，只会读取文档开头的有限字节来找编码声明；放得太晚，标题就可能乱码。 声明只管「浏览器怎么读」，文件本身也得真的存成 UTF-8。VS Code 右下角能看到并修改当前编码；用记事本保存时选「UTF-8」。声明与实际编码不一致，改声明没用，改存储编码才有用。 lang 的取值是 BCP 47 语言标签：简体中文 zh-CN、繁体中文 zh-TW、英文 en、日文 ja。它影响屏幕阅读器发音、浏览器翻译提示、连字与换行规则（CSS 的 :lang() 选择器也读它，CSS 教程的选择器一章里会用到）。 下面这个示例把本节要点都放进去了，右上角可以直接看它自己的 \u003Chead> 写了什么：",{"id":7881,"title":702,"titles":7882,"content":7883,"level":146},"\u002Ftutorials\u002Fhtml#常见报错与自查",[5,349],"现象 大概率原因 怎么确认 中文变乱码 文件实际编码不是 UTF-8 编辑器右下角看编码，另存为 UTF-8 页面顶部多出一行文字 DOCTYPE 前后有内容，或首行漏写 查看源码第一行是否为 \u003C!DOCTYPE html> 百分比高度不生效、间距莫名 浏览器进了怪异模式 DevTools Console 输入 document.compatMode，标准模式返回 CSS1Compat，怪异模式返回 BackCompat 屏幕阅读器读中文很怪 \u003Chtml> 上没有 lang 查看源码 \u003Chtml> 标签",{"id":7885,"title":792,"titles":7886,"content":7887,"level":126},"\u002Ftutorials\u002Fhtml#三文本与段落标签",[5],"从这一章起进入「内容型标签」。判断一个标签该不该用，先问：它表达的是结构含义还是视觉效果？前者归 HTML，后者归 CSS。",{"id":7889,"title":807,"titles":7890,"content":7891,"level":146},"\u002Ftutorials\u002Fhtml#pbrhr-的分工",[5,792],"\u003Cp> 是「一个语意段落」，浏览器默认给它上下外边距。段落之间空一行是排版结果，不是你把回车敲出来的。 \u003Cbr> 只表示「同一句话内部必须换行」，典型场景是地址、诗歌、签名。它不产生新段落，所以段间距不会变大。 \u003Chr> 表示「主题转换的分隔」，比如一章结束、话题跳到另一个。它语义上有意义，不是装饰线——想画一条线请交给 CSS 的 border。 HTML 会把源码里连续的空白字符（空格、制表符、回车）折叠成一个空格，这叫空白折叠。所以你在 \u003Cp> 里敲十个回车也不会出现空行。想保留原始空白要用 \u003Cpre>（它还保留缩进，常用于代码块），想要真正的空格请用实体 &nbsp;。 下面这个示例把这三条都做实了，包括「空白折叠验证」和 \u003Cpre> 的对比：",{"id":7893,"title":865,"titles":7894,"content":7895,"level":146},"\u002Ftutorials\u002Fhtml#h1h6层级不是字号",[5,792],"六个标题标签构成文档的大纲，屏幕阅读器和浏览器的「页面概览」都读它。规则只有两条： 一页可以有多个 \u003Ch1>，但通常只放一个（页面主标题），从 \u003Ch2> 开始往下分章节。 不要跳层级：h2 之后直接写 h4 会在大纲里留下空洞。 新手最常见的错误是「觉得这行字要小一点，所以用 h5」。层级和字号是两件事，字号交给 CSS 改；用错层级只是把可访问性弄坏了。",{"id":7897,"title":912,"titles":7898,"content":7899,"level":146},"\u002Ftutorials\u002Fhtml#strongem-与-bi-的语义差别",[5,792],"标签 含义 屏幕阅读器 默认视觉 \u003Cstrong> 内容重要 加重语气播报 加粗 \u003Cem> 语气重音 换语调播报 斜体 \u003Cb> 仅视觉偏移（产品名、关键词） 不特别处理 加粗 \u003Ci> 仅视觉偏移（术语、外文、物种拉丁名） 不特别处理 斜体 判断办法：如果把这段文字的强调效果去掉，意思有没有变？变了就是 strong\u002Fem，没变就是 b\u002Fi，而纯粹「想让它显眼」应该用 CSS。",{"id":7901,"title":1018,"titles":7902,"content":7903,"level":146},"\u002Ftutorials\u002Fhtml#代码引用与其余文本标签",[5,792],"正文里还有几个高频标签值得一次记牢： \u003Ccode> 行内代码，通常包在 \u003Cpre> 里做成代码块：\u003Cpre>\u003Ccode>…\u003C\u002Fcode>\u003C\u002Fpre>。 \u003Cabbr title=\"Document Object Model\">DOM\u003C\u002Fabbr> 首次出现的缩写给它全称，鼠标悬停和读屏都能拿到。 \u003Cblockquote> 引用整段，\u003Cq> 引用句内；\u003Ccite> 标出处。 \u003Cdel> \u002F \u003Cins> 标删除与插入（价格改动、修订记录），带语义而非单纯划线。 \u003Cs> 表示「已不准确」，跟 \u003Cdel>（内容被删除）不同。 \u003Csub> \u002F \u003Csup> 下标上标，H\u003Csub>2\u003C\u002Fsub>O。 \u003Ctime datetime=\"2026-10-03\">10 月 3 日\u003C\u002Ftime> 给人看中文日期，给机器读 ISO 格式，搜索引擎会用它。",{"id":7905,"title":1096,"titles":7906,"content":101,"level":126},"\u002Ftutorials\u002Fhtml#四列表",[5],{"id":7908,"title":1100,"titles":7909,"content":7910,"level":146},"\u002Ftutorials\u002Fhtml#ul-与-ol顺序有没有意义",[5,1096],"判断只需一个问题：换一个顺序，内容还成立吗？ 不成立（安装步骤、排行榜、法律条款编号）→ \u003Col>，序号本身携带信息。 成立（特性清单、导航项、标签云）→ \u003Cul>。 \u003Col> 的三个属性很实用：type=\"A|a|I|i|1\" 换编号样式，start=\"5\" 指定起始序号，reversed 倒序。给单个 \u003Cli> 写 value=\"10\" 可以插入自定义序号。 圆点和数字都由 CSS 的 list-style 控制，「不想要圆点」不是放弃 \u003Cul> 的理由——写 list-style: none 即可，语义保留。",{"id":7912,"title":1167,"titles":7913,"content":7914,"level":146},"\u002Ftutorials\u002Fhtml#嵌套列表的正确位置",[5,1096],"嵌套的唯一规则：新的 \u003Cul> 或 \u003Col> 必须放在父 \u003Cli> 内部、文本之后、\u003C\u002Fli> 之前。 \u003Cul>\n  \u003Cli>支持 UTF-8\n    \u003Cul>\n      \u003Cli>中文标题不乱码\u003C\u002Fli>\n    \u003C\u002Ful>\n  \u003C\u002Fli>\n  \u003Cli>支持表格\u003C\u002Fli>\n\u003C\u002Ful> 如果误把子列表写在 \u003C\u002Fli> 之后，它会变成和父列表同级，缩进全乱——而且浏览器不会报错，这是最难自查的一类问题。",{"id":7916,"title":1279,"titles":7917,"content":7918,"level":146},"\u002Ftutorials\u002Fhtml#dldtdd术语表专用",[5,1096],"\u003Cdl> 是「描述列表」，\u003Cdt> 名词、\u003Cdd> 解释，适合 API 参数表、FAQ、词典条目。一个 \u003Cdt> 可以跟多个 \u003Cdd>（一词多义），也可以多个 \u003Cdt> 共用一个 \u003Cdd>（多词一义）。用 \u003Cp> 假装术语表是常见错误，因为它丢掉了「这是配对关系」的结构信息。",{"id":7920,"title":1316,"titles":7921,"content":7922,"level":146},"\u002Ftutorials\u002Fhtml#列表的三处常见误用",[5,1096],"用 \u003Cbr> 或连续 \u003Cp> 堆清单——读屏会播报「列表，共 3 项」，帮你数位置，堆出来的没有。 站内导航写成 \u003Cdiv>\u003Ca>。导航的正确形状是 \u003Cnav> + \u003Cul> + \u003Cli> + \u003Ca>，第九章展开。 表格数据用列表写、列表用 \u003Ctable> 写。判断标准：横竖都有对应关系、需要表头才能读懂 → 表格；否则列表。第七章细说。",{"id":7924,"title":1357,"titles":7925,"content":7926,"level":126},"\u002Ftutorials\u002Fhtml#五超链接-a",[5],"\u003Ca> 是 HTML 里唯一让「文档」变成「网络」的标签——href 指向另一个位置，浏览器就把两张图连成一条边。",{"id":7928,"title":1370,"titles":7929,"content":7930,"level":146},"\u002Ftutorials\u002Fhtml#href-的四种路径写法",[5,1357],"假设当前页面在 \u002Fblog\u002F2026\u002Fintro.html： 写法 实际指向 什么时候用 other.html \u002Fblog\u002F2026\u002Fother.html 同目录跳转 ..\u002Fimg\u002Fa.png \u002Fblog\u002Fimg\u002Fa.png 往上退一级 \u002Fimg\u002Fa.png 站点根下的 \u002Fimg\u002Fa.png 最推荐，与当前页层级无关 https:\u002F\u002Fsite.com\u002Fx 站外 真的去站外时 .\u002F 开头等于当前目录，可省略。以 \u002F 开头的路径最稳，因为「跟着目录跑」的相对路径在部署到子目录后极易断链。本站部署在 \u002Fnuxt\u002F 子路径下，教程里的示例统一用带前缀的站内绝对路径。",{"id":7932,"title":1475,"titles":7933,"content":7934,"level":146},"\u002Ftutorials\u002Fhtml#页内锚点id-是跳转目标",[5,1357],"任何元素只要有 id，就能被 #那个id 定位： \u003Ch2 id=\"pricing\">价格\u003C\u002Fh2>\n\u003Ca href=\"#pricing\">跳到价格\u003C\u002Fa> 跨页锚点把两者拼起来：\u002Ftutorials\u002Fhtml#表格。旧教程里的 \u003Ca name=\"x\"> 已过时，一律改用 id。 注意：锚点跳转是浏览器行为，CSS 的 scroll-behavior: smooth 可以让它平滑滚动，被跳转的标题需要 scroll-margin-top 才不会被吸顶导航遮住——这两条都属于 CSS，写在样式里而不是 JS。",{"id":7936,"title":1565,"titles":7937,"content":7938,"level":146},"\u002Ftutorials\u002Fhtml#target-与-rel新标签页必须带-noopener",[5,1357],"\u003Ca href=\"https:\u002F\u002Fexample.com\" target=\"_blank\" rel=\"noopener\">外部站点\u003C\u002Fa> target=\"_blank\" 会让被打开的页面在旧浏览器里拿到 window.opener，从而能反向修改你本页地址（tabnabbing）。现代浏览器已对 _blank 隐含 noopener，但显式写出更保险。加上 noreferrer 还会禁止发送来源信息，代价是对方统计里看不到你。 两条实践约定： 站内链接不要加 target=\"_blank\"：它切断返回路径，也多占一个标签页。 下载链接、PDF 之类需要保留当前页时才用新标签页。",{"id":7940,"title":1646,"titles":7941,"content":7942,"level":146},"\u002Ftutorials\u002Fhtml#链接文字要能被脱离上下文读懂",[5,1357],"\u003Ca>点击这里\u003C\u002Fa> 是反例：读屏用户逐个链接浏览时只听到一串「点击这里」。把链接嵌在能独立表意的短语上： \u003Cp>具体配置项见\u003Ca href=\"\u002Fdocs\u002Fconfig\">配置文件说明\u003C\u002Fa>。\u003C\u002Fp> 同理，href=\"#\" 的空链接会让页面滚回顶部且没有任何语义，纯按钮行为应该用 \u003Cbutton>（第十章），不要拿 \u003Ca> 冒充。",{"id":7944,"title":1705,"titles":7945,"content":1708,"level":126},"\u002Ftutorials\u002Fhtml#六图片-img",[5],{"id":7947,"title":1712,"titles":7948,"content":7949,"level":146},"\u002Ftutorials\u002Fhtml#srcaltwidthheight四个都要写",[5,1705],"\u003Cimg src=\"\u002Fimg\u002Fhero.webp\" alt=\"首页主图：三人协作场景\" width=\"1200\" height=\"600\" loading=\"lazy\"> src 必填，路径规则同第五章。 alt 必填（装饰图写空字符串 alt=\"\"，但不能省略属性）。它同时是三样东西：屏幕阅读器的播报文本、图片加载失败时显示的兜底文字、搜索引擎理解图片的唯一线索。别写「图片」「photo of」这类前缀，播报本身已带「图像」提示。 width \u002F height 写图片固有尺寸（不是最终显示尺寸）。浏览器据此预先算出宽高比并占好位置，图下载完页面不会「跳一下」——这个跳动会打断用户阅读，也是 Google 衡量的 CLS 指标的主要来源。显示尺寸想用 CSS 改，直接写 img { width: 100%; height: auto }，比例仍由那两个属性提供。",{"id":7951,"title":1812,"titles":7952,"content":7953,"level":146},"\u002Ftutorials\u002Fhtml#格式怎么选",[5,1705],"格式 用在哪 注意 SVG 图标、logo、图表、需要无损缩放的图形 文本型内容清晰；不适合照片；来自第三方上传的 SVG 有 XSS 风险，别直接内联 WebP \u002F AVIF 照片、大图首选 同画质体积比 JPEG 小约三成；AVIF 更小但编码慢 JPEG 需要兼容兜底的照片 无透明通道 PNG 需要透明通道、或纯色大面积截图 照片用 PNG 体积通常是 JPEG 的 3–5 倍 GIF 基本别用 动图改用 \u003Cvideo muted loop playsinline>，体积能小一个数量级 需要多格式降级时套 \u003Cpicture>（和第十二章 \u003Cvideo> 的多 \u003Csource> 降级是同一思路）： \u003Cpicture>\n  \u003Csource srcset=\"\u002Fimg\u002Fhero.avif\" type=\"image\u002Favif\">\n  \u003Csource srcset=\"\u002Fimg\u002Fhero.webp\" type=\"image\u002Fwebp\">\n  \u003Cimg src=\"\u002Fimg\u002Fhero.jpg\" alt=\"首页主图\" width=\"1200\" height=\"600\">\n\u003C\u002Fpicture> 浏览器从上往下挑第一个支持的 \u003Csource>，都不支持时落到 \u003Cimg>——\u003Cimg> 及其 alt 必须留在最后，否则降级路径和替代文本一起丢。",{"id":7955,"title":2027,"titles":7956,"content":7957,"level":146},"\u002Ftutorials\u002Fhtml#懒加载与破图排查",[5,1705],"loading=\"lazy\"：首屏外的图片等到接近视口才下载，长列表页收益明显。首屏主图不要加，反而应写 fetchpriority=\"high\"，否则会被自己的懒加载推迟。 decoding=\"async\"：让图片解码不阻塞其它内容渲染。 破图排查按顺序四步：① DevTools Network 看请求状态码；② 路径是否漏了部署前缀（本站在 \u002Fnuxt\u002F 子路径下，写 \u002Fimg\u002Fa.png 会打到域名根）；③ 文件真实扩展名与大小写是否一致（Linux 服务器区分大小写）；④ 本地双击能显示、部署后不显示 → 几乎一定是相对路径层级问题。",{"id":7959,"title":2062,"titles":7960,"content":7961,"level":126},"\u002Ftutorials\u002Fhtml#七表格",[5],"\u003Ctable> 用来呈现需要横竖对照才能读懂的数据。用它做页面布局是九十年代的遗产，今天由 Flex\u002FGrid 负责（见 CSS 教程第十、十一章）。",{"id":7963,"title":2074,"titles":7964,"content":7965,"level":146},"\u002Ftutorials\u002Fhtml#六个结构标签",[5,2062],"\u003Ctable>\n  \u003Ccaption>季度开销\u003C\u002Fcaption>\n  \u003Cthead>\u003Ctr>\u003Cth scope=\"col\">项目\u003C\u002Fth>\u003Cth scope=\"col\">金额\u003C\u002Fth>\u003C\u002Ftr>\u003C\u002Fthead>\n  \u003Ctbody>\u003Ctr>\u003Cth scope=\"row\">计算\u003C\u002Fth>\u003Ctd>1280\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\n  \u003Ctfoot>\u003Ctr>\u003Ctd>合计\u003C\u002Ftd>\u003Ctd>1950\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftfoot>\n\u003C\u002Ftable> \u003Ctable> 容器；\u003Ctr> 一行；\u003Ctd> 数据格；\u003Cth> 表头格。 \u003Cthead> \u002F \u003Ctbody> \u002F \u003Ctfoot> 把行分组。\u003Ctfoot> 建议写在 \u003Ctbody> 之后，但浏览器会把它绘制在表格末尾——历史上为了让多页打印时表头每页重复才这么规定。 \u003Ccaption> 是这张表的标题，必须紧跟 \u003Ctable> 之后、且是第一个子元素。它对读屏用户的价值相当于图片的 alt。",{"id":7967,"title":2307,"titles":7968,"content":7969,"level":146},"\u002Ftutorials\u002Fhtml#caption-与-th-scope让读屏知道这一格是什么",[5,2062],"屏幕阅读器逐格朗读时，靠 scope 建立「这一格属于哪一行、哪一列」的对应关系： 列表头写 \u003Cth scope=\"col\">，行表头写 \u003Cth scope=\"row\">。 没有 scope，用户念到「512」时只听得到一个孤立数字；有了它，播报是「存储，8 月，512」。 复杂表头（一格横跨多列）用 headers=\"格 id 列表\" 显式关联，配合 \u003Cth id=\"...\">。 \u003Ctd> 也能当行标题用吗？可以（第一列写成 \u003Ctd>计算\u003C\u002Ftd>），但它就失去了表头语义，读屏不会把它作为对应关系播报——这是最常见的表格可访问性缺失。",{"id":7971,"title":2355,"titles":7972,"content":7973,"level":146},"\u002Ftutorials\u002Fhtml#colspan-与-rowspan",[5,2062],"合并单元格只有一条铁律：colspan \u002F rowspan 写在被合并的第一个格子上，被它盖住的格子要从源码里删掉，不能留空 \u003Ctd>\u003C\u002Ftd>，否则行列整体错位。 \u003Ctr>\n  \u003Cth scope=\"row\" rowspan=\"2\">Nuxt\u003C\u002Fth>\n  \u003Ctd>3.12\u003C\u002Ftd>\u003Ctd>2024-07\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n  \u003C!-- 这里没有第一个格子，被上面的 rowspan 占了 -->\n  \u003Ctd>4.0\u003C\u002Ftd>\u003Ctd>2025-07\u003C\u002Ftd>\n\u003C\u002Ftr>",{"id":7975,"title":2489,"titles":7976,"content":7977,"level":146},"\u002Ftutorials\u002Fhtml#宽表格与滚动",[5,2062],"窄屏上表格溢出会撑破整页布局。处理办法是外套一层横向滚动容器，而不是把字号压到 10px： \u003Cdiv style=\"overflow-x:auto\">\u003Ctable>…\u003C\u002Ftable>\u003C\u002Fdiv> 数据量很大时考虑改成「小屏幕用卡片列表、大屏幕用表格」两套结构按媒体查询切换——这属于 CSS 教程第十三章的响应式范畴。",{"id":7979,"title":2534,"titles":7980,"content":7981,"level":126},"\u002Ftutorials\u002Fhtml#八divspan-与-classid",[5],"\u003Cdiv> 和 \u003Cspan> 是「无内容的容器」：它们不表达任何含义，唯一作用是把一组元素圈起来，好让 CSS 或 JS 找到这一块。所以判断标准始终是：有语义更准的标签吗？有就别用 div。",{"id":7983,"title":2555,"titles":7984,"content":7985,"level":146},"\u002Ftutorials\u002Fhtml#块级与行内默认排版行为",[5,2534],"块级（block） 行内（inline） 代表 div、p、h1–h6、ul、li、section、table、form span、a、strong、em、code、img、small 占宽 独占一行，撑满可用宽度 只包住内容宽度 宽高\u002Fmargin 可设宽高，上下 margin 生效 宽高无效，上下 margin 不生效（padding 生效但会溢出） 能包含 可包含块级和行内 默认只能放行内（\u003Ca>、\u003Cbutton> 例外） 「块级和行内」是默认显示行为，不是标签的本质属性——CSS 的 display 可以互相转换（display: inline-block、display: flex、display: grid），这在 CSS 教程第九章专门讲。",{"id":7987,"title":2684,"titles":7988,"content":7989,"level":146},"\u002Ftutorials\u002Fhtml#class-与-id-的边界",[5,2534],"id 在整篇文档内必须唯一，合法用途只有三类：锚点跳转目标（#id）、被 for \u002F aria-labelledby 指向、被脚本 getElementById 抓取。 不要用 id 写 CSS 选择器。它的优先级是 100，远高于 class（10），后面临时覆盖只能靠 !important，等于把样式的可维护性提前透支。 class 可以重复使用，也可以一个元素挂多个：class=\"btn btn--primary is-disabled\"——基础样式 + 变体 + 状态分开，本站所有组件都用这个命名法（BEM 风格）。 命名带用途而不是带外观：is-error 好于 red-text。 重复 id 浏览器不报错，但锚点和 label 绑定只命中第一个，属于最难排查的一类 bug。用 DevTools Console 执行 document.querySelectorAll('[id]').length 与 [...document.querySelectorAll('[id]')].map(e => e.id) 排序对比，可以一眼看到重复。",{"id":7991,"title":2765,"titles":7992,"content":7993,"level":146},"\u002Ftutorials\u002Fhtml#嵌套规则与缩进",[5,2534],"后开的标签必须先关（\u003Cb>\u003Ci>文字\u003C\u002Fi>\u003C\u002Fb>），交叉嵌套 \u003Cb>\u003Ci>文字\u003C\u002Fb>\u003C\u002Fi> 是非法的，浏览器会强行「就近闭合」，结果与你的预期完全不同。 三条实操约定： 缩进体现层级，两个空格一档；VS Code 里 Shift+Alt+F 自动格式化整份 HTML。 块级里放块级、行内里放行内，是唯一安全的默认策略。 \u003Cp> 里不能再放 \u003Cdiv>、\u003Cp>、\u003Cul> 等块级元素——浏览器会自动把 \u003Cp> 提前闭合，导致结构意外分裂（第十八章排错清单里它是常客）。",{"id":7995,"title":2810,"titles":7996,"content":101,"level":126},"\u002Ftutorials\u002Fhtml#九语义化结构与布局标签",[5],{"id":7998,"title":2813,"titles":7999,"content":8000,"level":146},"\u002Ftutorials\u002Fhtml#七个地标标签",[5,2810],"标签 含义 数量约定 \u003Cheader> 区块的介绍性内容（站点头、文章头） 每个区块各一个 \u003Cnav> 主导航区块 站内导航各一个 \u003Cmain> 页面主内容 整页一个 \u003Csection> 需要标题的一个分区 任意，但每个都该有标题 \u003Carticle> 可独立分发的一段内容（博文、评论、卡片） 任意 \u003Caside> 与主内容相关但可剥离（侧栏、相关推荐） 任意 \u003Cfooter> 区块的收尾内容 每个区块各一个 唯一性说的是嵌套层级：\u003Cmain> 整页只有一个；但 \u003Carticle> 内部的 \u003Cheader> 和 \u003Cfooter> 与页面的 \u003Cheader> 不冲突，它们属于各自父区块。",{"id":8002,"title":2945,"titles":8003,"content":8004,"level":146},"\u002Ftutorials\u002Fhtml#整页骨架",[5,2810],"一个可直接复制的起点，不含任何样式： \u003Cbody>\n  \u003Cheader>\n    \u003Ch1>站点名\u003C\u002Fh1>\n    \u003Cnav>\u003Cul>\u003Cli>\u003Ca href=\"\u002F\">首页\u003C\u002Fa>\u003C\u002Fli>\u003C\u002Ful>\u003C\u002Fnav>\n  \u003C\u002Fheader>\n\n  \u003Cmain>\n    \u003Carticle>\n      \u003Cheader>\u003Ch2>文章标题\u003C\u002Fh2>\u003Cp>\u003Ctime datetime=\"2026-10-03\">10 月 3 日\u003C\u002Ftime>\u003C\u002Fp>\u003C\u002Fheader>\n      \u003Csection>\u003Ch3>小节\u003C\u002Fh3>\u003Cp>正文……\u003C\u002Fp>\u003C\u002Fsection>\n      \u003Cfooter>\u003Cp>标签、作者\u003C\u002Fp>\u003C\u002Ffooter>\n    \u003C\u002Farticle>\n  \u003C\u002Fmain>\n\n  \u003Caside>\u003Ch2>相关推荐\u003C\u002Fh2>\u003Cul>\u003Cli>\u003Ca href=\"#\">另一篇\u003C\u002Fa>\u003C\u002Fli>\u003C\u002Ful>\u003C\u002Faside>\n\n  \u003Cfooter>\u003Cp>版权信息\u003C\u002Fp>\u003C\u002Ffooter>\n\u003C\u002Fbody> \u003Caside> 放在 \u003Cmain> 外面还是里面，取决于它是不是「主内容的一部分」：相关推荐属于某篇文章就放进去，全站侧栏就放外面。",{"id":8006,"title":3285,"titles":8007,"content":8008,"level":146},"\u002Ftutorials\u002Fhtml#为什么不能-div-全天下",[5,2810],"同样一个页面，全用 \u003Cdiv> 视觉上毫无区别，但你会丢掉三样东西： 辅助技术的地标导航。读屏软件（NVDA\u002FJAWS\u002FVoiceOver）有「地标列表」，用户靠它在导航、主内容、侧栏之间一键跳转。全是 div 时这个列表是空的，用户只能从头听到尾。 机器可读的结构。搜索引擎、微信\u002F微博的抓取器、浏览器的「阅读模式」都靠标签推断正文。\u003Carticle> + \u003Cmain> 的页面被正确抽取的概率明显更高。 可读的源码。半年后回来看 \u003Carticle class=\"post-card\"> 和 \u003Cdiv class=\"d3\">，维护成本不是一个量级。 反过来说，语义标签不是越多越好：给一个既无标题也不能独立分发的包裹层硬套 \u003Csection>，比 \u003Cdiv> 更糟——你对读屏用户许下了一个兑现不了的承诺。",{"id":8010,"title":3336,"titles":8011,"content":8012,"level":146},"\u002Ftutorials\u002Fhtml#什么时候仍然该用-div",[5,2810],"三种情况：纯样式包裹层（为了 flex 布局多套一层）、组件的根容器、以及找不到对应语义标签的抽象分组。判断顺序永远是：先找语义标签 → 再找列表\u002F表格\u002F表单结构标签 → 最后才是 div\u002Fspan。",{"id":8014,"title":3347,"titles":8015,"content":8016,"level":126},"\u002Ftutorials\u002Fhtml#十表单-form-与常见-input",[5],"表单是页面把数据交给服务器的唯一标准通道。它的问题通常不在标签语法，而在三个容易被忽略的细节：控件没写 name、label 没绑定、type 用了默认的 text。",{"id":8018,"title":3371,"titles":8019,"content":8020,"level":146},"\u002Ftutorials\u002Fhtml#formactionmethod数据发去哪怎么发",[5,3347],"\u003Cform action=\"\u002Fapi\u002Flogin\" method=\"post\" enctype=\"multipart\u002Fform-data\">\n  \u003C!-- 控件 -->\n  \u003Cbutton type=\"submit\">登录\u003C\u002Fbutton>\n\u003C\u002Fform> action：接收数据的地址，省略时提交到当前页面 URL。 method：get 把数据拼在 URL 问号后（进浏览器历史、进服务器日志、长度受限），只用于搜索\u002F筛选这类可分享、无副作用的查询；post 把数据放请求体里，登录、写入、上传一律用它。 enctype：默认 application\u002Fx-www-form-urlencoded；上传文件必须改成 multipart\u002Fform-data。 只有带 name 的控件才会被发送。id 是给 label 和 JS 用的，与提交无关——这是「后端说没收到数据」的第一大原因。 \u003Cbutton> 在表单内默认就是 type=\"submit\"。不想提交的按钮（清空、切换显示）必须显式写 type=\"button\"，否则一点就触发提交。",{"id":8022,"title":3515,"titles":8023,"content":8024,"level":146},"\u002Ftutorials\u002Fhtml#input-的-type-决定一切",[5,3347],"type 同时决定三件事：用什么控件呈现、手机弹出什么键盘、浏览器按哪套规则校验。 type 用途 附带校验 text 默认值，单行文本 无 email 邮箱 基本格式 password 密码 无（配 autocomplete=\"new-password\" \u002F \"current-password\"） number 数字 数字 + min\u002Fmax\u002Fstep tel 电话 不校验，配 inputmode=\"numeric\" 换数字键盘 url 网址 需含协议名 search 搜索框 无 date \u002F time \u002F datetime-local 日期时间 格式 range 滑块 min\u002Fmax\u002Fstep color 取色器 十六进制色值 checkbox \u002F radio 多选 \u002F 单选 同组 radio 必须同名 name file 文件 配 accept 筛选类型 hidden 随表单提交的不可见数据（如 CSRF token） 无 两个高频纠正： placeholder 不能替代 label：它一输入就消失、对比度常常不达标、翻译后易截断。 读写控件的值要用属性而不是特征：el.value（复选框 el.checked、文件 el.files）。改 HTML 的 value 特征只改「初始值」，用户已输入的内容不受影响。",{"id":8026,"title":3796,"titles":8027,"content":8028,"level":146},"\u002Ftutorials\u002Fhtml#label-的正确绑法",[5,3347],"\u003C!-- 显式绑定：for 与 id 成对，推荐 -->\n\u003Clabel for=\"email\">邮箱\u003C\u002Flabel>\n\u003Cinput id=\"email\" name=\"email\" type=\"email\">\n\n\u003C!-- 隐式包裹：合法，但标题和控件被拆散时不好维护 -->\n\u003Clabel>邮箱 \u003Cinput name=\"email\" type=\"email\">\u003C\u002Flabel> 显式绑定换来三个实际收益：点标题文字即聚焦（复选框是点标题即勾选，命中区域大得多，移动端体验差距明显）、读屏软件把标题播报成这个控件的名字、浏览器推断 autocomplete 也更准。 for 指向的 id 必须存在且不重复。一个控件允许被多个 \u003Clabel> 指向，但读屏会把两段都念出来。",{"id":8030,"title":3915,"titles":8031,"content":8032,"level":146},"\u002Ftutorials\u002Fhtml#selecttextareaoptgroup-与-datalist",[5,3347],"\u003Cselect id=\"city\" name=\"city\">\n  \u003Coption value=\"\">请选择城市\u003C\u002Foption>\n  \u003Coptgroup label=\"华东\">\n    \u003Coption value=\"sh\">上海\u003C\u002Foption>\n    \u003Coption value=\"hz\">杭州\u003C\u002Foption>\n  \u003C\u002Foptgroup>\n\u003C\u002Fselect>\n\n\u003Ctextarea id=\"note\" name=\"note\" rows=\"4\">\u003C\u002Ftextarea>\n\n\u003Cinput list=\"langs\" id=\"lang\" name=\"lang\">\n\u003Cdatalist id=\"langs\">\n  \u003Coption value=\"HTML\">\u003C\u002Foption>\n  \u003Coption value=\"CSS\">\u003C\u002Foption>\n\u003C\u002Fdatalist> 五条约定： \u003Ctextarea> 没有 value 属性，初始内容写在开闭标签之间，第一个字符前不要留缩进（空白会真的显示出来），也不可自闭合。 \u003Cselect multiple> 在移动端体验很差，多数场景应改用一组复选框。 \u003Coption> 不带 value 时提交的是它的文本，带 value 时提交 value——这个默认差异常造成前后端读到的值不一致。 datalist 是「自由输入 + 建议」，不强制只能选；select 是「只能选」。 \u003Coptgroup> 给下拉里的选项分组，\u003Cfieldset> + \u003Clegend> 给页面上的控件分组，两者作用域完全不同。",{"id":8034,"title":4231,"titles":8035,"content":8036,"level":126},"\u002Ftutorials\u002Fhtml#十一表单校验属性",[5],"HTML 原生校验（Constraint Validation API）的价值是在服务端返回之前就先给用户反馈，成本为零。",{"id":8038,"title":4242,"titles":8039,"content":8040,"level":146},"\u002Ftutorials\u002Fhtml#required-与-type最常用的两个",[5,4231],"\u003Cinput name=\"nick\" required>\n\u003Cinput name=\"email\" type=\"email\" required> required 拦空值，type=\"email\" \u002F \"url\" 附带格式检查，两者叠加才能既「不能不填」又「不能乱填」。type=\"number\" 已隐含「必须是数字」，一般不需要额外 pattern。 复选框的必填有个坑：「必须同意条款」要给那一个复选框自己加 required；一组同名复选框里只要任意一个带 required，就只要求其中一个被勾选。",{"id":8042,"title":4325,"titles":8043,"content":8044,"level":146},"\u002Ftutorials\u002Fhtml#patternminmaxstepminlength",[5,4231],"属性 规则 备注 pattern 正则 隐含整串匹配，别自己加 ^…$；必须同时给 title 说明规则，否则用户只看到「与此格式不符」 minlength \u002F maxlength 文本长度 maxlength 直接禁止继续输入（硬限制），minlength 在提交时校验（软限制） min \u002F max 数值或日期边界 只对数字\u002F日期类 type 生效 step 步长倍数 step=\"2\" 时输入 3 不合法 multiple 允许多值 配合 type=\"email\"（逗号分隔）或 \"file\" accept 文件类型 accept=\"image\u002F*\" 或 .pdf,.doc；只是筛选，不是安全限制 pattern=\"[A-Z]{2}\\d{4}\" title=\"两位大写字母加四位数字\" 会让浏览器气泡直接显示这段 title，比默认提示有用得多。",{"id":8046,"title":4466,"titles":8047,"content":8048,"level":146},"\u002Ftutorials\u002Fhtml#校验提示novalidate-与接管",[5,4231],"原生气泡的文案语言跟随浏览器界面语言，中文站会出现「Please fill out this field」。接管的标准写法： \u003Cform novalidate>…\u003C\u002Fform> const form = document.querySelector('form')\nform.addEventListener('submit', (e) => {\n  if (!form.checkValidity()) {\n    e.preventDefault()\n    form.reportValidity()   \u002F\u002F 仍然想用浏览器气泡时就调这个\n    return\n  }\n  \u002F\u002F 校验通过，再发请求\n}) 单个控件的三个 API：el.checkValidity() 返回布尔，el.validationMessage 是浏览器准备说的话，el.setCustomValidity('文案') 替换那句话——不成立时记得设回空字符串，否则该字段永久无效。CSS 侧还有 :valid \u002F :invalid \u002F :required \u002F :optional 四个伪类可以做即时描边，注意它们在用户还没输入完时就已变红，通常要配合 :not(:placeholder-shown) 收窄条件。",{"id":8050,"title":4656,"titles":8051,"content":8052,"level":146},"\u002Ftutorials\u002Fhtml#原生校验不能替代服务端校验",[5,4231],"原生校验属于用户体验层，不是安全层。绕过它只要一行： curl -X POST https:\u002F\u002F你的域名\u002Fapi\u002Fregister -d 'email=不是邮箱&nick=' 三条边界：前端校验负责即时反馈、可以宽松；服务端校验是唯一可信的一层，所有字段必查；数据库约束是最后一道防线。 顺带厘清 disabled 与 readonly：readonly 的值会提交、可聚焦可复制；disabled 的值不会提交、不可聚焦、通常显示为灰。只想「不让改但仍要有值」用 readonly，纯展示结果更应改用 \u003Coutput>。",{"id":8054,"title":4725,"titles":8055,"content":8056,"level":126},"\u002Ftutorials\u002Fhtml#十二audio-与-video",[5],"音视频标签的难点不在属性多，而在浏览器的自动播放策略和多格式降级。",{"id":8058,"title":4739,"titles":8059,"content":8060,"level":146},"\u002Ftutorials\u002Fhtml#controlsautoplay-与浏览器的自动播放限制",[5,4725],"\u003Cvideo controls preload=\"metadata\" poster=\"\u002Fimg\u002Fposter.jpg\" width=\"1280\" height=\"720\">\n  你的浏览器不支持 video，\u003Ca href=\"\u002Fmedia\u002Fdemo.mp4\">下载视频\u003C\u002Fa>。\n\u003C\u002Fvideo> controls 不写就没有播放条，用户什么都点不了——自绘播放器要几十行 JS，默认用它。 autoplay 必须同时写 muted 才可能生效。带声音的自动播放会被浏览器拦截（这是 Chrome\u002FSafari\u002FFirefox 共同策略，为了不把用户突然吓一跳，也为了省流量）。用户与页面有过交互（点击、按键）之后，脚本调用 play() 才不受限制。 playsinline 在 iPhone 上必写，漏了它 Safari 会强制切换到全屏播放器，你在视频上方叠的字幕、水印、按钮全部失效。 loop + muted + playsinline + preload=\"none\" 是背景装饰视频的标准组合。 preload 取 none \u002F metadata \u002F auto：列表页里的多个视频绝不能一律 auto，否则首屏带宽被视频吃光。 poster 给首帧占位图，既省流量也防止布局塌陷。",{"id":8062,"title":4892,"titles":8063,"content":8064,"level":146},"\u002Ftutorials\u002Fhtml#source-多格式降级",[5,4725],"\u003Cvideo controls poster=\"\u002Fimg\u002Fposter.jpg\">\n  \u003Csource src=\"\u002Fmedia\u002Fdemo.av1.mp4\" type=\"video\u002Fmp4;codecs=av01.0.09M.08\">\n  \u003Csource src=\"\u002Fmedia\u002Fdemo.webm\" type=\"video\u002Fwebm\">\n  \u003Csource src=\"\u002Fmedia\u002Fdemo.mp4\" type=\"video\u002Fmp4\">\n  降级文字与下载链接\n\u003C\u002Fvideo> 浏览器从上往下逐个试，用第一个它能解码的 \u003Csource>，所以顺序应当是「体积最优 → 兼容兜底」。全部失败时渲染 \u003Cvideo> 内部的降级内容——这一句是必须保留的，否则旧浏览器只剩一个空白框。 编码格式的现实选择：H.264（video\u002Fmp4）兼容最好、效率一般；VP9\u002FAV1（video\u002Fwebm）同画质明显更小，Safari 对 AV1 的支持较晚。因此常见做法是同时给 mp4 和 webm，或用 \u003Csource> 列出三种让新浏览器优先取小体积的那份。音频同理，\u003Caudio> 可以只给一个 MP3，兼容性风险最小。",{"id":8066,"title":5030,"titles":8067,"content":8068,"level":146},"\u002Ftutorials\u002Fhtml#track-字幕",[5,4725],"\u003Cvideo controls>\n  \u003Csource src=\"\u002Fmedia\u002Fdemo.mp4\" type=\"video\u002Fmp4\">\n  \u003Ctrack kind=\"subtitles\" src=\"\u002Fmedia\u002Fdemo.zh.vtt\" srclang=\"zh\" label=\"中文\" default>\n  \u003Ctrack kind=\"captions\" src=\"\u002Fmedia\u002Fdemo.en.vtt\" srclang=\"en\" label=\"English\">\n\u003C\u002Fvideo> 字幕文件是 WebVTT 格式：首行必须 WEBVTT，之后是「起止时间 → 文本」的块，可含 \u003Ci>、\u003Cc.class> 之类的轻量标记。 kind=\"subtitles\" 是给听不见声音的用户以外的人用的翻译；kind=\"captions\" 是给听障用户的，包含「掌声」这类音效说明；kind=\"descriptions\" 是给视障用户的画面描述音轨。 default 让该轨默认开启，label 是播放器字幕菜单里的名字。 字幕文件与页面不同源时必须返回 CORS 头，否则浏览器静默不加载——表现为「字幕点了没反应」，Network 面板里那条请求是 (failed) net::ERR_… 或被 CORS 策略拦下。 用 JS 操作轨道：video.textTracks[0].mode = 'showing'（其它值 hidden \u002F disabled）。 \u003Caudio> 也支持 \u003Ctrack>，但没有可视区域来显示，实际用途很小——给音频配文字稿才是对用户真正有帮助的做法。",{"id":8070,"title":5233,"titles":8071,"content":5236,"level":126},"\u002Ftutorials\u002Fhtml#十三iframe-与内嵌内容",[5],{"id":8073,"title":5240,"titles":8074,"content":8075,"level":146},"\u002Ftutorials\u002Fhtml#src-与-sandbox-的边界",[5,5233],"\u003Ciframe\n  src=\"\u002Fembed\u002Freport\"\n  title=\"季度报表\"\n  loading=\"lazy\"\n  sandbox=\"allow-scripts allow-forms\"\n  referrerpolicy=\"no-referrer-when-downgrade\"\n>\u003C\u002Fiframe> sandbox 是白名单：不写这个属性表示什么都不限制；写了但值为空串（sandbox=\"\"）表示最严格的封锁——脚本不能跑、表单不能提交、不能弹窗、不能把外层页面跳走。需要哪项就显式列哪项： 值 放开什么 备注 allow-scripts 内部 JS 演示代码必须 allow-forms 内部表单提交 表单示例 allow-modals alert\u002Fconfirm 讲解弹窗时 allow-popups window.open 少用 allow-same-origin 保留同源身份，可读自己的 localStorage\u002Fcookie 需要存储的示例 allow-top-navigation 允许内部把外层跳走 基本别开，是钓鱼入口 有个必须知道的陷阱：allow-scripts 与 allow-same-origin 同时给、且嵌的是同源文档时，沙箱等于形同虚设（内部脚本能拿到自己的 window 再爬 parent），浏览器控制台会直接警告这一组合。本站的示例框故意不给 allow-same-origin，代价是涉及 localStorage 的例子必须在「新标签页」里看。",{"id":8077,"title":5458,"titles":8078,"content":8079,"level":146},"\u002Ftutorials\u002Fhtml#title-不是可选项",[5,5233],"读屏用户通过「框架列表」在 iframe 间跳转，title 就是列表里的名称。缺 title 的 iframe 在 Lighthouse \u002F axe 的无障碍审计里是确定的错误项。同理，width\u002Fheight 或 CSS 尺寸要写清楚，否则 iframe 会塌陷成 150px 高的默认值。",{"id":8081,"title":5479,"titles":8082,"content":8083,"level":146},"\u002Ftutorials\u002Fhtml#响应式内嵌容器",[5,5233],"第三方给的嵌入代码通常写死 width=\"560\" height=\"315\"，手机上直接横向溢出。现代写法用 aspect-ratio： \u003Cdiv style=\"aspect-ratio:16\u002F9;position:relative\">\n  \u003Ciframe style=\"position:absolute;inset:0;width:100%;height:100%;border:0\" src=\"…\" title=\"…\" loading=\"lazy\">\u003C\u002Fiframe>\n\u003C\u002Fdiv> 老浏览器兼容写法是 padding-top: 56.25% 撑出比例（原理相同：百分比 padding 相对宽度计算），CSS 教程第六章会解释这条规则。",{"id":8085,"title":5571,"titles":8086,"content":8087,"level":146},"\u002Ftutorials\u002Fhtml#性能代价与-facade-模式",[5,5233],"iframe 内容与主页面不共享渲染资源，一份 iframe 就是一次独立的网络与解析开销，是 Lighthouse「减少第三方代码」告警的主要来源。 iframe 内的内容不会被搜索引擎当作本页内容收录——把正文放 iframe 里等于放弃 SEO。 视频、地图、社交组件建议用 facade 模式：先渲染一张海报图 + 播放按钮（成本几乎为零），用户点击后才写入 src 真正加载。YouTube 的 youtube-nocookie.com 组件、多数静态站的视频方案都是这个思路。 跨 iframe 通信只能用 postMessage，两侧都必须校验 event.origin，否则任意页面都能给你发消息。",{"id":8089,"title":5618,"titles":8090,"content":101,"level":126},"\u002Ftutorials\u002Fhtml#十四实体注释与书写约定",[5],{"id":8092,"title":5621,"titles":8093,"content":8094,"level":146},"\u002Ftutorials\u002Fhtml#常用实体",[5,5618],"只要内容里要显示标签字符本身，就必须转义： 写法 渲染 用途 &lt; &gt; \u003C > 写代码示例，教程页最高频 &amp; & 与符号；URL 里的 & 也要写成它 &quot; \" 属性值里嵌套双引号 &apos; 或 &#39; ' 单引号（后者兼容性更稳） &nbsp; 空格 不换行空格，强制不在此处折行 &copy; &reg; &trade; © ® ™ 版权、注册、商标 &mdash; &ndash; — – 长破折号、短连接号 &middot; · 面包屑分隔符 &#20195; \u002F &#x4EE3; 代 任意 Unicode 的十进制 \u002F 十六进制写法",{"id":8096,"title":5783,"titles":8097,"content":8098,"level":146},"\u002Ftutorials\u002Fhtml#中文还需要实体吗",[5,5618],"不需要。声明了 \u003Cmeta charset=\"UTF-8\"> 且文件确实存成 UTF-8，中文、emoji（😀）、数学符号（∑）都能直接写。实体的现代用途只剩两类：展示标签语法的教程页，以及编码不确定的旧环境。 反过来要警惕的是该转义时没转义：把用户输入直接拼进 HTML 是 XSS 的标准成因。转义发生在「输出到 HTML」这一步，而不是存储时；主流框架的插值语法（Vue 的 {{ }}、React 的 JSX）已默认转义，真正要小心的是主动使用 v-html \u002F innerHTML \u002F dangerouslySetInnerHTML。",{"id":8100,"title":5812,"titles":8101,"content":8102,"level":146},"\u002Ftutorials\u002Fhtml#注释的现状与风险",[5,5618],"\u003C!-- 普通注释 -->\n\u003C!-- 注释里不能出现连续两个减号，会被提前当成结束 --> 三条现状：① 注释会随 HTML 一起发给用户，内部接口地址、密钥、客户名字都不能写；② 浏览器不执行注释里的脚本，但「把密码注释掉」这种写法每年仍会被扫描器捡出来；③ 条件注释（\u003C!--[if IE]>）在 IE 10+ 已彻底失效，别再用来做浏览器分支。",{"id":8104,"title":5842,"titles":8105,"content":8106,"level":146},"\u002Ftutorials\u002Fhtml#大小写引号与布尔属性",[5,5618],"项目 现代约定 原因 标签大小写 全部小写 HTML 不区分，但 XHTML\u002FVue 模板区分，混写最容易出问题 属性值引号 始终加 值里含空格或 & 时不加就错 布尔属性 required 而非 required=\"required\" JS 里 getAttribute('disabled') 拿到的是空串而不是 \"true\"，判断要用 el.disabled 自闭合 \u003Cmeta charset=\"UTF-8\">，不写 \u002F> void 元素（br\u002Fimg\u002Finput\u002Fhr\u002Fmeta\u002Flink\u002Fsource）本来就没有结束标签 缩进 两个空格，项目内统一 交给编辑器\u002FPrettier 格式化，别手对齐",{"id":8108,"title":5959,"titles":8109,"content":8110,"level":126},"\u002Ftutorials\u002Fhtml#十五data-自定义属性",[5],"data-* 是「给元素挂自定义数据」的合法位置，值永远是字符串。命名规则：必须以 data- 开头，后面只允许小写字母、数字和连字符——不能有大写、不能有点。",{"id":8112,"title":5975,"titles":8113,"content":8114,"level":146},"\u002Ftutorials\u002Fhtml#语法与命名",[5,5959],"\u003Carticle id=\"a12\"\n         data-level=\"hot\"\n         data-user-id=\"1024\"\n         data-max-width=\"480\"\n         data-config='{\"sortable\":true,\"page\":1}'>\n  …\n\u003C\u002Farticle> 名字用 kebab-case（data-max-width），驼峰只出现在 JS 的 dataset 访问端。 名字要有语义：data-role=\"price\" 好于 data-x=\"3\"。 JSON 值用单引号包裹，内部保持标准双引号 JSON。",{"id":8116,"title":6081,"titles":8117,"content":8118,"level":146},"\u002Ftutorials\u002Fhtml#配合-css-属性选择器",[5,5959],"CSS 能直接按属性匹配，这带来一种很实用的写法：把「内容属性」交给 data，样式响应它，JS 只改属性不碰类名。 .card[data-level=\"hot\"] { border-color: #cf222e; }   \u002F* 精确匹配 *\u002F\n.card[data-level]       { cursor: pointer; }          \u002F* 只要属性存在 *\u002F\n[data-level^=\"h\"]       { … }                         \u002F* 前缀，还有 $= *= ~= |= *\u002F\n.card[data-level]::after { content: attr(data-level); } \u002F* 把值当文本显示出来 *\u002F\n.grid-item[data-span=\"2\"] { grid-column: span 2; } attr() 在 content 里取值非常省事（做角标、单位后缀），但它目前只能可靠地用在 content 上，不能拿去当宽度、颜色等任意属性值使用——那条能力（attr() 的通用用法）还在逐步落地中，写生产样式前请查一次基线支持。",{"id":8120,"title":6258,"titles":8121,"content":8122,"level":146},"\u002Ftutorials\u002Fhtml#dataset-读取",[5,5959],"const el = document.querySelector('[data-user-id]')\n\nel.dataset.userId      \u002F\u002F '1024' —— 连字符名自动映射成驼峰\nel.dataset.maxWidth    \u002F\u002F '480'，注意仍是字符串，要自己 parseInt\nel.dataset.state = 'ok'\u002F\u002F 写入会同步反映到 HTML 属性，CSS 选择器立刻生效\n\nObject.entries(el.dataset)          \u002F\u002F 遍历该元素上全部 data-*\ndelete el.dataset.clicks            \u002F\u002F 删除属性\n\nel.getAttribute('data-user-id')     \u002F\u002F 等价读法；setAttribute 是等价写法 dataset 与 getAttribute 的区别只在可读性，操作的是同一个属性。要监听变化用 MutationObserver。",{"id":8124,"title":6382,"titles":8125,"content":8126,"level":146},"\u002Ftutorials\u002Fhtml#什么时候不该用-data",[5,5959],"外观开关用 class，不用 data-*。data-color=\"red\" 通常说明你真正想要的是一个语义类名。 敏感信息不要放：和所有属性一样，它会出现在源码里，任何人可见。 可访问性状态必须用 ARIA：aria-expanded、aria-current、aria-hidden 会被读屏播报，data-* 完全不会。把 data-open 当成 aria-expanded 用是常见错误。 需要传结构化配置时，集中成一个 data-config JSON 比散成十几个属性更好维护。",{"id":8128,"title":6436,"titles":8129,"content":8130,"level":126},"\u002Ftutorials\u002Fhtml#十六响应式视口head-元信息与-seo",[5],"\u003Chead> 里的内容决定三件事：手机上能不能正常排版、搜索结果长什么样、分享到社交平台时有没有卡片图。",{"id":8132,"title":6445,"titles":8133,"content":8134,"level":146},"\u002Ftutorials\u002Fhtml#viewport-那一行到底在说什么",[5,6436],"\u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\"> width=device-width：按设备的逻辑宽度排版；initial-scale=1：初始不缩放。 漏写这一行，手机会按 980px 宽排版再整体缩小，文字小到看不清，并且所有媒体查询失效——它看到的宽度永远是 980 而不是 375。 不要再写 user-scalable=no 或 maximum-scale=1：禁止双指放大是可访问性错误，主流浏览器也已不再遵守。 与它配套的 CSS 是「流式布局 + 少量断点」，见 CSS 教程第十三章。",{"id":8136,"title":6514,"titles":8137,"content":8138,"level":146},"\u002Ftutorials\u002Fhtml#titledescriptioncanonical三件必写",[5,6436],"项 建议 常见失误 \u003Ctitle> 每页唯一，30 字以内，核心词靠前，「文章名 - 站点名」格式 全站同一个标题；被截断；标题堆砌关键词 \u003Cmeta name=\"description\"> 80–120 字，写给用户看的一句话摘要 缺失（引擎自己拼，通常很难看）；把整段正文粘进去 \u003Clink rel=\"canonical\"> 同一内容的多个 URL 指向唯一规范地址 分页\u002F排序参数各自一条规范地址，导致权重分散 description 不直接提升排名，但它决定用户在你的摘要里看到什么，影响点击率。robots 相关：内部搜索结果页、预览页写 noindex；hreflang 用于多语言版本互相声明，必须双向；sitemap.xml 提交到搜索引擎站长平台。 静态站框架通常能自动产出这几项。本站在 Nuxt 里对应 useSeoMeta()（页面级）与 nuxt.config.ts 的 site.url——注意 site.url 上线前必须换成真实域名，否则 canonical 和 sitemap 都会指向占位地址。",{"id":8140,"title":6613,"titles":8141,"content":8142,"level":146},"\u002Ftutorials\u002Fhtml#open-graph-与分享卡片",[5,6436],"\u003Cmeta property=\"og:type\" content=\"article\">\n\u003Cmeta property=\"og:title\" content=\"HTML 教程：从第一个标签到语义化页面\">\n\u003Cmeta property=\"og:description\" content=\"19 章循序渐进，每章配可直接运行的示例。\">\n\u003Cmeta property=\"og:image\" content=\"https:\u002F\u002F你的域名\u002Fimg\u002Fhtml-cover.svg\">\n\u003Cmeta property=\"og:url\" content=\"https:\u002F\u002F你的域名\u002Ftutorials\u002Fhtml\">\n\u003Cmeta name=\"twitter:card\" content=\"summary_large_image\"> 要点：og:image 必须是绝对 URL，比例约 1.91:1（1200×630）；相对路径不会报错，只会让卡片没有图。部分平台不支持 SVG 作为 og:image，稳妥做法是额外准备一份 JPG\u002FPNG。各平台都有官方校验工具（微信\u002F微博、Facebook Sharing Debugger、Twitter Card Validator），发布新模板时逐个跑一次。",{"id":8144,"title":6768,"titles":8145,"content":8146,"level":146},"\u002Ftutorials\u002Fhtml#其它常用-head-元信息",[5,6436],"theme-color：手机浏览器地址栏配色；给深色模式再补一条带 media=\"(prefers-color-scheme: dark)\" 的。 link rel=\"icon\"：标签页图标，同时保留 \u002Ffavicon.ico 兜底最省事。 link rel=\"alternate\" type=\"application\u002Frss+xml\"：RSS 订阅地址。 link rel=\"preconnect\"：只对确定要请求的第三方域名握手，加多了适得其反。 format-detection telephone=no：禁止 iOS 把一串数字变成电话链接。 \u003Cmeta http-equiv=\"refresh\">：除非做跳转兜底，否则别用——它打断用户且读屏不友好。",{"id":8148,"title":6825,"titles":8149,"content":6828,"level":126},"\u002Ftutorials\u002Fhtml#十七可访问性",[5],{"id":8151,"title":6831,"titles":8152,"content":8153,"level":146},"\u002Ftutorials\u002Fhtml#键盘可达与焦点顺序",[5,6825],"原生可交互元素——\u003Cbutton>、\u003Ca href>、\u003Cinput>、\u003Cselect>、\u003Ctextarea>——天然能 Tab 到、天然响应 Enter\u002F空格。用 \u003Cdiv> 冒充按钮时你需要自己补齐三件事：tabindex=\"0\"、role=\"button\"、以及监听 keydown 处理 Enter 与空格。只要能用原生标签就用原生标签，这是最省事也最不容易出错的做法。 tabindex 的三个取值要知道：0 按 DOM 顺序入列；-1 不能被 Tab 到但可被脚本聚焦（弹窗背景常用）；正数会打乱全局顺序，绝大多数场景都不该出现。 另外两条：模态弹窗打开时应把焦点移入并在内部循环（focus trap），关闭后归还到触发元素；「跳到主内容」的隐藏链接（首个焦点时露出）能替键盘用户省掉每次遍历整条导航。",{"id":8155,"title":6894,"titles":8156,"content":8157,"level":146},"\u002Ftutorials\u002Fhtml#焦点必须看得见",[5,6825],"最严重也最常见的问题是抹掉焦点环： \u002F* 错误：键盘用户直接失去定位能力 *\u002F\nbutton:focus { outline: none; }\n\n\u002F* 正确：保留焦点，但换成符合设计的外观 *\u002F\n:focus-visible { outline: 3px solid #0969da; outline-offset: 2px; } :focus-visible 的好处是只在键盘聚焦时显示，鼠标点击不会留环，设计和可访问性都不用让步。本站的目录链接、搜索框用的就是这个伪类。",{"id":8159,"title":6984,"titles":8160,"content":8161,"level":146},"\u002Ftutorials\u002Fhtml#aria-的第一定律与常用属性",[5,6825],"第一定律：能用原生语义标签和属性做到的，就不要用 ARIA。 \u003Cbutton> 已经是按钮，写 role=\"button\" 只会增加出错面积。 确实需要 ARIA 的典型场景： 属性 用途 注意 aria-label \u002F aria-labelledby 给无名控件命名 图标按钮必用；labelledby 指向已有可见文本更稳 aria-expanded 展开\u002F折叠状态 自绘折叠组件必须自己维护，原生 \u003Cdetails> 自动带 aria-controls 按钮与被控区块的关联 值为目标 id aria-current=\"page\" 标出导航里的当前项 比只靠变色更明确 aria-live=\"polite\" 局部异步更新播报 表单校验结果、加载中提示；用 role=\"status\" 更简洁 aria-invalid + aria-describedby 把错误信息与控件关联 不要只用红色描边表达错误 aria-hidden=\"true\" 从无障碍树里移除纯装饰元素 装饰性图标字体、内联 SVG 用；绝不能用在可聚焦元素上 role=\"region\" \u002F 地标 给长页面分区 \u003Cmain>\u002F\u003Cnav>\u002F\u003Caside> 已自带，重复声明是冗余",{"id":8163,"title":7148,"titles":8164,"content":8165,"level":146},"\u002Ftutorials\u002Fhtml#对比度与其它底线",[5,6825],"正文与背景对比度至少 4.5:1（大号字 3:1）。把提示文字统一设成 #aaa 是很多站点批量失分的原因；图片上的文字要么加遮罩要么换到纯色区域。 尊重 prefers-reduced-motion，为前庭功能敏感的用户关掉大幅位移动画（写法见 CSS 教程第十三章）。 移动端可点区域不小于 44×44 像素。 \u003Chtml lang=\"zh-CN\"> 决定读屏的发音引擎（第二章）。 交付前自己走两遍：只用 Tab 完成一次核心操作；把宽度压到 375px 读一遍。这两步能发现九成问题，成本远低于任何自动化工具。",{"id":8167,"title":7188,"titles":8168,"content":8169,"level":126},"\u002Ftutorials\u002Fhtml#十八常见坑与排错清单",[5],"HTML 的错误绝大多数不会报错——它的设计目标就是「尽力理解你写的东西」。所以排查靠的是「对比实际 DOM 与预期 DOM」，而不是等控制台提示。",{"id":8171,"title":7198,"titles":8172,"content":8173,"level":146},"\u002Ftutorials\u002Fhtml#块级与行内的混用",[5,7188],"最高频的一类：在 \u003Cp> 里放 \u003Cdiv>、\u003Cp>、\u003Cul> 等块级元素。浏览器会自动闭合前面的 \u003Cp>，于是你以为的一段变成三段，缩进和间距全乱。同类问题还有 \u003Cspan> 里放 \u003Cdiv>（不合法但会被纠正）、\u003Ca> 里套 \u003Ca>（内层被踢出）。",{"id":8175,"title":7235,"titles":8176,"content":8177,"level":146},"\u002Ftutorials\u002Fhtml#未闭合标签与浏览器的就近纠正",[5,7188],"\u003Cli> 省略 \u003C\u002Fli> 在 HTML 里是合法的（下一个 \u003Cli> 或 \u003C\u002Ful> 会隐式闭合它），但 \u003Cdiv>、\u003Csection> 这类没有隐式闭合规则的标签漏掉闭合，就会吞掉后续一大块内容——表现为「下半页莫名其妙变成了一列」。交叉嵌套 \u003Cb>\u003Ci>文字\u003C\u002Fi>\u003C\u002Fb> 会被就近纠正成两对，结果与预期完全不同。 发现办法：Elements 面板看实际层级（与源码对不上就是被纠正过），或者把文件交给 W3C Nu 校验器，它会直接指出「element not allowed as child of …」。",{"id":8179,"title":7267,"titles":8180,"content":8181,"level":146},"\u002Ftutorials\u002Fhtml#表格与表单的典型错误",[5,7188],"现象 真实原因 修法 表格列整体错位 合并单元格后没删被盖住的 \u003Ctd> 数每行列数，colspan\u002Frowspan 计入被占位的那几列 读屏只念数字 \u003Cth> 缺 scope 列表头 scope=\"col\"，行表头 scope=\"row\" 后端收不到某字段 控件只有 id 没有 name 补 name，id 与提交无关 点标题不聚焦 label for 与 id 不一致，或 id 重复 成对检查；重复 id 只命中第一个 按钮一点就整页刷新 表单里 \u003Cbutton> 默认 type=\"submit\" 非提交按钮写 type=\"button\"，JS 里 preventDefault() 单选组能同时选两个 同组 radio 的 name 不同 一组同名，value 区分选项 图片不显示 路径 404 \u002F 漏了部署子路径前缀 先看 Network，再看是不是写了 ..\u002F..\u002F 百分比高度为 0 父级没有确定高度，或进了怪异模式 用 min-height\u002Faspect-ratio；document.compatMode 检查",{"id":8183,"title":7442,"titles":8184,"content":8185,"level":146},"\u002Ftutorials\u002Fhtml#排错的固定路径",[5,7188],"关掉浏览器扩展再刷新（广告拦截、隐私插件是「本地正常别人打不开」的第一大来源）。 Console 看红字，Network 看红条：路径、接口、CORS 问题一次现形。 Elements 看实际 DOM，Computed 看最终样式：你在源码里写的顺序不是浏览器的理解结果。 最小复现：把可疑片段复制到独立的空白 HTML 里，只留最小结构，逐步加回。 跑一遍校验与审计：Nu 校验器查结构，Lighthouse 查对比度、缺 label、缺 title。 真机验证：375px 宽度 + 只用键盘走一遍核心流程。",{"id":8187,"title":7489,"titles":8188,"content":8189,"level":126},"\u002Ftutorials\u002Fhtml#十九综合实战用语义标签重写一个页面",[5],"前面十八章的知识在这一章一次性用掉。下面两个示例是同一个页面的两个版本，视觉效果一致，差别全在结构上。先用浏览器打开看两边是否真的长得差不多，再对照源码。",{"id":8191,"title":7499,"titles":8192,"content":8193,"level":146},"\u002Ftutorials\u002Fhtml#先看清旧结构",[5,7489],"这份代码的可见问题是：整页只有 div\u002Fspan；导航和「阅读全文」是上了色的文字，没有任何可聚焦元素；日期是纯文本；样式全部内联。它在视觉上完全正常，但读屏用户拿不到地标列表，键盘用户一步都走不了，搜索引擎抽正文时会把友情链接一起抽走。",{"id":8195,"title":7519,"titles":8196,"content":8197,"level":146},"\u002Ftutorials\u002Fhtml#重写从地标开始",[5,7489],"重写的顺序值得记住，它和写 CSS 的顺序相反——先结构后外观： 先放五个地标：header（含 h1）、nav aria-label=\"主导航\"、main、aside、footer。 nav 里放真正的 ul > li > a，当前页给 aria-current=\"page\"；链接必须是 a[href]，不要用 div 上色冒充。 每一篇可独立分发的内容给一个 article，标题用 h3 并把整条标题做成链接（a 可以包块级内容，卡片整体可点就是这么实现的）。 日期用 \u003Ctime datetime=\"2026-09-20\">，机器读得到、人看到的是中文。 并列的目录项用 ul\u002Fol，并给承载它的分区加 h2 + aria-labelledby。 样式全部收进类名（BEM 风格），去掉内联 style——这一步属于 CSS 教程，但改造时顺手做完最省事。 注意改造后没有新增一个 role：\u003Cmain> 已隐含 role=\"main\"，\u003Cnav> 已隐含 role=\"navigation\"，重复声明是冗余。ARIA 只用来补原生标签补不了的信息（aria-current、aria-label、aria-labelledby）。",{"id":8199,"title":7633,"titles":8200,"content":8201,"level":146},"\u002Ftutorials\u002Fhtml#对照检查清单",[5,7489],"改完一个页面，按这七条逐项打勾： 只用 Tab 能从页头走到页尾，每一步都看得见焦点环。 读屏的地标列表里能看到 导航 \u002F 主内容 \u002F 侧栏 \u002F 内容信息 四项。 页面只有一个 \u003Ch1>，标题层级不跳跃（h2 之后不直接出现 h4）。 所有图片都有 alt，装饰图的 alt=\"\" 而不是省略。 所有图标按钮都有 aria-label，所有表单控件都有绑定的 \u003Clabel>。 每个 \u003Ciframe> 都有 title。 正文文字与背景对比度 ≥ 4.5:1。 到这里 HTML 教程完结。接下来建议直接进入 CSS 教程：本教程里所有「交给 CSS」的部分（布局、单位、颜色、响应式、主题变量）都在那边展开。 html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .s4JwU, html code.shiki .s4JwU{--shiki-default:#85E89D}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",1793263562819]