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

xpath-helper

v0.1.3

Published

A simple and chainnable API to build complicated XPath queries without the hassle.

Downloads

12

Readme

A chainable API to build complex XPath queries along the different XPath axes. Available both in Python and JavaScript.

  • Documentation — Consult the quick start guide and the online documentation.

Installation

xpath-helper can be installed using npm:

npm install xpath-helper

It can be also be imported as a JavaScript module.

<script type="module" >
  import { xh, filter} from 'https://unpkg.com/xpath-helper@latest/dist/mjs/xpath-helper.js';
  console.log(
    xh.getElementByTag('body').getElementByTag('h1', filter.valueEquals('hello')).toString()
  );
</script> 

Quick-start

You can chain method call on the different XPath axes and easily add filters.

import { xh, filter } from 'xpath-helper';

// Finds a paragraph <p> containing a CSS class 'very-nice-p'
const p = xh.getElementByTag('p', filter.attributeContains('class', 'very-nice-p'));
p.toString(); // "//p[contains(@class, 'very-nice-p')]"

// Finds the paragraph that is following the above one
const nextP = p.getFollowingSiblingByTag('p');
nextP.toString(); // "//p[contains(@class, 'very-nice-p')]/following-sibling::p"

// Finds the modal containing a button with text "Register" 
const modal = xh
  .getElement(filter.valueEquals('Register'))
  .getAncestor(filter.attributeEquals('class', 'modal'));
modal.toString(); // "//*[text() = 'Register']/ancestor::*[@class='modal']"

// An elaborated filter with a boolean expression
const li = xh.getElementByTag("li",
  filter.and(
    filter.or(
      filter.valueContains("JavaScript"), filter.valueContains("Python")
    ),
    filter.hasAttribute("data-description")
));
li.toString() // "//li[((text()[contains(., 'JavaScript')] or text()[contains(., 'Python')]) and @data-description)]"

Chaining

XPath natively lets your build complex queries chaining them along its different axes. Read this article to understand the different XPath axes.

This library let you do exactly the same by chaining method calls along the different axes: descendant aliased as element, descendant-or-self, child, parent, ancestor, ancestor-or-self, preceding, preceding-sibling, following, following-sibling.

For each axis, xpath-helper provides 3 methods, like for instance getElement(filter), getElementByTag(tag, filter), getElementBySVGTag(svgTag, filter) for the descendant axis aliased as element.

The complete JavaScript API can be found here.

import { xh, filter } from 'xpath-helper';

// Finds an element into the page, moves to its parent, 
// finds a brother node of the parent positioned after it.
const el = xh.getElementByTag('p', filter.attributeContains('class', 'very-nice-p'))
  .getParent()
  .getFollowingSiblingByTag('p');
el.toString(); // "//p[contains(@class, 'very-nice-p')]/../following-sibling::p"

// Finds an element into the page, moves to its ancestor 
// containing 'very-nice-p' ass CSS class, 
// finds a brother node of the ancestor positioned before it.
el = xh.getElementByTag('p', filter.attributeContains('class', 'very-nice-p'))
  .getAncestorByTag('div')
  .getPrecedingSibling(filter.hasAttribute('data-foo-bar'));
el.toString(); // "//p[contains(@class, 'very-nice-p')]/../following-sibling::p//p[contains(@class, 'very-nice-p')]/ancestor::div/preceding-sibling::*[@data-foo-bar]"

It is also possible to keep a relative path in a variable and re-use it after.

import { xh, filter } from 'xpath-helper';
// Stores the path of a modal window
const modal = xh.getElement(filter.attributeContains('class', 'modal'));
// Finds the Submit button inside the modal window
const submitButton = modal.getElementByTag('button', filter.valueEquals('Submit'));
// Finds the Cancel button inside the modal window
const cancelButton = modal.getElementByTag('button', filter.valueEquals('Cancel'));

Filters

To select elements more precisely you can add filters: on attributes, on element values, element position, and combine them with conditional operators: and(...), or(...), and not(...).

The complete filter API can be found here.

Attributes

Find below a few examples of filters on attributes.

import { xh, filter } from 'xpath-helper';
// Looks for an element that has a class attribute equals to 'foo'
const el = xh.getElement(filter.attributeEquals('class', 'foo'));
// Looks for an element that has a class attribute containing 'bar'
const el2 = xh.getElement(filter.attributeContains('class', 'bar'));
// Looks for an element that has the attribute 'alt'
const img = xh.getElementByTag('img', filter.hasAttribute('alt'));
// Looks for all the li element with a data-attribute superior to 3
const li = xh.getElementByTag('li', filter.attributeGreaterThan('data-index', 3);)

Values

Find below a few examples of filters on node values.

import { xh, filter } from 'xpath-helper';

// Looks for a button whose text is 'Submit'
const button = xh.getElementByTag('button', filter.valueEquals('Submit'));
// Looks for an element whose text contains 'foobar'
const el = xh.getElement(filter.valueContains('foobar'));
// Looks for all the li element with a value superior to 3
const li = xh.getElementByTag('li', filter.valueGreaterThan(3));

Position

Find below a few examples of filters on node position.

import { xh, filter } from 'xpath-helper';

// Looks for the first li element in ul list
const first = xh.getElementByTag('ul').getElementByTag('li', filter.getFirst());
// Looks for the first li element in ul list
const last = xh.getElementByTag('ul').getElementByTag('li', filter.getLast());
// Looks for the third li element in ul list
const third = xh.getElementByTag('ul').getElementByTag('li', filter.get(3));

Conditional expression

Find below a few examples of filters with conditional expression.

import { xh, filter } from 'xpath-helper';

// Finds an element that has a CSS class 'a-link' and contains an attribute href
let el = xh.getElement(
  filter.attributeContains('class', 'a-link').and(
    filter.hasAttribute('href')
  )
);
el.toString(); // "//*[contains(@class, 'a-link') and (@href)]"

// Finds an element that has a CSS class 'foo' or a CSS class 'bar'
el = xh.getElement(
  filter.attributeContains('class', 'foo').or(
    filter.attributeContains('class', 'bar')
  )
);
el.toString(); // "//*[contains(@class, 'foo') or (contains(@class, 'bar'))]"

// Builds a complex logical expression combining and & or
el = xh.getElement(
  filter.and(
    filter.or(
      filter.valueContains("JavaScript"),
      filter.valueContains("Pyhton")
    ),
      filter.valueContains("package")
  )
);
el.toString(); // "//*[((text()[contains(., 'JavaScript')] or text()[contains(., 'Pyhton')]) and text()[contains(., 'package')])]"

SVG

Navigating into SVG elements from an HTML file can be tricky with XPath, that is why a subset of functions have been added. They are all ending with ...bySVGTag and can be used as below.

import { xh, filter } from 'xpath-helper';

// Stores the path of a modal window
const path = xh.getElementBySVGTag('path', 
  filter.attributeEquals('d', 'M 310 130 L 90 130 L 90 183.63')
);
path.toString(); // "//*[local-name() = 'path'][@d='M 310 130 L 90 130 L 90 183.63']"
    
// Finds the Submit button inside the modal window
const g = xh.getElementBySVGTag('path', filter.attributeEquals('id', 'id-path'))
  .getAncestorBySVGTag('g');
g.toString(); // "//*[local-name() = 'path'][@id='id-path']/ancestor::*[local-name() = 'g']"