1.0.3 • Published 2 years ago

@imohuan/selector v1.0.3

Weekly downloads
-
License
ISC
Repository
-
Last release
2 years ago

Css选择器

声明 interface

export interface QueryChar {
  /** 获取 `querySelectorAll` (默认: `@`) */
  all: string;
  /** 获取 `document.documentElement.querySelector` (默认: `!`)*/
  root: string;
  /** 获取 循环根部 (默认: `_`)*/
  current: string;
  /** 模板替换变量 (默认: `/\{([_a-zA-Z0-9]+)}/g`)*/
  var: RegExp;
  /** 不进行获取, 直接返回 * 后面的内容 (默认: `*`)*/
  no: string;
}

export interface QueryOption<T> {
  /* class选择器 */
  cls: string | string[];
  /* 获取json数据的时候使用 */
  value: string | string[];
  /* 自定义字符 */
  char: QueryChar;
  /* 类似后处理,这里提供了快捷方便的预制函数,提供有 int(转为整数),float(转为小数),trim(去除首位空格),url(补全URL),filterEmpty(过滤空数组),json(JSON化数据); */
  rules: RuleItem[];
  /* 父节点 class 或者 ParseDom(为内部获取节点的一个类, 类似 dom) */
  parent: string | ParseDom;
  /* 对获取的数据进行处理 data(获取class的内容) option(内置的一些配置和参数) */
  processing: (data: any, option: ProcessingOption<T>) => any;
  /* 替换class可以自己将常用的class简便化 */
  replaces: QueryReplace[];
}

:::

预先配置

预制代码

import { resolve } from "path";
import { getSelector } from "@imohuan/selector";

const html = `<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div class="head">
      <span>经济xxx</span>
    </div>

    <ul>
      <li id="xxx">
        <div class="title">       标题1          </div>
        <div class="description">描述1</div>
        <div class="tags"><span class="tag">1</span> <span class="tag">2</span></div>
      </li>
      <li>
        <div class="title">标题2      </div>
        <div class="description">描述2</div>
        <div class="tags"><span class="tag">1</span><span class="tag">2</span></div>
      </li>
      <li>
        <div class="title">标题3</div>
        <div class="description">描述3</div>
        <div class="tags"><span class="tag">1</span><span class="tag">2</span></div>
      </li>
    </ul>
  </body>
</html>
`;

const parser = getSelector(html, { name: "@imohuan/imohuan", age: 100 });
console.log(parser.query({ cls: ".head::text", rules: ["trim"] }));
console.log(parser.query({ cls: ".what::text|.dddd::text|.head::text", rules: ["trim"] }));

输出

经济xxx
经济xxx

选择器解析

  • * 不进行处理
const cls = "*Hello";
// 注意: 只能选择器首位
// 输出: "hello"
  • {name} 使用变量
const cls = "*{testVar}";
// 注意: 使用变量之后依然会进行解析,如果不需要进行解析的话,可以在开头添加*
// 输出: "测试变量"
  • @ 获取所有匹配选择器的元素
const cls = "@a::attr(href)";
// 注意: 只能选择器首位
// 输出: ["https://1....", "https://2....",...]
  • ! 从根节点获取选择器 (同: document.documentElement.querySelector)
/** 伪代码 html 表述需要解析的数据 */
const html = `
<html>
  <title>标题</title>
  <body>
    <ul>
      <li><span class="title">1</span></li>
      <li><span class="title">2</span></li>
    </ul>
  </body>
</html>
`;

/** 替换 parsers 参数 */
const parsers = [
  {
    name: "list",
    parent: "ul li",
    children: [
      { name: "title", cls: ".title::text" },
      { name: "title2", cls: "title::text" },
      { name: "rootTitle", cls: "!title::text" }
    ]
  }
];
// 注意: 只能选择器首位
// 输出: [{ title: "1", title2: null, rootTitle: "标题" }, { title: "2", title2: null, rootTitle: "标题" }]
  • _children中获取循环部位的根节点
/** 伪代码 html 表述需要解析的数据 */
const html = `
<html>
  <title>标题</title>
  <body>
    <ul>
      <li title="1"></li>
      <li title="2"></li>
    </ul>
  </body>
</html>
`;

/** 替换 parsers 参数 */
const parsers = [
  {
    name: "list",
    parent: "ul li",
    children: [{ name: "title", cls: "_::attr(title)" }]
  }
];
// 注意: 只能选择器首位
// 输出: [{ title: "1" }, { title: "2" }]
  • | 多个选择器从前往后匹配有内容的数据
/** 伪代码 html 表述需要解析的数据 */
const html = `<span class="hello">你好</span>`;
const cls = ".n::text|.what::text|.hello::text";
// 注意: 只能选择器首位
// 输出: "你好"
  • ::text 获取元素的内容
  • ::html 获取元素的 HTML 内容
  • ::attr(name) 获取元素属性,name表示属性名称

自定义替换

默认配置 (无需配置)

