npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@imohuan/selector

v1.0.3

Published

支持简单的字符串选择器

Downloads

3

Readme

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 选择同级没有相同元素的元素