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

[Bug]: page.click doesn't work with pseudo elements #8902

Closed
GuillaumeGomez opened this issue Sep 5, 2022 · 13 comments
Closed

[Bug]: page.click doesn't work with pseudo elements #8902

GuillaumeGomez opened this issue Sep 5, 2022 · 13 comments

Comments

@GuillaumeGomez
Copy link
Contributor

Bug description

Trying to click on any pseudo element. However, the last version of puppeteer which supported to click on pseudo elements was the 3.X. Since then, it's not possible anymore.

There is no error log or anything, it's just doing nothing.

You can check it on this page with the following CSS selector: .impl-items .rustdoc-toggle summary::before.

It's problematic since it's preventing us from upgrading from the 3.3 version.

Puppeteer version

any version above 3

Node.js version

v14.19.3

npm version

6.14.17

What operating system are you seeing the problem on?

Linux, macOS, Windows

Relevant log output

There is no error log or anything, it's just doing nothing.
@GuillaumeGomez GuillaumeGomez changed the title [Bug]: Impossible to click on pseudo elements [Bug]: page.click doesn't work with pseudo elements Sep 5, 2022
@OrKoN
Copy link
Collaborator

OrKoN commented Sep 5, 2022

Could you please provide a repro script? (is it just newPage/goto/page.click(selector))?

@GuillaumeGomez
Copy link
Contributor Author

Sure, here is a script to test it:

const puppeteer = require('puppeteer');

const elemPath = ".impl-items .rustdoc-toggle summary";

async function checkAttribute(page, expected) {
    const elem = await page.$(elemPath);
    const attr = await page.evaluate(e => e.getAttribute("open"), elem)
    if (attr !== expected) {
        throw new Error(`"${expected}" !== "${attr}"`);
    }
}

async function run () {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.goto("https://doc.rust-lang.org/stable/std/string/struct.String.html");
    // The `<summary>` is open by default so we should have an `open` attribute
    // with `""` as value.
    let err = null;
    try {
        // First we check that the `open` attribute exists.
        await checkAttribute(page, "");
        // Clicking on the pseudo ::before element will collapse the summary.
        await page.click(elemPath + "::before");
        // Waiting a little bit to be sure that the `<summary>` has been collapsed.
        await new Promise(r => setTimeout(r, 100));
        // It should be closed now so the `open` attribute shouldn't be there anymore,
        // `getAttribute` should therefore return null.
        await checkAttribute(page, null);
    } catch (ex) {
        err = ex;
    }
    browser.close();
    if (err !== null) {
        throw err;
    }
    console.log("success!");
}

run();

To help you visualize the ::before part:

Screenshot from 2022-09-05 20-32-10

On the 3.X versions and before, it worked as expected. But starting 4.0 it stopped working.

Don't hesitate to ask if anything isn't clear.

@OrKoN
Copy link
Collaborator

OrKoN commented Sep 6, 2022

@GuillaumeGomez I am getting an error running the script as is using the latest Puppeteer (on line 22 before even the click is sent):

        throw new Error(`"${expected}" !== "${attr}"`);
              ^

Error: "" !== "null"
    at checkAttribute (/Users/alexrudenko/pptr-test/rust.js:9:15)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async run (/Users/alexrudenko/pptr-test/rust.js:22:9)

I also wonder if the selector with ::before is a valid one as even running the following in DevTools console returns null: document.querySelector('.impl-items .rustdoc-toggle summary::before')

@OrKoN
Copy link
Collaborator

OrKoN commented Sep 6, 2022

Also, I have removed the line 22 and tried to use Puppeteer@v3.3.0 and I get an error:

Error: No node found for selector: .impl-items .rustdoc-toggle summary::before

@OrKoN
Copy link
Collaborator

OrKoN commented Sep 6, 2022

So it looks like it must have been not spec-compliant (and worked) at one point. https://www.w3.org/TR/selectors-api/#grammar:~:text=Authors%20are%20advised%20that,defined%20in%20this%20specification says that querying for pseudo elements should not work and I don't Puppeteer ever attempted to customize the platfrom behaviour here.

@GuillaumeGomez
Copy link
Contributor Author

Oh interesting. Since we can't query a pseudo element position either, I'm not sure how I'll go around this limitation. It's a shame it's not spec compliant but I guess there is not much I can do then...

@GuillaumeGomez
Copy link
Contributor Author

Closing since it's not fixeable. Thanks a lot for your answers!

@OrKoN
Copy link
Collaborator

OrKoN commented Sep 6, 2022

@GuillaumeGomez I think you should be able to click on the summary element as that is the element that should be clickable by default. The script reports success! if I do that, although visually it still remains expanded. Perhaps there is a preventDefault handler somewhere or some other custom logic on the page.

@GuillaumeGomez
Copy link
Contributor Author

We have a preventDefault in place to allow to select text but not on the ::before, hence why I was testing it specifically. Also in my code example, I provided the wrong path to check the open attribute: it's on the <details> tag, not on the <summary>. Sorry about that.

In any case, I'll find another way to test this behaviour. Once done, we'll be able to update to the latest puppeteer version when #8903 is released too.

@OrKoN
Copy link
Collaborator

OrKoN commented Sep 6, 2022

Clicking with the negative offset seems to do the trick:

        const el = await page.waitForSelector(elemPath);
        await el.click({
          offset: {
            x: -24,
            y: 8,
          }
        });

@GuillaumeGomez
Copy link
Contributor Author

Nice trick! It works well when you already have these numbers but is it possible to make it work for all pseudo elements? If I remember correctly, we can't query pseudo elements position, but maybe I am (and I ally hope I am) missing something?

@OrKoN
Copy link
Collaborator

OrKoN commented Sep 6, 2022

@GuillaumeGomez unfortunately, I don't think it's possible as they are meant for styling only and are themselves not clickable. The negative offsets only work because clicking on the pseudo element area registers the click on summary element. All other clicks are targeting the content inside the summary tag and therefore the event handler with preventDefault fires.

@GuillaumeGomez
Copy link
Contributor Author

It's a shame but not much we can do. We'll find another way. Thanks a lot for your suggestions!

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

No branches or pull requests

2 participants