Add polyfill for `:has()` CSS4 pseudo-selector to element hiding filters
|Cc:||mapx, Lain_13, sebastian, fhd, mario, kzar, rach, arthur, sergz, oleksandr||Blocked By:||#4394, #4726, #4796, #4962|
|Blocking:||#2360, #5094||Platform:||Unknown / Cross platform|
Description (last modified by fhd)
(taken from original description in #2360 about hiding elements by selecting their child elements)
Currently, there's no way to hide an element on a page dependent on its children. This proposed solution is based on existing CSS functionality, so it will be possible to bind an observer to the document to check for new parent nodes and bind another observer object to them to check for changes within only these particular nodes in case specific child node will be added. Over time it will be possible to completely disable such functionality due to native support for CSS4 selectors in newer browser versions.
It may be possible to look at the jQuery code for this functionality since they supported :has() pseudo-selector in their selectors long before it was even added into the CSS4 drafts.
Generally, this proposal has similar issues as CSS property filters such as requiring to hide elements manually and to observe the page for changes at runtime.
:has() and CSS property filters have to work in conjunction, therefore they need to be stored in the same filter class (see #4394). In addition, we will probably have to change the way CSS property filters work: Currently we eventually generate standard CSS rules for them, which are then being applied by the browser. For :has(), we have to apply the rules manually.
What to change
- Allow :has() to be specified within element hiding filter CSS selectors
- Determine whether :has() pseudo-selector is supported natively (document.querySelector("abp:has(abp)") throws a syntax error exception if :has() is not supported)
- Otherwise detect the pattern PREFIX:has(SELECTOR)SUFFIX inside the selector string and run the following logic upon encountering it:
- Extract selectors from selector string because it might contain multiple separate selectors
- For each element a matching PREFIX in selector
- Check whether SELECTOR matches any of a's child elements
- If so, hide all elements matching SUFFIX inside a or a if SUFFIX does not exist
- Make sure :has() and -abp-properties works in conjunction
- Recursively resolve further instances of :has() inside SELECTOR and SUFFIX using the same logic (e.g. to resolve abc:has(def:has(ghi):has(jkl)) mno:has(pqr))
- Observe DOM changes to apply filters to dynamically added/modified elements