普通视图

发现新文章,点击刷新页面。
昨天 — 2026年1月24日首页
昨天以前首页

【Playwright学习笔记 06】用户视觉定位的方法

作者 鄭郑
2026年1月23日 17:50

根据文本内容定位

Page/Locator 对象的 get_by_text 方法

比如,

如果要获取 所有 文本内容包含 11 的元素,就可以这样

elements = page.get_by_text('11').all()

如果,你希望包含的内容是以 11 结尾的,就可以使用正则表达式对象 作为参数,如下

import re
elements = page.get_by_text(re.compile("11$")).all()

根据 元素 role 定位

web应用现在有一种标准 称之为: ARIA (Accessible Rich Internet Applications)

ARIA 根据web界面元素的用途,为这些元素定义了一套 角色( Role ) 信息,添加到页面中,

从而 让 残疾人士,或者 普通人在 某种环境下(比如夜里,太空中),不方便使用常规方法操作应用,使用辅助技术工具,来操作web应用的。

若想定位这个元素:

<div class="alert-message">
  您已成功注册,很快您将收到一封确认电子邮件
</div>

直接可以根据如下代码 定位该元素

# 根据 role 定位
lc = page.get_by_role('alert')

# 打印元素文本
print(lc.inner_text())

html元素中,有些 特定语义元素(semantic elements)被ARIA规范认定为自身就包含 ARIA role 信息,并不需要我们明显的加上 ARIA role 属性设置,

比如

<progress value="75" max="100">75 %</progress>

就等于隐含了如下信息

<progress value="75" max="100"
  role="progressbar"
  aria-valuenow="75"
  aria-valuemax="100">75 %</div>

所以,直接可以根据如下代码 定位该元素

# 根据 role 定位
lc = page.get_by_role('progressbar')

# 打印元素属性 value 的值
print(lc.get_attribute('value'))

再比如 search 类型的输入框,默认就有 searchbox role,

<input type="search">

ARIA Attribute

ARIA规范除了可以给元素添加 ARIA role ,还可以添加其它  ARIA属性(ARIA attributes) ,比如

<div role="heading" aria-level="1">白月黑羽标题1</div>
<div role="heading" aria-level="2">白月黑羽标题2</div>

aria-level 就是一个 ARIA 属性,表示 role 为 heading 时的 等级 信息

上面的定义,其实和下面的 html 元素 h1/h2 等价

<h1>白月黑羽标题1</h1>
<h2>白月黑羽标题2</h2>

上例中,h2 元素,隐含了 role="heading" aria-level="2" , 所以可以用下面代码定位

lc = page.get_by_role('heading',level=2)
print(lc.inner_text())

Accessible Name

只根据 ARIA roleARIA属性 往往并不能唯一定位元素。

role定位最常见的组合是 ARIA roleAccessible Name

因为,Accessible Name 就像元素的 名字 一样,往往可以唯一定位。

html 元素标准属性 name 是浏览器内部的,用户看不到,比如

Accessible Name 不一样,它是元素界面可见的文本名,

<a name='link2byhy' href="https://www.byhy.net">白月黑羽教程</a>

比如上面的元素,暗含的 Accessible Name 值就是 白月黑羽教程 , 当然也暗含了 ARIA role 值为 link

所以,可以这样定位

lc = page.get_by_role('link',name='白月黑羽教程')
print(lc.click())

上面的写法, 只要 Accessible Name 包含 参数name 的字符串内容即可,而且大小写不分, 并不需要完全一致。

所以,这样也可以定位到

lc = page.get_by_role('link',name='白月黑羽')

如果你需要 Accessible Name 和 参数name 的内容完全一致,可以指定 exact=True ,如下

lc = page.get_by_role('link',name='白月黑羽', exact=True)

name值还可以通过正则表达式,进行较复杂的匹配规则,比如

lc = page.get_by_role('link',name=re.compile("^白月.*羽"))

我们这里说一些常见的:

<a> <td> <button> Accessible Name 值 就是其内部的文本内容。

<textarea> <input> 这些输入框,它们的 Accessible Name 值 是和他们关联的 的文本。

比如:

<label>
  <input type="checkbox" /> Subscribe
</label>

这个 checkbox 的 Accessible Name 却是 Subscribe

一些元素 比如 <img> ,它的 Accessible Name 是其html 属性 alt 的值

比如

<img src="grape.jpg" alt="banana"/>

role定位代码复杂,建议使用codegen代码助手来完成

# CSS 学习笔记:彻底掌握 `:nth-child(n)` 伪类选择器

