The objective I am pursuing is the following: I want to be able to open a web site or web application and show a floating toolbar on top of the web site’s content. As I am making my way through the site or application, I can use functions from the toolbar – such as taking a snapshot of the current page state and saving it to file, downloading all images in the current page, fill in the form fields using predefined or random values and start a demo.
In various prior articles I have discussed Playwright – an open source Node library (also available for Java, C#, Go, Python) that provides access to an embedded web browser that can be fully controlled from the host application. Playwright allows interaction from the browser content with the application and vice versa. Node code can manipulate the DOM and the JavaScript context in the browser – add or alter DOM elements and JavaScript functions and objects. Node code can trap browser events as well as trigger browser events.
What I want to do this time is:
- creating a floating, fixed, semi-transparant toolbar – using HTML and CSS
- add this toolbar – using Playwright – on top of a site or app that is loaded into the embedded browser
- ensure that the toolbar stays around whenever the browser is navigated to a new page (it probably needs to be added again after a page is refreshed or loads from, a new URL)
- implement the Snapshot function linked to a toolbar option: when the option is triggered, the Playwright function for taking a snapshot of the page is activated. Note: the toolbar itself should be hidden at the time of taking the snapshot
- show and hide the toolbar using a short cut key combination
- add a toolbar option for loading all images in a page
The next challenge I have identified: using Playwright, I want to create a demonstration of a web application or web site. Playwright commands are used to perform the actions in the web page. What is special, is that these actions are to be grouped in steps. Together, these steps form a scenario. Using the toolbar – the user can play (one or more steps), pause, skip (a step), reset (return execution to the first step). In between the execution of the automated steps, the user can interact freely with the web page. The demo steps do not have to be executed or they do not need to be the only thing that is done. Each step can have a title and a description that could be shown in the toolbar. Additionally, associated with a step can be a call out – a text balloon or an arrow with text – that can be positioned on top the page near the element that is manipulated in the step.
In this way, demo scripts or tutorials can be created that take a user by the hand in a live web environment. The user can choose to let the prepared scenario play out or to intervene, contribute or event take over (in part).
As a next step, we could create multiple scenarios for a page or app and the user could choose a scenario to execute in the context of a page. At any point, the user can decide to run a different scenario.
Note: all code for this article is available on GitHub: https://github.com/lucasjellema/playwright-scenarios/tree/main/floating_menu.
1 creating a floating, fixed, semi-transparent toolbar – using HTML and CSS
My best resources for creating the floating toolbar was W3 Schools (https://www.w3schools.com/howto/howto_js_navbar_sticky.asp and others). This is what I have created:
Each item is an link. When the link is clicked, an action can be triggered.
The HTML and CSS is relatively simple. First the CSS:
2. Add this toolbar on top of a site or app that is loaded into the embedded browser using Playwright
Playwright allows us to manipulate a page that is loaded in the browser context. We can add styles and JavaScript – and have JavaScript executed.
As soon as the new browser context is created, a function defined in the Node context (prepareToolbar) is exposed inside the browser on the window object (as prepareToolbarFunction). Subsequently, using addInitScript(), a JavaScript command is set up – a call to window.prepareToolbarFunction() which in effect is a call to function prepareToolbar. This command is to be executed whenever the page or an iframe within the page is created, reloaded or navigated; this even applies to additional tabs that the user may open in the browser. Function prepareToolbar() does two things: it creates a Style Tag that defines the CSS for the toolbar (through addStyleTag()) and it creates the HTML elements for the toolbar in in the page through the call to addHTML. In addHTML(), a DIV element is added as direct child of BODY. This element is the container for the toolbar. The HTML content for the DIV – the toolbar itself – is defined in the constant navBar that is passed in the call to addHTML() and set as the innerHTML property on the div element.
The toolbar is added to the DOM as child of the BODY element. Whenever the DOM is refreshed and the BODY element is discarded, the toolbar disappears along with it. Because the initscript is executed by Playwright after every action that may have destroyed BODY and toolbar, we have made sure that the toolbar stays around (== gets recreated) whenever the browser is navigated to a new page.
This code refers to two constants that define the CSS and HTML respectively:
3. Implement the Snapshot function linked to a toolbar option
When the option is triggered, the Playwright function for taking a snapshot of the page is activated. Note: the toolbar itself should be hidden at the time of taking the snapshot. The toolbar option Take Snapshot is linked up to the function window.snapshotFunction:
<li style=”float:right”><a class=”active” onclick=”window.snapshotFunction(‘FloatingDemo’)”>Take Snapshot</a></li>
This function is injected into the window object through the following call:
await context.exposeBinding(‘snapshotFunction’, snapshotter)
The function is a proxy (in the browser context) for the function snapshotter (in the Node context). This function is defined as follows:
The first argument passed to snapshotter is courtesy of Playwright: any call to a function exposed through exposeBinding will have this source parameter added by Playwright; it contains the relevant context details in which the call takes place; very importantly, it contains the page object that is the linking pin to the DOM and JavaScript context.
The function gets a handle on the toolbar (through the page.$eval) and hides the div (using style,.display = ‘none’. Next, the name of the snapshot file is derived – including the label parameter and the timestamp – and the snapshot is taken and saved – by Playwright’s function screenshot on the page object. Subsequently, the toolbar is made visible again.
At this point, the Take Snapshot functi0n in the toolbar is active. When a page is loaded in the embedded Playwright browser, a snapshot is taken by clicking the toolbar option.
In this example, I have loaded the Playwright home page at https://playwright.dev/. I have taken a snapshot, navigate to the documentation section and scrolled the page and taken a second snapshot. The bottom of the figure shows VS Code with the two snapshot files open. Note that the toolbar is not part of the snapshot.
4. Show and hide the toolbar using a short cut key combination
The toolbar can be in the way at times, so it would be good to be able to hide it. And show it again. We can do so using a short cut key. In this article I have described how to inject a short cut key through Playwright to any page. Following the instructions from this article, it is relatively easy to set up the shortcut key CTRL+B for toggling the toolbar between hidden and visible.
Two browser side JavaScript functions are defined in the next code snippet: handleShortCutKey() is a function that will inspect the key that has just been released; if that key requires a special action, this function will trigger a special function for handling that key. Currently the function only checks for CTRL + b; upon that key combination, it invokes the second browser side function: toggleToolbar(). This function locates the DOM element with the toolbar identifier (read passed into this function’s definition from the constant TOOLBAR_ID). Depending on the current value of style.display, the property is switched from none to block or vice versa. This shows or hides the toolbar div and with it the entire toolbar.
Finally, the call to document.addEventListener() configures function handleShortCutKey() as an handler for keyUp events.
The two function definitions and this final call to document.addEventListener() are all defined in const shortCutJS. This const is added to the content passed in addInitScript and will therefore be executed in each page and frame. Whenever we run a page, the toolbar is added and we can hide it with CTRL+b and bring it back with that same combination.
5. Add a toolbar option for loading all images in a page
This step is not strictly required – but it is fun and given all the pieces we already have it is a puzzle that is easy to solve. Also read my previous article on adding a short cut key to download all images: https://technology.amis.nl/2020/12/24/use-playwright-to-inject-shortcut-keys-into-any-web-page-for-example-to-download-all-images/
First, add an option in the toolbar to Save All Images:
Then define the function allImageDownloader with all it requires (including function streamImageFromURL, const IMAGE_PATH and the require statements for request and fs.
Finally, expose this function allImageDownloader in the window object to make it available to the onclick action on the toolbar option:
So: toolbar has link with onclick action; this invokes window.saveAllImagesFunction in the browser context. This call is forwarded to the Node function allImageDownloader. This function locates all images in the current page and retrieves their content from the image src url and saves it to the local images directory.
The toolbar has the extra option to save images. I have loaded a Dutch news site.
Before pressing the Save Images, this is what the image folder looked like in VS Code:
And this is what it looks like after pressing the toolbar option:
Resources
All code for this article is available on GitHub: https://github.com/lucasjellema/playwright-scenarios/tree/main/floating_menu.
My previous article: Use Playwright to Inject Shortcut Keys into Any Web Page – for example to download all images – https://technology.amis.nl/2020/12/24/use-playwright-to-inject-shortcut-keys-into-any-web-page-for-example-to-download-all-images/
I have introduced Playwright in an earlier article
W3Schools on Horizontal Navigation Bars – https://www.w3schools.com/howto/howto_js_navbar_slide.asp and others
Article Set CSS Styles with JavaScript – https://dev.to/karataev/set-css-styles-with-javascript-3nl5
PlayWright documentation on AddInitScript: https://microsoft.github.io/playwright/docs/1.6.1/api/class-browsercontext#browsercontextaddinitscriptscript-arg