/*! For license information please see 7e31b798.6151c596.js.LICENSE.txt */ "use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[93713],{48728:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>a,contentTitle:()=>i,default:()=>h,frontMatter:()=>l,metadata:()=>r,toc:()=>c});var s=t(85893),o=t(11151);const l={},i="Page interactions",r={id:"guides/page-interactions",title:"Page interactions",description:"Puppeteer allows you interact with the pages in various ways.",source:"@site/versioned_docs/version-22.8.2/guides/page-interactions.md",sourceDirName:"guides",slug:"/guides/page-interactions",permalink:"/guides/page-interactions",draft:!1,unlisted:!1,tags:[],version:"22.8.2",frontMatter:{},sidebar:"docs",previous:{title:"Browser management",permalink:"/guides/browser-management"},next:{title:"JavaScript execution",permalink:"/guides/javascript-execution"}},a={},c=[{value:"Locators",id:"locators",level:2},{value:"Use cases",id:"use-cases",level:3},{value:"Waiting for an element",id:"waiting-for-an-element",level:4},{value:"Waiting for a function",id:"waiting-for-a-function",level:4},{value:"Clicking an element",id:"clicking-an-element",level:4},{value:"Clicking an element matching a criteria",id:"clicking-an-element-matching-a-criteria",level:4},{value:"Filling out an input",id:"filling-out-an-input",level:4},{value:"Retrieving an element property",id:"retrieving-an-element-property",level:4},{value:"Hover over an element",id:"hover-over-an-element",level:4},{value:"Scroll an element",id:"scroll-an-element",level:4},{value:"Configuring locators",id:"configuring-locators",level:3},{value:"Getting locator events",id:"getting-locator-events",level:3},{value:"Query Selectors",id:"query-selectors",level:2},{value:"P Selectors",id:"p-selectors",level:3},{value:">>> and >>>> combinators",id:"-and--combinators",level:4},{value:"Example",id:"example",level:5},{value:"P-elements",id:"p-elements",level:4},{value:"Text selectors (-p-text)",id:"text-selectors--p-text",level:5},{value:"Example",id:"example-1",level:6},{value:"XPath selectors (-p-xpath)",id:"xpath-selectors--p-xpath",level:5},{value:"Example",id:"example-2",level:6},{value:"ARIA selectors (-p-aria)",id:"aria-selectors--p-aria",level:5},{value:"Example",id:"example-3",level:6},{value:"Custom selectors",id:"custom-selectors",level:4},{value:"Custom Selectors",id:"custom-selectors-1",level:5},{value:"Custom framework components selector",id:"custom-framework-components-selector",level:5},{value:"Web Components",id:"web-components",level:5},{value:"Query Selectors (legacy)",id:"query-selectors-legacy",level:2},{value:"CSS",id:"css",level:3},{value:"Example",id:"example-4",level:4},{value:"Built-in selectors",id:"built-in-selectors",level:3},{value:"Text selectors (text/)",id:"text-selectors-text",level:4},{value:"Example",id:"example-5",level:5},{value:"XPath selectors (xpath/)",id:"xpath-selectors-xpath",level:4},{value:"Example",id:"example-6",level:5},{value:"ARIA selectors (aria/)",id:"aria-selectors-aria",level:4},{value:"Example",id:"example-7",level:5},{value:"Pierce selectors (pierce/)",id:"pierce-selectors-pierce",level:4},{value:"Example",id:"example-8",level:5},{value:"Custom selectors",id:"custom-selectors-2",level:3}];function d(e){const n={a:"a",admonition:"admonition",blockquote:"blockquote",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",h4:"h4",h5:"h5",h6:"h6",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,o.a)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.h1,{id:"page-interactions",children:"Page interactions"}),"\n",(0,s.jsx)(n.p,{children:"Puppeteer allows you interact with the pages in various ways."}),"\n",(0,s.jsx)(n.h2,{id:"locators",children:"Locators"}),"\n",(0,s.jsx)(n.p,{children:"Locators is a new, experimental API that combines the functionalities of\nwaiting and actions. With additional precondition checks, it\nenables automatic retries for failed actions, resulting in more reliable and\nless flaky automation scripts."}),"\n",(0,s.jsx)(n.admonition,{type:"note",children:(0,s.jsx)(n.p,{children:"Locators API is experimental and we will not follow semver for breaking changes\nin the Locators API."})}),"\n",(0,s.jsx)(n.h3,{id:"use-cases",children:"Use cases"}),"\n",(0,s.jsx)(n.h4,{id:"waiting-for-an-element",children:"Waiting for an element"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ts",children:"await page.locator('button').wait();\n"})}),"\n",(0,s.jsx)(n.p,{children:"The following preconditions are automatically checked:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["Waits for the element to become\n",(0,s.jsx)(n.a,{href:"https://pptr.dev/api/puppeteer.elementhandle.isvisible/",children:"visible"})," or hidden."]}),"\n"]}),"\n",(0,s.jsx)(n.h4,{id:"waiting-for-a-function",children:"Waiting for a function"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ts",children:"await page\n .locator(() => {\n let resolve!: (node: HTMLCanvasElement) => void;\n const promise = new Promise(res => {\n return (resolve = res);\n });\n const observer = new MutationObserver(records => {\n for (const record of records) {\n if (record.target instanceof HTMLCanvasElement) {\n resolve(record.target);\n }\n }\n });\n observer.observe(document);\n return promise;\n })\n .wait();\n"})}),"\n",(0,s.jsx)(n.h4,{id:"clicking-an-element",children:"Clicking an element"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ts",children:"await page.locator('button').click();\n"})}),"\n",(0,s.jsx)(n.p,{children:"The following preconditions are automatically checked:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Ensures the element is in the viewport."}),"\n",(0,s.jsxs)(n.li,{children:["Waits for the element to become\n",(0,s.jsx)(n.a,{href:"https://pptr.dev/api/puppeteer.elementhandle.isvisible/",children:"visible"})," or hidden."]}),"\n",(0,s.jsx)(n.li,{children:"Waits for the element to become enabled."}),"\n",(0,s.jsx)(n.li,{children:"Waits for the element to have a stable bounding box over two consecutive\nanimation frames."}),"\n"]}),"\n",(0,s.jsx)(n.h4,{id:"clicking-an-element-matching-a-criteria",children:"Clicking an element matching a criteria"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ts",children:"await page\n .locator('button')\n .filter(button => !button.disabled)\n .click();\n"})}),"\n",(0,s.jsx)(n.p,{children:"The following preconditions are automatically checked:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Ensures the element is in the viewport."}),"\n",(0,s.jsxs)(n.li,{children:["Waits for the element to become\n",(0,s.jsx)(n.a,{href:"https://pptr.dev/api/puppeteer.elementhandle.isvisible/",children:"visible"})," or hidden."]}),"\n",(0,s.jsx)(n.li,{children:"Waits for the element to become enabled."}),"\n",(0,s.jsx)(n.li,{children:"Waits for the element to have a stable bounding box over two consecutive\nanimation frames."}),"\n"]}),"\n",(0,s.jsx)(n.h4,{id:"filling-out-an-input",children:"Filling out an input"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ts",children:"await page.locator('input').fill('value');\n"})}),"\n",(0,s.jsx)(n.p,{children:"Automatically detects the input type and choose an appropriate way to fill it out with the provided value."}),"\n",(0,s.jsx)(n.p,{children:"The following preconditions are automatically checked:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Ensures the element is in the viewport."}),"\n",(0,s.jsxs)(n.li,{children:["Waits for the element to become\n",(0,s.jsx)(n.a,{href:"https://pptr.dev/api/puppeteer.elementhandle.isvisible/",children:"visible"})," or hidden."]}),"\n",(0,s.jsx)(n.li,{children:"Waits for the element to become enabled."}),"\n",(0,s.jsx)(n.li,{children:"Waits for the element to have a stable bounding box over two consecutive\nanimation frames."}),"\n"]}),"\n",(0,s.jsx)(n.h4,{id:"retrieving-an-element-property",children:"Retrieving an element property"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ts",children:"const enabled = await page\n .locator('button')\n .map(button => !button.disabled)\n .wait();\n"})}),"\n",(0,s.jsx)(n.h4,{id:"hover-over-an-element",children:"Hover over an element"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ts",children:"await page.locator('div').hover();\n"})}),"\n",(0,s.jsx)(n.p,{children:"The following preconditions are automatically checked:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Ensures the element is in the viewport."}),"\n",(0,s.jsxs)(n.li,{children:["Waits for the element to become\n",(0,s.jsx)(n.a,{href:"https://pptr.dev/api/puppeteer.elementhandle.isvisible/",children:"visible"})," or hidden."]}),"\n",(0,s.jsx)(n.li,{children:"Waits for the element to have a stable bounding box over two consecutive\nanimation frames."}),"\n"]}),"\n",(0,s.jsx)(n.h4,{id:"scroll-an-element",children:"Scroll an element"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ts",children:"await page.locator('div').scroll({\n scrollLeft: 10,\n scrollTop: 20,\n});\n"})}),"\n",(0,s.jsx)(n.p,{children:"The following preconditions are automatically checked:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Ensures the element is in the viewport."}),"\n",(0,s.jsxs)(n.li,{children:["Waits for the element to become\n",(0,s.jsx)(n.a,{href:"https://pptr.dev/api/puppeteer.elementhandle.isvisible/",children:"visible"})," or hidden."]}),"\n",(0,s.jsx)(n.li,{children:"Waits for the element to have a stable bounding box over two consecutive\nanimation frames."}),"\n"]}),"\n",(0,s.jsx)(n.h3,{id:"configuring-locators",children:"Configuring locators"}),"\n",(0,s.jsx)(n.p,{children:"Locators can be configured to tune configure the preconditions and other other options:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ts",children:"await page\n .locator('button')\n .setEnsureElementIsInTheViewport(false)\n .setTimeout(0)\n .setVisibility(null)\n .setWaitForEnabled(false)\n .setWaitForStableBoundingBox(false)\n .click();\n"})}),"\n",(0,s.jsx)(n.h3,{id:"getting-locator-events",children:"Getting locator events"}),"\n",(0,s.jsx)(n.p,{children:"Currently, locators support a single event that notifies you when the locator is about to perform the action:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ts",children:"let willClick = false;\nawait page\n .locator('button')\n .on(LocatorEvent.Action, () => {\n willClick = true;\n })\n .click();\n"})}),"\n",(0,s.jsx)(n.p,{children:"This event can be used for logging/debugging or other purposes. The event might\nfire multiple times if the locator retries the action."}),"\n",(0,s.jsx)(n.h2,{id:"query-selectors",children:"Query Selectors"}),"\n",(0,s.jsx)(n.p,{children:"Queries are the primary mechanism for interacting with the DOM on your site. For example, a typical workflow goes like:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ts",children:"// Import puppeteer\nimport puppeteer from 'puppeteer';\n\n(async () => {\n // Launch the browser\n const browser = await puppeteer.launch();\n\n // Create a page\n const page = await browser.newPage();\n\n // Go to your site\n await page.goto('YOUR_SITE');\n\n // Query for an element handle.\n const element = await page.waitForSelector('div > .class-name');\n\n // Do something with element...\n await element.click(); // Just an example.\n\n // Dispose of handle\n await element.dispose();\n\n // Close browser.\n await browser.close();\n})();\n"})}),"\n",(0,s.jsxs)(n.h3,{id:"p-selectors",children:[(0,s.jsx)(n.code,{children:"P"})," Selectors"]}),"\n",(0,s.jsxs)(n.p,{children:["Puppeteer uses a superset of the CSS selector syntax for querying. We call this syntax ",(0,s.jsx)(n.em,{children:"P selectors"})," and it's supercharged with extra capabilities such as deep combinators and text selection."]}),"\n",(0,s.jsx)(n.admonition,{type:"caution",children:(0,s.jsx)(n.p,{children:"Although P selectors look like real CSS selectors (we intentionally designed it this way), they should not be used for actually CSS styling. They are designed only for Puppeteer."})}),"\n",(0,s.jsx)(n.admonition,{type:"note",children:(0,s.jsxs)(n.p,{children:['P selectors only work on the first "depth" of selectors; for example, ',(0,s.jsx)(n.code,{children:":is(div >>> a)"})," will not work."]})}),"\n",(0,s.jsxs)(n.h4,{id:"-and--combinators",children:[(0,s.jsx)(n.code,{children:">>>"})," and ",(0,s.jsx)(n.code,{children:">>>>"})," combinators"]}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:">>>"})," and ",(0,s.jsx)(n.code,{children:">>>>"})," are called ",(0,s.jsx)(n.em,{children:"deep descendent"})," and ",(0,s.jsx)(n.em,{children:"deep"})," combinators respectively. Both combinators have the effect of going into shadow hosts with ",(0,s.jsx)(n.code,{children:">>>"})," going into every shadow host under a node and ",(0,s.jsx)(n.code,{children:">>>>"})," going into the immediate one (if the node is a shadow host; otherwise, it's a no-op)."]}),"\n",(0,s.jsx)(n.admonition,{type:"note",children:(0,s.jsxs)(n.p,{children:["A common question is when should ",(0,s.jsx)(n.code,{children:">>>>"})," be chosen over ",(0,s.jsx)(n.code,{children:">>>"})," considering the flexibility of ",(0,s.jsx)(n.code,{children:">>>"}),". A similar question can be asked about ",(0,s.jsx)(n.code,{children:">"})," and a space; choose ",(0,s.jsx)(n.code,{children:">"})," if you do not need to query all elements under a given node and a space otherwise. This answer extends to ",(0,s.jsx)(n.code,{children:">>>>"})," (",(0,s.jsx)(n.code,{children:">"}),") and ",(0,s.jsx)(n.code,{children:">>>"})," (space) naturally."]})}),"\n",(0,s.jsx)(n.h5,{id:"example",children:"Example"}),"\n",(0,s.jsx)(n.p,{children:"Suppose we have the markup"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-html",children:'\n \n \n \n \n \n

Light content

\n
\n
\n
\n'})}),"\n",(0,s.jsxs)(n.blockquote,{children:["\n",(0,s.jsxs)(n.p,{children:["Note: ",(0,s.jsx)(n.code,{children:'