2026年1月23日 17:46

CSS 学习笔记:彻底掌握 :nth-child(n) 伪类选择器

一、一句话定义(先锚定认知)

:nth-child(n) 是一个基于父元素子节点顺序的结构化伪类选择器,用于匹配:
“是其直接父元素的第 n 个子元素,且自身标签名(或类型)与选择器前缀一致” 的元素。
它不关心“这是第几个 <li>”,而只认“这是父元素的第几个孩子”。


二、语法全解:从简单到强大

1️ 基础形式(最常用)

写法 含义 示例
:nth-child(1) 第1个子元素 li:nth-child(1) → 列表第一个 <li>
:nth-child(odd) 所有奇数位子元素(1,3,5…) tr:nth-child(odd) → 表格奇数行
:nth-child(even) 所有偶数位子元素(2,4,6…) div:nth-child(even) → 偶数位 <div>

2️ 数学公式 an + b(万能通式)

  • n 是从 0 开始的整数(n = 0, 1, 2, 3...
  • 计算结果 an + b 即为匹配的子元素位置(从 1 开始计数)
公式 展开(n=0,1,2,3…) 实际匹配位置 常见用途
2n 0, 2, 4, 6… → 忽略 0 2, 4, 6, 8… 等价于 even
2n+1 1, 3, 5, 7… 1, 3, 5, 7… 等价于 odd
3n+1 1, 4, 7, 10… 每隔 3 个选第 1 个 网格首列高亮
n+3 3, 4, 5, 6… 从第 3 个开始所有子元素 “跳过前两个”
-n+3 3, 2, 1, 0… → 0 及负数无效 1, 2, 3 前三个元素(黄金公式 ✅)

记忆口诀:

  • even/odd → 看是否被 2 整除;
  • n+X → 从第 X 个起全部;
  • -n+X → 前 X 个;
  • an+b → 代入 n=0,1,2… 算出有效正整数即可。

三、核心机制(必读!90% 错误源于此)

正确逻辑(三步判定)

对页面中每一个目标元素(如每个 <li>),浏览器执行:

  1. 定位父元素 → 找到它的 parentNode(直接父容器);
  2. 计算位置 → 查看它在父元素的 children 列表中是第几个(从 1 开始计数,只算元素节点,忽略纯文本/注释);
  3. 双重校验 → ① 位置符合 an+b 公式;② 元素类型与选择器前缀一致(如 li 必须是 <li> 标签)→ 两者都满足才匹配。

常见误解(红色警报!)

误解 为什么错 正确做法
:nth-child(3) 就是第三个 <li> ❌ 它找的是“第3个子元素”,不是“第3个 <li> ✅ 改用 li:nth-of-type(3)
“父元素没写,就找不到父” ❌ 每个元素天然有父节点(<body> 或其他),无需显式声明 li:nth-child(odd) 会自动作用于所有 <li>,各自找爹
“隐藏元素(display:none)不参与计数” :nth-child 基于 DOM 结构,与渲染无关 ✅ 隐藏元素仍占位置;若需“视觉上第n个”,需 JS
“空白换行会影响序号” ⚠️ 现代浏览器已优化:只计算元素节点(Element Nodes),忽略纯空白文本节点 ✅ 安心写格式化 HTML,无需压缩成一行

四、实战对比::nth-child() vs :nth-of-type()

维度 :nth-child(n) :nth-of-type(n)
判定依据 元素在其父元素中的总子元素序号 元素在其父元素中同类型元素的序号
是否跨类型计数 ✅ 是(<h2>, <p>, <div> 全部计入) ❌ 否(只数 <p>,忽略 <h2>
HTML 示例 <ul><h2>T</h2><p>A</p><span>S</span><p>B</p></ul> 同上
p:nth-child(2) ✅ 匹配 <p>A</p>(它是第2个子元素)
p:nth-child(4) ✅ 匹配 <p>B</p>(它是第4个子元素)
p:nth-of-type(1) ✅ 匹配 <p>A</p>(第一个 <p>
p:nth-of-type(2) ✅ 匹配 <p>B</p>(第二个 <p>
何时选用 需要“按物理位置布局”(如表格隔行、网格首行) 需要“按标签语义筛选”(如第3个段落、最后2个列表项)

快速决策树:
你想选「排第几的元素」→ :nth-child()
你想选「第几个同类元素」→ :nth-of-type()


五、无父类选择器的真实含义(深度澄清)

li:nth-child(odd) { background: #eee; }

它不是“没有父”,而是“不限定父”

  • 浏览器会为每个 <li> 独立执行
    当前<li>.parentNode.children → 找到它在其中的索引 → 判断是否为奇数 → 是则应用样式。
  • 因此,它可能同时作用于:
    • <ul> 下的 <li>
    • <ol> 下的 <li>
    • <nav> 中的 <li>(语义化导航)
    • 甚至 <section> 直接子级的 <li>(HTML5 允许)

为什么可以这样写?

  • CSS 是全局作用域,选择器描述的是“满足条件的元素集合”,不强制绑定父容器;
  • 这种写法简洁高效,适合通用样式(如所有列表隔行变色);
  • ⚠️ 但大型项目中建议增加命名空间约束以防冲突:
/* 推荐:模块化限定 */
.product-list li:nth-child(odd) { ... }
.nav-menu li:nth-child(odd)   { ... }

/* 不推荐:全局污染风险 */
li:nth-child(odd) { ... } /* 可能意外影响第三方组件 */

六、经典应用场景(附可复制代码)

场景 1:表格隔行变色(最稳方案)

<table>
  <tr><td>张三</td><td>85</td></tr>
  <tr><td>李四</td><td>92</td></tr>
  <tr><td>王五</td><td>78</td></tr>
</table>
/*  推荐:用 :nth-child,不受 thead/tfoot 干扰 */
table tr:nth-child(even) { background: #f9f9f9; }
table tr:nth-child(odd)  { background: #fff; }

场景 2:网格布局首行加粗

<div class="grid">
  <div>1</div><div>2</div><div>3</div>
  <div>4</div><div>5</div><div>6</div>
</div>
/* 每行3列 → 首行:1,2,3 → 位置 1,2,3 → 用 -n+3 */
.grid div:nth-child(-n+3) { font-weight: bold; }

场景 3:导航菜单仅第1、3、5项有下划线

.nav-link:nth-child(2n+1) { border-bottom: 2px solid blue; }

七、调试技巧(开发必备)

方法 操作 作用
浏览器开发者工具 在 Elements 面板选中 <li> → 右侧 Styles 查看是否生效 → 展开父节点数子元素 ✅ 验证实际位置
Console 快速验证 输入 document.querySelector('li:nth-child(2)') ✅ 返回匹配的第一个元素
临时高亮所有匹配项 *:nth-child(odd) { outline: 2px solid red !important; } ✅ 可视化所有奇数位子元素(慎用)

八、兼容性与注意事项

项目 说明
浏览器支持 Chrome 4+ / Firefox 3.5+ / Safari 3.1+ / Edge 12+ / IE9+(IE8及以下不支持)
⚠️ 服务端渲染(SSR)注意 若 HTML 由服务端生成,确保结构与客户端一致,否则样式错位
🚫 不能做的事 • 无法基于内容(如文字包含“重要”)筛选• 无法选择“倒数第2个可见元素”(需 JS)• 无法跨父元素计数(如“整个页面第5个 <li>”)

附录:速查记忆卡(打印/收藏版)

目标 写法 备注
第1个元素 :nth-child(1) 最简写法
奇数位 :nth-child(odd):nth-child(2n+1) 二选一
偶数位 :nth-child(even):nth-child(2n) 二选一
前3个 :nth-child(-n+3) ✅ 黄金公式
从第4个起所有 :nth-child(n+4) ✅ 黄金公式
每4个选第3个 :nth-child(4n+3) 如 3,7,11,15…
仅最后一个 :nth-last-child(1) 倒序计数

最后送你一句心法

:nth-child() 不是“找第几个 <p>”,而是“站在父元素门口,点名叫第n个进门的孩子——还得确认他是不是你要找的那位”。
理解了这个画面,你就永远不会再写错。

vue3+vite使用unocss和vant做移动端开发适配,使用lib-flexible适配RemToPx

2026年1月23日 17:43

lib-flexible设置根字号

npm i lib-flexible
//然后在mian.js中引入

uno.config.js设置

根目录创建uno.config.js文件,需要安装 npm i @unocss/preset-rem-to-px

import { defineConfig, presetWind3 } from 'unocss'
import presetRemToPx from '@unocss/preset-rem-to-px'
export default defineConfig({
    presets: [
        presetWind3(),  //预设样式
        presetRemToPx()  // 重点,把预设的rem转成px,这样postCssPxToRem就可以转了 例如预设样式 text-base的font-size:1rem;经过转换变成16px,在经过postCssPxToRem转换成rem
    ]
})

这个是vite的配置

import postCssPxToRem from 'postcss-pxtorem'
import UnoCSS from 'unocss/vite'

export default defineConfig({
    plugins: [
        UnoCSS(),
    ],
    css: {
        postcss: {
            plugins: [
                postCssPxToRem({
                  rootValue: 37.5,
                  unitPrecision: 6,
                  minPixelValue: 1,
                  propList: ['*'],
                  mediaQuery: false,
                })
            ]
        }
    }
)}

其他的按照vant文档正常使用就好了

【Playwright 学习笔记 05】Xpath选择

作者 鄭郑
2026年1月23日 16:05

XPath (XML Path Language) 是由国际标准化组织W3C指定的,用来在 XML 和 HTML 文档中选择节点的语言。

目前主流浏览器 (chrome、firefox,edge,safari) 都支持XPath语法,xpath有多个版本,目前浏览器支持的是 xpath 1的语法。

语法介绍

xpath 语法中,整个HTML文档根节点用'/'表示

绝对路径选择

从根节点开始的,到某个节点,每层都依次写下来,每层之间用 / 分隔的表达式,就是某元素的 绝对路径

上面的xpath表达式 /html/body/div ,就是一个绝对路径的xpath表达式, 类似 css表达式 html>body>div

相对路径选择

有的时候,我们需要选择网页中某个元素, 不管它在什么位置

xpath需要前面加 // , 表示从当前节点往下寻找所有的后代元素,不管它在什么位置。

所以xpath表达式,应该这样写: //div

通配符

如果要选择所有div节点的所有直接子节点,可以使用表达式 //div/*

* 是一个通配符,对应任意节点名的元素

根据属性选择

Xpath 可以根据属性来选择元素。

根据属性来选择元素 是通过 这种格式来的 [@属性名='属性值']

注意:

  • 属性名注意前面有个@
  • 属性值一定要用引号, 可以是单引号,也可以是双引号

根据id属性选择

选择 id 为 west 的元素,可以这样 //*[@id='west'] (选择任意元素中含有id=west的)

根据其他属性

同样的道理,我们也可以利用其它的属性选择

比如选择 具有multiple属性的所有页面元素 ,可以这样 //*[@multiple]

属性值包含字符串

要选择 style属性值 包含 color 字符串的 页面元素 ,可以这样 //*[contains(@style,'color')]

要选择 style属性值 以 color 字符串 开头 的 页面元素 ,可以这样 //*[starts-with(@style,'color')]

要选择 style属性值 以 某个 字符串 结尾 的 页面元素 ,大家可以推测是 //*[ends-with(@style,'color')] , 但是,很遗憾,这是xpath 2.0 的语法 ,目前浏览器都不支持

按次序选择

某类型 第几个 子元素

比如要选择 p类型第2个的子元素,就是

//p[2]

第几个子元素

也可以选择第2个子元素,不管是什么类型,采用通配符

比如 选择父元素为div的第2个子元素,不管是什么类型

//div/*[2]

某类型 倒数第几个 子元素

当然也可以选取倒数第几个子元素

比如:

  • 选取p类型倒数第1个子元素
//p[last()]

  • 选取p类型倒数第2个子元素
//p[last()-1]

范围选择

xpath还可以选择子元素的次序范围。

比如

  • 选取option类型第1到2个子元素
//option[position()<=2]

  • 选择class属性为multi_choice的后3个子元素
//*[@class='multi_choice']/*[position()>=last()-2]

组选择、父节点、兄弟节点

组选择

xpath也有组选择, 是用 竖线 隔开多个表达式

比如,要选所有的option元素 和所有的 h4 元素,可以使用

//option | //h4

选择父节点

xpath可以选择父节点, 这是css做不到的。

某个元素的父节点用 /.. 表示

要选择 id 为 china 的节点的父节点,可以这样写 //*[@id='china']/..

兄弟节点选择

xpath也可以选择 后续 兄弟节点,用这样的语法 following-sibling::

比如,要选择 class 为 single_choice 的元素的所有后续兄弟节点 //*[@class='single_choice']/following-sibling::*

等同于CSS选择器 .single_choice ~ *

如果,要选择后续节点中的div节点, 就应该这样写 //*[@class='single_choice']/following-sibling::div

xpath还可以选择 前面的 兄弟节点,用这样的语法 preceding-sibling::

比如,要选择 class 为 single_choice 的元素的 所有 前面的兄弟节点,这样写

//*[@class='single_choice']/preceding-sibling::*

❌
❌