> For the complete documentation index, see [llms.txt](https://docs.apryse.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apryse.com/salesforce/events/wv-events.md).

# Interacting with WebViewer events

Learn about the order of events when WebViewer is instantiated and a document is loaded. Get a complete visualization and detailed description. Optimize your WebViewer implementation for seamless reso

This guide will go over the order of events that happen when WebViewer is instantiated and a document is loaded.

Here is a complete visualization of the order of events that occur. For a more detailed description, see the [sections below](https://docs.apryse.com).

![](https://306473577-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FfmJo9lQOEYOFBgOyFp26%2Fuploads%2Fgit-blob-cbc681d02bbc0ba54501c42f8e4a1c4a5ea3a5ce%2Fe306293515bee0e4e86d29a0188015c99e5751d0-781x1059.png?alt=media)

## Instantiation and Resource Loading

The first step you take when implementing WebViewer is creating a new instance of WebViewer. This is done with the `PDFTron.WebViewer` constructor, like so:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
WebViewer({
  initialDoc: 'https://myserver.com/myfile.pdf'
}, document.getElementById('viewer'));
```

{% endcode %}
{% endtab %}
{% endtabs %}

WebViewer then creates an iframe in the DOM element that you provide, and starts loading all the necessary resources in that iframe, including the UI and web workers.

When those resources are finished loading and WebViewer can be interacted with, the [WebViewer promise](https://sdk.apryse.com/api/web/global.html#WebViewer) resolves.

### WebViewer promise

Once the [WebViewer promise](https://sdk.apryse.com/api/web/global.html#WebViewer) resolves, you can begin to interact with WebViewer. This includes [UI customizations](/web/ui-customization/ui-customization.md), [loading documents](/web/open-save-document/open/blob.md), subscribing to other WebViewer events, and any other functionality you may want to use.

{% hint style="warning" %}
Keep in mind that at this point, the document (if one was provided) has **not** been loaded and cannot be interacted with yet. See [document loading](https://docs.apryse.com) for more info.
{% endhint %}

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
WebViewer({
  initialDoc: 'https://myserver.com/myfile.pdf'
}, document.getElementById('viewer'))
  .then(instance => {
    // you can disable annotations
    instance.UI.disableAnnotations();

    // or customize the UI
    instance.UI.setTheme({ primary: 'blue', secondary: 'white' });

    // etc..
  });
```

{% endcode %}
{% endtab %}
{% endtabs %}

[WebViewerInstance.UI.setTheme](https://sdk.apryse.com/api/web/UI.html#setTheme__anchor)

## Document Loading

Once all the required WebViewer resources are loaded, documents can start loading. You can load a document either by passing one to the [`initialDoc`](https://sdk.apryse.com/api/web/global.html#WebViewer__anchor) constructor option, or calling [`loadDocument`](https://sdk.apryse.com/api/web/UI.html#.loadDocument) after the WebViewer promise resolves (as seen above).

The first step of loading a document is loading all, or part, of the document into memory. Once we have enough information about the document stored in memory, the first document lifecycle event is called, [`DocumentViewer.documentLoaded`](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#event:documentLoaded__anchor).

### DocumentViewer.documentLoaded

This event is called when the document is loaded into memory and you can start interacting with the document. This includes [page manipulation](/web/get-started/guides/page-manipulation.md), [loading annotations](/web/annotation/import-export.md), [initializing collaboration](/web/collaboration/realtime-collaboration-client.md), and more!

You can bind to the event like so:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
WebViewer({
  initialDoc: 'https://myserver.com/myfile.pdf'
}, document.getElementById('viewer'))
  .then(instance => {
    const { documentViewer, annotationManager } = instance.Core;

    documentViewer.addEventListener('documentLoaded', () => {
      // here you can get the document and perform actions on it,
      // such as removing pages
      documentViewer.getDocument().removePages([2]).then(() => { })


      // or importing annotations from your server
      getAnnotationsFromServer(DOCUMENT_ID).then(async xfdfString => {
        const annotations = await annotationManager.importAnnotations(xfdfString);
        annotationManager.drawAnnotationsFromList(annotations);
      });
    })
  });
window.addEventListener('documentLoaded', async function () {

  instance.UI.setToolbarGroup('toolbarGroup-Redact');
  instance.UI.openElements(["redactionPanel"]);
  
});
```

{% endcode %}
{% endtab %}
{% endtabs %}

[documentLoaded event](https://docs.apryse.com/api/web/Core.DocumentViewer.html#event:documentLoaded__anchor) [Saving and loading annotations ](/web/annotation/import-export.md)[Page manipulation](/web/get-started/guides/page-manipulation.md)

Keep in mind that this callback gets fired for every document that gets loaded throughout the life of your app. If you want the callback to only be fired once, you can unsubscribe from the event like so:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
WebViewer(...)
  .then(instance => {
    const docViewer = instance.Core.documentViewer;

    const callback = () => {
      // unsubscribe immediatly after invoking
      docViewer.removeEventListener('documentLoaded', callback);
    }

    docViewer.addEventListener('documentLoaded', callback);

    // or

    docViewer.addEventListener('documentLoaded', () => { }, { once: true });
  })
```

{% endcode %}
{% endtab %}
{% endtabs %}

[DocumentViewer#documentLoaded](https://docs.apryse.com/api/web/Core.DocumentViewer.html#event:documentLoaded__anchor)

### Document Load Error Handling

On the other hand, if a document fails to load, a `loaderror` event will be triggered on the iframe window. Although WebViewer can recover from most loading errors, you may want to show a custom error message, submit a log to an API, and/or load a new document.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
WebViewer({
  initialDoc: 'https://myserver.com/myfile.pdf'
}, document.getElementById('viewer'))
  .then(function(instance) {
    const UIEvents = instance.UI.Events;
    instance.UI.addEventListener(UIEvents.LOAD_ERROR, function(err) {
      // Do something with error. eg. instance.showErrorMessage('An error has occurred')
    });
  });
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Document and Annotation Rendering

After the document is in memory, we are ready to start rendering to the screen. The document and its annotations are rendered in parallel, and there are two main events that are fired during this cycle.

### DocumentViewer.pageComplete

[This event](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#event:pageComplete__anchor) is fired for each page that is rendered. We only render the pages that are visible on the screen, so this event won't get fired for every page in the document at once. This event will get called when the user scrolls up and down the document, or when a page is zoomed or rotated, or anything else that makes it rerender.

{% hint style="info" %}
A few extra pages may be prerendered at lower priority, so pageComplete may be called for pages that are not currently visibie. You can set the prerender level with the [SetPreRenderLevel](https://sdk.apryse.com/api/web/Core.html#.SetPreRenderLevel__anchor) function.
{% endhint %}

You can subscribe to this event similar to how you subscribe to the `documentLoaded` event (as seen in the previous section).

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
WebViewer({
  initialDoc: 'https://myserver.com/myfile.pdf'
}, document.getElementById('viewer'))
  .then(instance => {
    const { documentViewer } = instance.Core;


    documentViewer.addEventListener('pageComplete', (pageNumber, canvas) => {
      // here it's guaranteed that page {pageNumber} is fully rendered
      // you can get or set pixels on the canvas, etc
    })
  });
```

{% endcode %}
{% endtab %}
{% endtabs %}

[pageComplete event](https://docs.apryse.com/api/web/Core.DocumentViewer.html#event:pageComplete__anchor) [Canvas API](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/)

Remember that this callback is fired for every page on every document that is loaded. You can unsubscribe using the `off` function (as seen in the previous section).

### DocumentViewer.annotationsLoaded

This event is fired when all the annotations have been loaded into memory. At this point you can start interacting with the annotations, such as [saving and loading ](/web/annotation/import-export.md).

{% hint style="info" %}
We load annotations asyncronously in the background, and they may be rendered before this event is fired.
{% endhint %}

You can subscribe to the event like so:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
WebViewer({
  initialDoc: 'https://myserver.com/myfile.pdf'
}, document.getElementById('viewer'))
  .then(instance => {
    const { documentViewer, annotationManager } = instance.Core;

    documentViewer.addEventListener('annotationsLoaded', async () => {
      // here you can start interacting with annotations,
      // like saving the original annotations to your server

      const xfdfString = await annotationManager.exportAnnotations({ widgets: false });
      saveAnnotsStringToServer(xfdfString);
    })
  });
```

{% endcode %}
{% endtab %}
{% endtabs %}

[DocumentViewer#annotationsLoaded](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#event:annotationsLoaded__anchor) [annotationsLoaded event](https://docs.apryse.com/api/web/Core.DocumentViewer.html#event:annotationsLoaded__anchor) [exportAnnotations](https://docs.apryse.com/api/web/Core.AnnotationManager.html#exportAnnotations__anchor)

## Input events

These input events are fired when the mouse and keyboard are interacting with the document viewer.

* [keyDown](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#event:keyDown__anchor)
* [keyUp](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#event:keyUp__anchor)
* [mouseEnter](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#event:mouseEnter__anchor)
* [mouseLeave](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#event:mouseLeave__anchor)
* [mouseLeftDown](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#event:mouseLeftDown__anchor)
* [mouseLeftUp](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#event:mouseLeftUp__anchor)
* [mouseMove](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#event:mouseMove__anchor)
* [mouseRightDown](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#event:mouseRightDown__anchor)
* [mouseRightUp](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#event:mouseRightUp__anchor)

Here is an example of an onHover for annotations using the mouseMove event.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
WebViewer({
  initialDoc: 'https://myserver.com/myfile.pdf'
}, document.getElementById('viewer'))
  .then(instance => {
    const { documentViewer, annotationManager } = instance.Core;

    documentViewer.addEventListener('mouseMove', evt => {
      const annot = annotationManager.getAnnotationByMouseEvent(evt);
      if (annot) {
        console.log("onHover: " + annot.Id);
      }
    });
  });
```

{% endcode %}
{% endtab %}
{% endtabs %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.apryse.com/salesforce/events/wv-events.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
