2024.2 Migration Guide

PSPDFKit for Web 2024.2 introduces some potentially breaking changes. To determine if you need to take action, check your implementation and refer to the information below.

Notable Changes

New API Interface for Clipboard Annotation Events

The interface for the annotations.paste and annotations.duplicate events has changed. Instead of multiple events emitted for each annotation in the copy group, one event is emitted with an array of all the annotations that were duplicated or pasted:

- type AnnotationsPasteEvent = {
-   annotation: AnnotationsUnion;
-   formField?: FormField;
-   previousAction: 'COPY' | 'CUT';
-   originalAnnotation: AnnotationsUnion;
-   originalFormField?: FormField;
+ type AnnotationsDuplicateEvent = {
+   annotations: AnnotationsUnion[];
+   formFields?: FormField[];
+   originalAnnotations: AnnotationsUnion[];
+   originalFormFields?: Map<string, FormField>;
- type AnnotationsDuplicateEvent = {
-   annotation: AnnotationsUnion;
-   formField?: FormField;
-   originalAnnotation: AnnotationsUnion;
-   originalFormField?: FormField;
+ type AnnotationsPasteEvent = AnnotationsDuplicateEvent & {
+   previousAction: 'COPY' | 'CUT';

Viewer CSS Reset

In preparation for the upcoming user interface (UI) redesign, we added the following CSS reset to the SDK:

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  -moz-osx-font-smoothing: grayscale;
  -webkit-font-smoothing: antialiased;

If you have custom styles that rely on the default box sizing or margins/paddings, you may need to update them to account for this change.

Annotation Keyboard Navigation Order

The viewer now honors the annotation keyboard navigation order specified in a document. Due to this change, the order in which annotations are accessed with the keyboard may change. If you want to restore the previous tab order or a different one, you can do so using the new instance#setPageTabOrder API method:

// This example will reverse the keyboard navigation order of the annotations in page 0.
instance.setPageTabOrder(0, (annotations) =>
  annotations.map((annotation) => annotation.id).reverse()

Using Shadow DOM instead of iframe

With the move to using the shadow DOM by default for embedding PSPDFKit, it may be necessary to update your code to work with the new implementation. The following sections outline the changes you may need to make.

Handling Event Targets in the Shadow DOM

A notable change is in how event targets are determined during event handling. This adjustment is crucial for accurately identifying elements within the PSPDFKit viewer, especially when dealing with events that bubble through the shadow DOM.

Previously, when PSPDFKit was encapsulated within an iframe, event handling directly accessed event.target to identify the element interacting with the user. This straightforward approach worked well within the isolated document environment provided by the iframe.

In the shadow DOM setup, events are retargeted as they bubble up to the main document, which can alter the event.target to reflect the shadow DOM’s host element, rather than the actual target element within the shadow DOM. To accurately identify the original target element where the event was dispatched, developers must now use event.composedPath(). This method returns an array of the nodes through which the event will pass. The first element of this array, composedPath()[0], is the original target of the event, allowing for precise interaction handling within the PSPDFKit viewer:

- const target = event.target;
+ const target = event.composedPath()[0];

Drag Events with Shadow DOM

Using the shadow DOM integration, handling drag events within PSPDFKit for Web has been updated to accommodate the encapsulation provided by the shadow DOM. Previously, handling drag events used to be done by attaching event handlers directly to the contentDocument of the PSPDFKit instance, which represented the document object of the viewer frame when using an iframe. With the shift to the shadow DOM, instance.contentDocument can now also return a ShadowRoot object, depending on whether PSPDFKit is running in shadow DOM mode.

This change requires a different strategy for attaching event listeners. Instead of binding event listeners directly to the contentDocument, developers must now retrieve the host element of the shadow root to attach event listeners. This host element serves as the root container for PSPDFKit in shadow DOM mode, ensuring that events are captured and handled correctly within the encapsulated viewer environment:

- instance.contentDocument.ondragover = function (event) {
+ // Get shadow root for `instance.contentDocument`.
+ const container = instance.contentDocument.host;
+ container.ondragover = function (event) {
  // Your logic here.

End-to-End (E2E) Testing

To ensure seamless integration and maintain the effectiveness of tests, there will be a need to adapt E2E testing strategies in the following areas:

  1. Element Access — PSPDFKit now runs within a shadow DOM, and direct access methods used in iframe-based tests should be replaced with selectors that are compatible with the shadow DOM. This involves using instance.contentDocument.querySelector — as instance.contentDocument now returns the shadow root in case of the shadow DOM — or equivalent methods in testing frameworks that pierce the shadow DOM boundary.

  2. Event Simulation — Adjustments in simulating user interactions, such as clicks or drags, are necessary because of the shadow DOM’s event handling model. Particularly, the use of composedPath() may be required to accurately target events within the shadow DOM.

  3. Assertions — Tests that perform assertions on the DOM structure or CSS styles may need to be updated to reflect the new changes in the shadow DOM.

Querying Coordinates in the Shadow DOM

When querying coordinates within the PSPDFKit viewer, it’s important to consider the encapsulation provided by the shadow DOM. The coordinates returned are now relative to the parent document, and they’re no longer relative to the previously used iframe. This means that to get the correct coordinates, you need to adjust the coordinates to be relative to the shadow DOM’s host element:

- const rect = instance.contentDocument.querySelector(”#myElement”).getBoundingClientRect()
+ const rect = instance.contentDocument.querySelector(”#myElement”).getBoundingClientRect()
+ const shadowDOMRect = instance.contentDocument.getBoundingClientRect()
+ const relativeRect = {
+  x: rect.x - shadowDOMRect.x,
+  y: rect.y - shadowDOMRect.y,
+  left: rect.left - shadowDOMRect.left,
+  top: rect.top - shadowDOMRect.top,
+  width: rect.width,
+  height: rect.height
+ }

Custom Stylesheet Adjustments

  • Positioning and Layout — Avoid using position: fixed within custom styles targeting elements inside the PSPDFKit UI, as positioning becomes relative to the parent document. Use other positioning strategies that respect the relative positioning context of the shadow DOM container.

  • Viewport-Relative Sizes — The use of viewport-relative units (e.g. vw, vh) is discouraged. These units will reference the full browser window rather than the size of the PSPDFKit UI container, potentially leading to unintended styling outcomes.

  • CSS Variable Scoping — Shift CSS variables from being scoped to :root to :host within the shadow DOM to ensure they’re applied correctly.

  • Font Declarations — Move any @font-face declarations to the parent document’s stylesheets. This ensures custom fonts are loaded and rendered as expected within the PSPDFKit UI.

For a full list of changes, check out the changelog.