mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
fix(page): fix page.#scrollIntoViewIfNeeded method (#8631)
This patch fixes page.#scrollIntoViewIfNeeded, so that it works with devtools protocol. Now it blocks the main thread and waits until the scrolling action finishes in Chrome. Fallbacks to the old implementation if `DOM.scrollIntoViewIfNeeded` is not supported for Firefox. Issues: #8627, #1805
This commit is contained in:
parent
1de0383abf
commit
b47f066c2c
@ -240,50 +240,52 @@ export class ElementHandle<
|
|||||||
|
|
||||||
async #scrollIntoViewIfNeeded(this: ElementHandle<Element>): Promise<void> {
|
async #scrollIntoViewIfNeeded(this: ElementHandle<Element>): Promise<void> {
|
||||||
const error = await this.evaluate(
|
const error = await this.evaluate(
|
||||||
async (element, pageJavascriptEnabled): Promise<string | false> => {
|
async (element): Promise<string | undefined> => {
|
||||||
if (!element.isConnected) {
|
if (!element.isConnected) {
|
||||||
return 'Node is detached from document';
|
return 'Node is detached from document';
|
||||||
}
|
}
|
||||||
if (element.nodeType !== Node.ELEMENT_NODE) {
|
if (element.nodeType !== Node.ELEMENT_NODE) {
|
||||||
return 'Node is not of type HTMLElement';
|
return 'Node is not of type HTMLElement';
|
||||||
}
|
}
|
||||||
// force-scroll if page's javascript is disabled.
|
return;
|
||||||
if (!pageJavascriptEnabled) {
|
}
|
||||||
element.scrollIntoView({
|
|
||||||
block: 'center',
|
|
||||||
inline: 'center',
|
|
||||||
// @ts-expect-error Chrome still supports behavior: instant but
|
|
||||||
// it's not in the spec so TS shouts We don't want to make this
|
|
||||||
// breaking change in Puppeteer yet so we'll ignore the line.
|
|
||||||
behavior: 'instant',
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const visibleRatio = await new Promise(resolve => {
|
|
||||||
const observer = new IntersectionObserver(entries => {
|
|
||||||
resolve(entries[0]!.intersectionRatio);
|
|
||||||
observer.disconnect();
|
|
||||||
});
|
|
||||||
observer.observe(element);
|
|
||||||
});
|
|
||||||
if (visibleRatio !== 1.0) {
|
|
||||||
element.scrollIntoView({
|
|
||||||
block: 'center',
|
|
||||||
inline: 'center',
|
|
||||||
// @ts-expect-error Chrome still supports behavior: instant but
|
|
||||||
// it's not in the spec so TS shouts We don't want to make this
|
|
||||||
// breaking change in Puppeteer yet so we'll ignore the line.
|
|
||||||
behavior: 'instant',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
this.#page.isJavaScriptEnabled()
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this._client.send('DOM.scrollIntoViewIfNeeded', {
|
||||||
|
objectId: this._remoteObject.objectId,
|
||||||
|
});
|
||||||
|
} catch (_err) {
|
||||||
|
// Fallback to Element.scrollIntoView if DOM.scrollIntoViewIfNeeded is not supported
|
||||||
|
await this.evaluate(
|
||||||
|
async (element, pageJavascriptEnabled): Promise<void> => {
|
||||||
|
const visibleRatio = async () => {
|
||||||
|
return await new Promise(resolve => {
|
||||||
|
const observer = new IntersectionObserver(entries => {
|
||||||
|
resolve(entries[0]!.intersectionRatio);
|
||||||
|
observer.disconnect();
|
||||||
|
});
|
||||||
|
observer.observe(element);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
if (!pageJavascriptEnabled || (await visibleRatio()) !== 1.0) {
|
||||||
|
element.scrollIntoView({
|
||||||
|
block: 'center',
|
||||||
|
inline: 'center',
|
||||||
|
// @ts-expect-error Chrome still supports behavior: instant but
|
||||||
|
// it's not in the spec so TS shouts We don't want to make this
|
||||||
|
// breaking change in Puppeteer yet so we'll ignore the line.
|
||||||
|
behavior: 'instant',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
this.#page.isJavaScriptEnabled()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async #getOOPIFOffsets(
|
async #getOOPIFOffsets(
|
||||||
|
@ -164,7 +164,10 @@ describe('Page.click', function () {
|
|||||||
await page.goto(server.PREFIX + '/offscreenbuttons.html');
|
await page.goto(server.PREFIX + '/offscreenbuttons.html');
|
||||||
const messages: any[] = [];
|
const messages: any[] = [];
|
||||||
page.on('console', msg => {
|
page.on('console', msg => {
|
||||||
return messages.push(msg.text());
|
if (msg.type() === 'log') {
|
||||||
|
return messages.push(msg.text());
|
||||||
|
}
|
||||||
|
return;
|
||||||
});
|
});
|
||||||
for (let i = 0; i < 11; ++i) {
|
for (let i = 0; i < 11; ++i) {
|
||||||
// We might've scrolled to click a button - reset to (0, 0).
|
// We might've scrolled to click a button - reset to (0, 0).
|
||||||
|
@ -203,7 +203,7 @@ describe('ElementHandle specs', function () {
|
|||||||
})
|
})
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
});
|
});
|
||||||
it('should work for TextNodes', async () => {
|
it('should not work for TextNodes', async () => {
|
||||||
const {page, server} = getTestState();
|
const {page, server} = getTestState();
|
||||||
|
|
||||||
await page.goto(server.PREFIX + '/input/button.html');
|
await page.goto(server.PREFIX + '/input/button.html');
|
||||||
|
Loading…
Reference in New Issue
Block a user