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

nested :has() selector not working [v1.17.2] #2131

Closed
ogolovanov opened this issue Mar 14, 2024 · 7 comments
Closed

nested :has() selector not working [v1.17.2] #2131

ogolovanov opened this issue Mar 14, 2024 · 7 comments
Assignees
Labels
bug Confirmed bug that we should fix fixed
Milestone

Comments

@ogolovanov
Copy link

ogolovanov commented Mar 14, 2024

Hi.

It seems nested :has() selectors not working.

String html =
    "<html>" +
        "<head></head>" +
        "<body>" +
            "<div>" +
                "<div><span>hello</span></div>" +
                "<div><span>world</span></div>" +
            "</div>" +
        "</body></html>";
Document document = Jsoup.parse(html);

System.out.println(document.select("div:has(> div:has(> span) + div:has(> span))").size());
System.out.println(document.select("div:has(> span) + div:has(> span)").stream().filter(v -> v.parent().is("div")).count());

Result:

0 // = unexpected
1 // = as expected

@jhy
Copy link
Owner

jhy commented Jul 3, 2024

General nested :has() selectors do work - we have tests here:

@Test public void testNestedHas() {
Document doc = Jsoup.parse("<div><p><span>One</span></p></div> <div><p>Two</p></div>");
Elements divs = doc.select("div:has(p:has(span))");
assertEquals(1, divs.size());
assertEquals("One", divs.first().text());
// test matches in has
divs = doc.select("div:has(p:matches((?i)two))");
assertEquals(1, divs.size());
assertEquals("div", divs.first().tagName());
assertEquals("Two", divs.first().text());
// test contains in has
divs = doc.select("div:has(p:contains(two))");
assertEquals(1, divs.size());
assertEquals("div", divs.first().tagName());
assertEquals("Two", divs.first().text());
}

I thought this might be related to #2137, but it doesn't seem to be.

But, your selector doesn't work in Chrome either.

So I'm not 100% sure here...

@ogolovanov
Copy link
Author

Yes, on one hand - nested :has() is not supported by browsers

The :has() pseudo-class cannot be nested within another :has(). This is because many pseudo-elements exist conditionally based on the styling of their ancestors and allowing these to be queried by :has() can introduce cyclic querying

On the other hand - "pseudo-elements problem" is a problem for browsers, but not for jsoup
https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements

Anyway, it would be nice if you could fix it.

@jhy
Copy link
Owner

jhy commented Aug 5, 2024

Looking at it some more. I don't think it's specifically the nested has.

The query can be simplified to:
div:has(>div + div)

which fails (yet works in browsers). Both div:has(div + div) and div:has(>div) work.

https://try.jsoup.org/~0Imle8wRufxJsGLk0Lt2ReHywC8

So there is an incorrect interaction between the ImmediateParentRun and the ImmediatePreviousSibling, somehow.

@jhy
Copy link
Owner

jhy commented Sep 10, 2024

This is an effective dupe of #2187

@jhy jhy closed this as completed Sep 10, 2024
@jhy jhy added the duplicate This is a duplicate issue or root-cause of another issue label Sep 10, 2024
@jhy jhy removed the duplicate This is a duplicate issue or root-cause of another issue label Sep 10, 2024
@jhy
Copy link
Owner

jhy commented Sep 10, 2024

Actually, my reduction of the issue found the same as #2187, but the initial report is something different.

@jhy jhy reopened this Sep 10, 2024
@jhy
Copy link
Owner

jhy commented Sep 10, 2024

div:has(div:has(span) + div:has(span)) works OK (now) -- without the > direct child of. The direct child of is not taking the appropriate (?) root.

@jhy jhy closed this as completed in 0daab3d Sep 11, 2024
@jhy jhy self-assigned this Sep 11, 2024
@jhy jhy added bug Confirmed bug that we should fix fixed labels Sep 11, 2024
@jhy jhy added this to the 1.18.2 milestone Sep 11, 2024
@jhy
Copy link
Owner

jhy commented Sep 11, 2024

OK, fixed! Thanks for the report. It wasn't the root being incorrect, it was re-use of the inflight Element Iterator that was trashing the match.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Confirmed bug that we should fix fixed
Projects
None yet
Development

No branches or pull requests

2 participants