1"use strict";(self.webpackChunkplaywright_dev=self.webpackChunkplaywright_dev||[]).push([["4793"],{55589(e,t,n){n.r(t),n.d(t,{metadata:()=>a,default:()=>h,frontMatter:()=>o,contentTitle:()=>r,toc:()=>d,assets:()=>l});var a=JSON.parse('{"id":"navigations","title":"Navigations","description":"Introduction","source":"@site/docs/navigations.mdx","sourceDirName":".","slug":"/navigations","permalink":"/docs/next/navigations","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"id":"navigations","title":"Navigations"},"sidebar":"docs","previous":{"title":"Mock browser APIs","permalink":"/docs/next/mock-browser-apis"},"next":{"title":"Network","permalink":"/docs/next/network"}}'),i=n(74848),s=n(28453);n(13554),n(41647),n(83137);let o={id:"navigations",title:"Navigations"},r,l={},d=[{value:"Introduction",id:"introduction",level:2},{value:"Basic navigation",id:"basic-navigation",level:2},{value:"When is the page loaded?",id:"when-is-the-page-loaded",level:2},{value:"Hydration",id:"hydration",level:2},{value:"Waiting for navigation",id:"waiting-for-navigation",level:2},{value:"Back/Forward Cache (BFCache)",id:"backforward-cache-bfcac
1he",level:2},{value:"Navigation events",id:"navigation-events",level:2}];function c(e){let t={a:"a",admonition:"admonition",code:"code",h2:"h2",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(t.h2,{id:"introduction",children:"Introduction"}),"\n",(0,i.jsx)(t.p,{children:"Playwright can navigate to URLs and handle navigations caused by the page interactions."}),"\n",(0,i.jsx)(t.h2,{id:"basic-navigation",children:"Basic navigation"}),"\n",(0,i.jsx)(t.p,{children:"Simplest form of a navigation is opening a URL:"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-js",children:"// Navigate the page\nawait page.goto('https://example.com');\n"})}),"\n",(0,i.jsxs)(t.p,{children:["The code above loads the page and waits for the web page to fire the ",(0,i.jsx)(t.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event",children:"load"})," event. The load event is fired when the whole page has loaded, including all dependent resources such as stylesheets, scripts, iframes, and images."]}),"\n",(0,i.jsx)(t.admonition,{type:"note",children:(0,i.jsxs)(t.p,{children:["If the page does a client-side redirect before ",(0,i.jsx)(t.code,{children:"load"}),", ",(0,i.jsx)(t.a,{href:"/docs/next/api/class-page#page-goto",children:"page.goto()"})," will wait for the redirected page to fire the ",(0,i.jsx)(t.code,{children:"load"})," event."]})}),"\n",(0,i.jsx)(t.h2,{id:"when-is-the-page-loaded",children:"When is the page loaded?"}),"\n",(0,i.jsxs)(t.p,{children:["Modern pages perform numerous activities after the ",(0,i.jsx)(t.code,{children:"load"})," event was fired. They fetch data lazily, populate UI, load expensive resources, scripts and styles after the ",(0,i.jsx)(t.code,{children:"load"})," event was fired. There is no way to tell that the page is ",(0,i.jsx)(t.code,{children:"loaded"}),", it depends on the page, framework, etc. So when can you start interacting with it?"]}),"\n",(0,i.jsxs)(t.p,{children:["In Playwright you can interact with the page at any moment. It will automatically wait for the target elements to become ",(0,i.jsx)(t.a,{href:"/docs/next/actionability",children:"actionable"}),"."]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-js",children:"// Navigate and click element\n// Click will auto-wait for the element\nawait page.goto('https://example.com');\nawait page.getByText('Example Domain').click();\n"})}),"\n",(0,i.jsx)(t.p,{children:"For the scenario above, Playwright will wait for the text to become visible, will wait for the rest of the actionability checks to pass for that element, and will click it."}),"\n",(0,i.jsx)(t.p,{children:"Playwright operates as a very fast user - the moment it sees the button, it clicks it. In the general case, you don't need to worry about whether all the resources loaded, etc."}),"\n",(0,i.jsx)(t.h2,{id:"hydration",children:"Hydration"}),"\n",(0,i.jsxs)(t.p,{children:["At some point in time, you'll stumble upon a use case where Playwright performs an action, but nothing seemingly happens. Or you enter some text into the input field and it will disappear. The most probable reason behind that is a poor page ",(0,i.jsx)(t.a,{href:"https://en.wikipedia.org/wiki/Hydration_%28web_development%29",children:"hydration"}),"."]}),"\n",(0,i.jsx)(t.p,{children:'When page is hydrated, first, a static version of the page is sent to the browser. Then the dynamic part is sent and the page becomes "live". As a very fast user, Playwright will start interacting with the page the moment it sees it. And if the button on a page is enabled, but the listeners have not yet been added, Playwright will do its job, but the click won\'t have any effect.'}),"\n",(0,i.jsx)(t.p,{children:'A simple way to verify if your page suffers from a poor hydration is to open Chrome DevTools, pick "Slow 3G" network emulation in the Network panel and reload the page. Once you see the element of interest, interact with it. You\'ll see that the button clicks will be ignored and the entered text will be reset by the subsequent page load code. The right fix for this issue is to make sure that all the interactive controls are disabled until after the hydration, when the page is fully functional.'}),"\n",(0,i.jsx)(t.h2,{id:"waiting-for-navigation",children:"Waiting for navigation"}),"\n",(0,i.jsxs)(t.p,{children:["Clicking an element could trigger multiple navigations. In these cases, it is recommended to explicitly ",(0,i.jsx)(t.a,{href:"/docs/next/api/class-page#page-wait-for-url",children:"page.waitForURL()"})," to a specific url."]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-js",children:"await page.getByText('Click me').click();\nawait page.waitForURL('**/login');\n"})}
1),"\n",(0,i.jsx)(t.h2,{id:"backforward-cache-bfcache",children:"Back/Forward Cache (BFCache)"}),"\n",(0,i.jsx)(t.p,{children:"Modern browsers utilize a Back/Forward Cache (BFCache) to instantly load a page when a user navigates back or forward. This is achieved by freezing the page's DOM and JavaScript heap in memory, and thawing it upon return."}),"\n",(0,i.jsx)(t.p,{children:"By default, Playwright disables the BFCache across all browsers to ensure consistent, clean testing environments."}),"\n",(0,i.jsxs)(t.p,{children:["Even if you explicitly enable BFCache, ",(0,i.jsx)(t.strong,{children:"testing BFCache restorations is not supported"}),". Because a BFCache restore skips the network fetch phase, the browser does not fire standard navigation lifecycle events (such as ",(0,i.jsx)(t.code,{children:"commit"}),", ",(0,i.jsx)(t.code,{children:"domcontentloaded"}),", or ",(0,i.jsx)(t.code,{children:"load"}),"). Playwright's internal ",(0,i.jsx)(t.code,{children:"Page"})," state heavily relies on these network-level events to stay synchronized."]}),"\n",(0,i.jsxs)(t.p,{children:["Consequently, triggering a BFCache restore (e.g., via ",(0,i.jsx)(t.code,{children:"page.goBack()"}),") will bypass Playwright's lifecycle tracking, resulting in timeouts and a completely desynchronized ",(0,i.jsx)(t.code,{children:"Page"})," object where subsequent interactions will fail."]}),"\n",(0,i.jsx)(t.h2,{id:"navigation-events",children:"Navigation events"}),"\n",(0,i.jsxs)(t.p,{children:["Playwright splits the process of showing a new document in a page into ",(0,i.jsx)(t.strong,{children:"navigation"})," and ",(0,i.jsx)(t.strong,{children:"loading"}),"."]}),"\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.strong,{children:"Navigation starts"})," by changing the page URL or by interacting with the page (e.g., clicking a link). The navigation intent may be canceled, for example, on hitting an unresolved DNS address or transformed into a file download."]}),"\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.strong,{children:"Navigation is committed"})," when the response headers have been parsed and session history is updated. Only after the navigation succeeds (is committed), the page starts ",(0,i.jsx)(t.strong,{children:"loading"})," the document."]}),"\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.strong,{children:"Loading"})," covers getting the remaining response body over the network, parsing, executing the scripts and firing load events:"]}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.a,{href:"/docs/next/api/class-page#page-url",children:"page.url()"})," is set to the new url"]}),"\n",(0,i.jsx)(t.li,{children:"document content is loaded over network and parsed"}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.a,{href:"/docs/next/api/class-page#page-event-dom-content-loaded",children:"page.on('domcontentloaded')"})," event is fired"]}),"\n",(0,i.jsx)(t.li,{children:"page executes some scripts and loads resources like stylesheets and images"}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.a,{href:"/docs/next/api/class-page#page-event-load",children:"page.on('load')"})," event is fired"]}),"\n",(0,i.jsx)(t.li,{children:"page executes dynamically loaded scripts"}),"\n"]})]})}function h(e={}){let{wrapper:t}={...(0,s.R)(),...e.components};return t?(0,i.jsx)(t,{...e,children:(0,i.jsx)(c,{...e})}):c(e)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.