Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check whether an element would be traversed by selector #249

Closed
strarsis opened this issue Oct 10, 2020 · 10 comments
Closed

Check whether an element would be traversed by selector #249

strarsis opened this issue Oct 10, 2020 · 10 comments

Comments

@strarsis
Copy link

Sometimes it is important to know whether a particular element is traversed by a CSS selector (not selected, e.g. being a descendant). How could css-tree/css-what be used to find this out in a performance-friendly manner?

@fb55
Copy link
Owner

fb55 commented Oct 11, 2020

Could you provide a little more detail on your use-case? Are you trying to determine if an element matches a partial selector?

@strarsis
Copy link
Author

@fb55: Sure, let's say there is a wrapper element with class wrapper and it contains a child element with class child.
Now I want to find out whether the selector .wrapper .child actually traverses the wrapper element, too, this is a very simple example, but there are other more complex queries possible.
A loop could be used which uses a CSS parser and constructs a test query from the existing CSS selector prelude items and checks for a match each time. But this is very ineffecient.
css-select already traversed that wrapper element with just one findOne call, hence it knows which elements that selector traverses.

@fb55
Copy link
Owner

fb55 commented Oct 11, 2020

First, practically speaking, css-select checks selectors right-to-left, so we would actually look for nodes with a child class first, then traverse the tree up looking for ancestors with the wrapper class.

Second, to execute a selector bit-by-bit, you should have a look at https://github.com/cheeriojs/cheerio-select. This library implements positional checks from jQuery, which depend on left-to-right execution. You would have to break up selectors before traversals (without removing an element), but otherwise follow the flow of this library. css-what's next version will include an isTraversal helper that should be useful here.

@strarsis
Copy link
Author

strarsis commented Oct 11, 2020

@fb55: The isTraversal feature would be really helpful! Could css-what also be used on the selector (prelude) AST of css-tree?

Could the adapter/css-select store a statistics object for each selector that contains all the traversed elements?

One issue where this can be used: svg/svgo#1077 (comment)

@fb55
Copy link
Owner

fb55 commented Oct 17, 2020

The isTraversal feature would be really helpful!

The latest release ships with it 🙂

Could css-what also be used on the selector (prelude) AST of css-tree?

Looks like css-tree selectors would have to be stringified first, before they can be processed by css-what.

Could the adapter/css-select store a statistics object for each selector that contains all the traversed elements?

You would have to chunk up the selector using the above method, but would then be able to add all of this information.

@strarsis
Copy link
Author

strarsis commented Oct 17, 2020

The latest release ships with it 🙂

Fantastic!

@fb55
Copy link
Owner

fb55 commented Oct 20, 2020

Assuming this has been resolve, let me know if it hasn't!

@fb55 fb55 closed this as completed Oct 20, 2020
@strarsis
Copy link
Author

@fb55: How can I use the isTraversal method? I can find it in css-select, how can I invoke it?
Or do I have to use it from css-what? It is not defined for cssSelect.isTraversal.

@fb55
Copy link
Owner

fb55 commented Jan 20, 2021

The TypeScript types should be helpful for understanding what this is doing: isTraversal takes a Selector instance (css-what's parse returns a Selector[][], an array of arrays of selectors), and returns whether the Selector is a traversal.

To check if a selector has a traversal, you'd do:

cssWhat.parse('div, a > b').some(subSelector => subSelector.some(cssWhat.isTraversal))

@strarsis
Copy link
Author

@fb55: This is a really useful tool, thanks.
When I got a DOM element or multiple DOM elements and a CSS selector string,
how can I find out whether that CSS selector would traverse through that DOM element or these DOM elements,
not necessarily select it or them, but traverse through them during its journey to possible matching DOM elements?
This would be similar to cssSelect.is but it would return true if it hits that DOM element, even when there are still further selectors and possible DOM elements to traverse.

Example:

<?xml version="1.0" standalone="no"?>
<svg width="200" height="250" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <g class="wrapper">
    <g class="secondary-wrapper">
      <rect class="some-element" x="10" y="10" width="30" height="30" stroke="black" fill="transparent" stroke-width="5"/>
    </g>
  </g>
</svg>

cssSelect.isTraversed( astInstanceOfWrapper, '.wrapper .some-element', opts) => true,
cssSelect.isTraversed( astInstanceOfSecondaryWrapper, '.wrapper .some-element', opts) => true,
because the .wrapper and .secondary-wrapper elements have to be traversed in order to get to .some-elementas the selector intends.

Would this be easily achievable with css-select? It already uses an intelligent and high-performant way to traverse through the DOM and it 'just' would have to stop when it hits that DOM element during traversal and return true (and at the end false), right?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants