Skip to main content

The Browser object

The Browser object can be used to control browser. Here are some use-cases:

  • Showing modals, alerts, popups.
  • Retrieving browser info, current URL.
  • Accessing local storage, cookies.
  • Navigation, routing.

Showing alert inside browser#

First, you'll need to modify initializeDraymanFramework function inside public/index.html. Add browser commands function and alert function as shown below:

public/index.html
...
<script>  initializeDraymanFramework({    browserCommands: () => ({      alert: async ({ text }) => {        alert(text);      },    }),  });</script>
...

Functions defined inside browserCommands will be executed by your browser. In our example we have added alert function which shows an alert inside browser when Drayman component asks to do so. And here is how Drayman component can do this:

src/components/home.tsx
export const component: DraymanComponent = async ({ Browser }) => {  return () => {    return (      <button        onclick={async () => {          await Browser.alert({ text: "Hello, world!" });        }}      >        Alert!      </button>    );  };};

We have exposed Browser object and told Drayman to execute alert function when user clicks a button.

Retrieving data from browser#

Functions defined inside browserCommands also can return some useful data:

public/index.html
...
<script>  initializeDraymanFramework({    browserCommands: () => ({      getCurrentUrl: async () => {        return window.location.href;      },    }),  });</script>
...

Now this function can be used inside Drayman component:

src/components/home.tsx
export const component: DraymanComponent = async ({ Browser }) => {  const currentUrl = await Browser.getCurrentUrl();
  return () => {    return <h3>You are now here: {currentUrl}</h3>;  };};

In result user will see something like this:

http://localhost:3033

You are now here: http://localhost:3033/

Executing callbacks#

You can also execute callbacks inside browserCommands. This can be useful if you are creating some interactive views (modals) and you need to wait for some user interaction.

Let's modify our previous alert example:

public/index.html
...
<script>  initializeDraymanFramework({    browserCommands: (emit) => ({      alert: async ({ text, onClose }) => {        alert(text);        emit(onClose, { text: "You have closed an alert!" });      },    }),  });</script>
...

Pay attention on how emit parameter of browserCommands was introduced. It is used to tell Drayman to execute passed callbacks. In our case we are executing onClose callback after alert is executed. It also passes text parameter. Now modify home.tsx component:

src/components/home.tsx
export const component: DraymanComponent = async ({ Browser, forceUpdate }) => {  let closed = false;  let textFromBrowser = "";
  return () => {    return (      <>        <button          onclick={async () => {            await Browser.alert({              text: "Hello, world!",              onClose: async ({ text }) => {                closed = true;                textFromBrowser = text;                await forceUpdate();              },            });          }}        >          Alert!        </button>        {closed && <h3>{textFromBrowser}</h3>}      </>    );  };};

Now when user clicks a button, an alert will be shown. After closing an alert, a text from passed text parameter "You have closed an alert!" will be shown on page:

http://localhost:3033

You have closed an alert!


tip

You can pass any JSON-serializable data to callbacks, for example, document.cookie or some items from localStorage.

Callback configuration options#

debounce#

Delays invoking callback until debounce milliseconds have elapsed since the last time the debounced callback was invoked.

debounce accepts number (ms to delay).

This option is useful, for example, when viewport of the browser was resized and you need to execute callback only after user has stopped resizing it:

public/index.html
...
<script>  initializeDraymanFramework({    browserCommands: (emit) => ({      events: async ({ onViewportChanged }) => {        window.onresize = () => {          emit(            onViewportChanged,            {              width: window.innerWidth,              height: window.innerHeight,            },            {              debounce: 500,            }          );        };      },    }),  });</script>
...
src/components/home.tsx
export const component: DraymanComponent<any> = async ({  Browser,  forceUpdate,}) => {  let dimensions = ``;
  Browser.events({    onViewportChanged: async (data) => {      dimensions = `${data.width}x${data.height}`;      await forceUpdate();    },  });
  return () => {    return <div>{dimensions}</div>;  };};

In the example above, onViewportChanged callback will be executed and text of window dimensions will be updated only after user has stopped resizing browser window for 500ms.

Referencing elements#

Sometimes UI interactivity like focusing elements, opening modals and drag-and-dropping is easier to implement on front-end side. In Drayman this can be done in three steps:

  1. Mark elements that need to be referenced with ref attribute and any unique name.
  2. Define some browser command which will do something with referenced element. Element references will be passed as the second parameter of the browser command.
  3. Execute browser command function by passing an array of required element references.

Let's, for example, see how this can be done if we want to focus some element:

src/components/home.tsx
export const component: DraymanComponent = async ({ Browser }) => {  return () => {    return (      <>        <input          type="text"          // set up reference          ref="my-input"        />        <button          id="focus-btn"          onclick={async () => {            // execute Browser function with referenced element            await Browser.focus(null, ["my-input"]);          }}        >          Focus input        </button>      </>    );  };};
public/index.html
...
<script>  initializeDraymanFramework({    browserCommands: () => ({      // your elements will appear as the second parameter of Browser function      focus: async (data, [element]) => {        element.focus();      },    }),  });</script>
...

Element referencing options#

When interacting with elements in a browser environment, especially within dynamic content, you can enhance control and flexibility by passing an object with specific options (wait, ref, customSelector) instead of a simple string reference. This method allows for detailed configuration of how and when elements are targeted.

wait option#

Use the wait option to delay actions until the targeted element is available. This is crucial for content that loads dynamically, ensuring actions are performed only when the element exists.

Example: Waiting for an element before setting its text

src/components/home.tsx
export const component: DraymanComponent = async ({  Browser,  forceUpdate,  props,}) => {  let renderTextElement = false;
  setTimeout(async () => {    renderTextElement = true;    await forceUpdate();  }, 2000);
  return () => {    return (      <>        <div id="error" ref="error"></div>        {!!renderTextElement && <div id="text" ref="text"></div>}        <button          id="btn"          onclick={async () => {            await Browser.setText(              {                text: "Hello World!",              },              [{ ref: "text", wait: true }, "error"]            );          }}        >          Set text        </button>      </>    );  };};
public/index.html
...
<script>  initializeDraymanFramework({    browserCommands: () => ({      setText: async ({ text }, [element, errorElement]) => {        try {          element.innerText = text;        } catch (err) {          errorElement.innerText = err.message;        }      },    }),  });</script>
...

ref option#

The ref option targets elements by their reference name within the component. It functions similarly to when you pass a reference name as a string.

customSelector option#

The customSelector option enables targeting using CSS selectors, offering flexibility for complex scenarios or when dealing with dynamically generated identifiers.

Example: Targeting an element with a custom CSS selector

src/components/home.tsx
export const component: DraymanComponent = async ({ Browser }) => {  return () => {    return (      <>        <div id="text-id"></div>        <button          id="btn"          onclick={async () => {            await Browser.setText(              {                text: "Hello World!",              },              [{ customSelector: "#text-id" }]            );          }}        >          Set text        </button>      </>    );  };};