export const defaultReplaces: QueryReplace[] = [
  [
    /:eq\(([0-9n\-+]+)\)/g,
    (_value: any, numStr: string) => {
      if (numStr.indexOf("n") !== -1) return `:nth-of-type(${numStr})`;
      const num = parseInt(numStr);
      if (num < 0) return `:nth-last-of-type(${Math.abs(num)})`;
      return `:nth-of-type(${num})`;
    },
    "eq 转换为 nth-of-type"
  ],
  [
    /:ed\(([0-9n\-+]+)\)/g,
    (_value: any, numStr: string) => {
      if (numStr.indexOf("n") !== -1) return `:nth-child(${numStr})`;
      const num = parseInt(numStr);
      if (num < 0) return `:nth-last-child(${Math.abs(num)})`;
      return `:nth-child(${num})`;
    },
    "ed 转换为 nth-child"
  ]
];

使用配置

console.log(parser.query({ cls: "ul li:eq(2)::text", rules: ["trim"], replaces: defaultReplaces }));
// cls将会被解析为: ul li:nth-of-type(2)::text

JSON 配置

::: tip 提醒 无需了解下列代码含义

只需要查看parentvalue字段即可(因为它同cls含义一样) :::

const jsonData = {
  page: 1,
  size: 10,
  list: [
    { title: "title1", url: "https://22222", tag: [1, 2, 3, 4, 5] },
    { title: "title2", url: "https://33333", tag: [11, 22, 33, 44, 55] }
  ]
};
const json = { global: jsonData, current: jsonData };
expect(queryJson(json, { value: "page" }, {})).toBe(1);
expect(queryJson(json, { value: "size" }, {})).toBe(10);
expect(queryJson(json, { value: "list[0].title" }, {})).toBe("title1");
expect(queryJson(json, { parent: "list", value: "title" }, {})).toEqual(["title1", "title2"]);
expect(queryJson(json, { parent: "list", value: "tag" }, {})).toEqual([
  [1, 2, 3, 4, 5],
  [11, 22, 33, 44, 55]
]);
expect(queryJson(json, { parent: "list", value: "!page" }, {})).toEqual([1, 1]);
expect(queryJson(json, { parent: "list", value: "!size" }, {})).toEqual([10, 10]);
expect(queryJson(json, { value: "{pageFor}" }, data)).toBe(1);
expect(queryJson(json, { value: "*123", rules: ["trim"] }, {})).toEqual("123");
expect(queryJson(json, { value: "*456", rules: ["trim"] }, {})).toEqual("456");
expect(queryJson(json, { value: "*{count}", rules: ["trim"] }, data)).toEqual("3");
expect(queryJson(json, { value: "*{hello}", rules: ["trim"] }, data)).toEqual("world");
expect(queryJson(json, { value: "*{hello}_{count}", rules: ["trim"] }, data)).toEqual("world_3");
expect(queryJson(json, { value: "*{arr}", rules: ["trim"] }, data)).toEqual("1,2,3");

案例

import { getSelector } from "im-selector";
// const { getSelector } = require("im-selector"); // 也可以
const html = `<a href="#1">1</a>
              <a href="#2">2</a>
              <a href="#3">3</a>
              <a href="#4">4</a>
              <a href="#5">5</a>
              <a href="#6">6</a>
              <a href="#7">7</a>
              <a href="#8">8</a>
              <a href="#9">9</a>
              <a href="#10">10</a>`;
const parser = Selector.getSelector(html, { name: "im-selector" });
console.log("使用变量", parser.query({ cls: "*{name}" }));
console.log("多个Class找到存在的值", parser.query({ cls: ".xxx::text|a::text", rules: ["trim"] }));
console.log("全选", parser.query({ cls: "@a::text", rules: ["trim"] }));

选择器

  • 常用选择器
  • id 选择 #app
  • class 选择 .app
  • 标签选择 span
  • 后代 div span
  • 子代 div > span
  • 邻接兄弟 span + div
  • 通用兄弟 span ~ div
  • 属性选择
  • 存在属性 span[attr] => a[title]
  • 属性相等 span[attr=value] => a[href="https://example.com"]
  • 包含(空格分开) [attr~=value] => li[class~="a"]
  • 开头等于或则已 zh-开头 [attr|=value] => div[lang|="zh"]
  • 开头包含 [attr^=value] => a[src^="https"]
  • 结尾包含 [attr$=value] => a[src$=".vue"]
  • 包含 [attr*=value] => a[src*="hello"]
  • 伪类
  • :eq(1) -> :last-of-type(1)
  • :eq(-1) -> :nth-last-of-type(1)
  • :ed(1) -> :last-child(1)
  • :ed(-1) -> :nth-last-child(1)

    • :nth-child(2n+1) -> span:nth-child(-n+3)选择父级下面第几个元素
    • :last-of-type 选择同级同元素的最后一个
    • :last-child 选择父元素下最后一个元素
    • :nth-of-type 选择父级下面同类型的第几个元素
    • :nth-last-child
    • :nth-last-of-type

CSS 伪类首先找到所有当前元素的兄弟元素,然后按照位置先后顺序从 1 开始排序,选择的结果为 CSS 伪类* :nth-child 括号中表达式(an+b)匹配到的元素集合(n=0,1,2,3...

  • :not(选择器) 匹配作为值传入自身的选择器未匹配的物件
  • :only-child选择没有兄弟的元素
  • :only-of-type 选择同级没有相同元素的元素