Add bookmarks to quickly return to specific pages within your PDF document.
This demo allows you to:
Implementation steps
To add user bookmark capability with WebViewer:
Step 1: Get started with your preferred web stack for WebViewer
Step 2: Add the ES6 JavaScript sample code provided in this guide
Once you generate your license key, it will automatically be included in your sample code below.
Apryse collects some data regarding your usage of the SDK for product improvement.
The data that Apryse collects include:
For clarity, no other data is collected by the SDK and Apryse has no access to the contents of your documents.
If you wish to continue without data collection, contact us and we will email you a no-tracking trial key for you to get started.
1
2
3import WebViewer from '@pdftron/webviewer';
4
5const licenseKey = 'YOUR_WEBVIEWER_LICENSE_KEY';
6
7// Function to initialize and load the WebViewer with user bookmarks feature
8function addUserBookmarks() {
9 const element = document.getElementById('viewer');
10 if (!element) {
11 console.error('Viewer div not found.');
12 return;
13 }
14
15 WebViewer({
16 path: '/lib',
17 initialDoc: 'https://apryse.s3.us-west-1.amazonaws.com/public/files/samples/Report_2011.pdf',
18 licenseKey: licenseKey,
19 enableFilePicker: true,
20 }, element).then(instance => {
21
22 instance.UI.enableFeatures([instance.UI.Feature.Initials]);
23
24 // Reset state on document change
25 instance.Core.documentViewer.addEventListener('documentLoaded', () => {
26 instance.UI.openElements(['tabPanel']);
27 instance.UI.setActiveTabInPanel({ tabPanel: 'tabPanel', tabName: 'bookmarksPanel' });
28 instance.UI.enableBookmarkIconShortcutVisibility();
29
30 setBookmarks({});
31 setBookmarkName('My Bookmark');
32 setPageNumber(1);
33 createBookmarkControls();
34 });
35
36 instance.UI.addEventListener('userBookmarksChanged', (e) => {
37 console.log('User bookmarks updated:', e.detail);
38
39 });
40 });
41}
42// Bookmarks state
43let bookmarks = {};
44
45//Usage: 'setBookmarks({ ...bookmarks, [pageNumber]: bookmarkName });'
46function setBookmarks(newBookmarks) {
47 bookmarks = newBookmarks;
48 // Optionally, update the UI or perform other actions here
49}
50
51// Page number state
52let pageNumber = 1;
53function setPageNumber(val) {
54 pageNumber = val;
55}
56
57// Bookmark name state
58let bookmarkName = 'My Bookmark';
59function setBookmarkName(val) {
60 bookmarkName = val;
61}
62
63// Error state
64let error = '';
65function setError(val) {
66 error = val;
67 // if empty, or null, hide error div
68 if (val && val !== '' && val.length !== 0) {
69 console.error('Bookmark Error:', val);
70
71 } else {
72 console.log('Clearing bookmark error');
73 }
74
75 const errorDiv = document.querySelector('#bookmark-error-div');
76 if (errorDiv) {
77 errorDiv.textContent = val;
78 errorDiv.style.display = 'block';
79 }
80}
81
82// Button enabled state
83let isButtonEnabled = true;
84function setIsButtonEnabled(enabled) {
85 isButtonEnabled = enabled;
86 const button = document.querySelector('.bookmark-add-btn');
87 if (button) {
88 button.disabled = !enabled;
89 }
90}
91
92// Function to create a new bookmark
93function createNewBookmark() {
94 const viewerElement = document.getElementById('viewer');
95 if (!viewerElement) {
96 console.error('Viewer div not found.');
97 return;
98 }
99
100 const instance = WebViewer.getInstance(viewerElement);
101 const doc = instance.Core.documentViewer.getDocument();
102 const bookmarksObject = bookmarks;
103 const bookmarkName = document.querySelector('.bookmark-title-input').value || 'My Bookmark';
104 setPageNumber(document.querySelector('.bookmark-page-input').value || 1);
105
106 console.log('Creating new bookmark at page', pageNumber, 'with name', bookmarkName);
107
108 // validate bookmarksObject exists
109 if (!bookmarksObject) {
110 console.log('Bookmarks feature is not available.');
111 return;
112 }
113
114 // validate doc exists
115 if (!doc) {
116 console.log('Document is not loaded.');
117 return;
118 }
119
120 // validate pageNumber is valid
121 if (isNaN(pageNumber) || pageNumber < 1 || pageNumber > doc.getPageCount()) {
122 setError('Please enter a valid page number.');
123 } else {
124 bookmarks[pageNumber - 1] = bookmarkName;
125 instance.UI.importBookmarks(bookmarksObject);
126 setBookmarks({ ...bookmarksObject, [pageNumber - 1]: bookmarkName });
127 setIsButtonEnabled(true);
128 setError('');
129 }
130}
131
132// Function to created bookmark controls
133function createBookmarkControls() {
134 console.log('Loading bookmark controls');
135
136//Checking if controls already exist
137
138 const existingControls = document.getElementById('controls-container');
139 if (existingControls) {
140 resetBookmarkControls();
141 console.log('Bookmark controls already exist');
142 return;
143 }
144
145 // Create a container for all controls (label, dropdown, and buttons)
146 const controlsContainer = document.createElement('div');
147 controlsContainer.className = 'control-container';
148 controlsContainer.id = 'controls-container';
149 const element = document.getElementById('viewer');
150 if (!element) {
151 console.error('Viewer div not found.');
152 return;
153 }
154 element.insertBefore(controlsContainer, element.firstChild);
155
156 console.log('controls.js loaded, createBookmarkControls:', globalThis.createBookmarkControls);
157 // Dynamically load controls.js if not already loaded
158 if (!globalThis.createBookmarkControls) {
159 const script = document.createElement('script');
160 script.src = '/showcase-demos/add-personal-bookmark-pdf/controls.js';
161 script.onload = function () {
162 console.log('Loaded controls', createControls);
163 console.log('createNewBookmarks:', createNewBookmark);
164 console.log('resetBookmarkControls:', resetBookmarkControls);
165
166 createControls('controls-container', createNewBookmark, resetBookmarkControls);
167
168 };
169 document.head.appendChild(script);
170 }
171}
172
173function resetBookmarkControls() {
174
175 setBookmarks({});//Clear bookmarks state
176 setPageNumber(1);
177 setBookmarkName('My Bookmark');
178 setError('');
179 setIsButtonEnabled(true);
180
181 console.log('Bookmark controls reset');
182}
183
184// Initialize the WebViewer and add user bookmarks feature
185addUserBookmarks();
186
187
188
189
1
2function createControls(targetDivId, createNewBookmark, resetBookmarkControls) {
3 const container = document.getElementById(targetDivId);
4 if (!container) {
5 console.error('Target div not found:', targetDivId);
6 return;
7 }
8 container.classList.add('bookmark-controls-container');
9
10 // 1. Bolded element with text "Bookmark Details"
11 const detailsP = document.createElement('p');
12 detailsP.textContent = 'Bookmark Details';
13 detailsP.style.fontWeight = 'bold';
14 container.appendChild(detailsP);
15
16 // 2. <p> with text "Bookmark Title:"
17 const titleLabel = document.createElement('p');
18 titleLabel.textContent = 'Bookmark Title:';
19 container.appendChild(titleLabel);
20
21 // 3. Textbox for bookmark title
22 const titleInput = document.createElement('input');
23 titleInput.type = 'text';
24 titleInput.value = 'My Bookmark';
25 titleInput.className = 'bookmark-title-input';
26 container.appendChild(titleInput);
27
28 // 4. <p> with text "Destination Page:"
29 const pageLabel = document.createElement('p');
30 pageLabel.textContent = 'Destination Page:';
31 container.appendChild(pageLabel);
32
33 // 5. Textbox for destination page
34 const pageInput = document.createElement('input');
35 pageInput.type = 'text';
36 pageInput.value = '1';
37 pageInput.className = 'bookmark-page-input';
38 container.appendChild(pageInput);
39
40 // 6. Button to add new bookmark
41 const addButton = document.createElement('button');
42 addButton.textContent = 'Add New Booknark';
43 addButton.className = 'bookmark-add-btn';
44 addButton.onclick = function () {
45 createNewBookmark();
46 };
47 container.appendChild(addButton);
48
49 //Add error div
50 const errorDiv = document.createElement('div');
51 errorDiv.style.color = 'red';
52 errorDiv.style.display = 'none';
53 errorDiv.id = 'bookmark-error-div';
54 container.appendChild(errorDiv);
55}
56
1/* Layout for viewer and its children */
2#viewer {
3 display: flex;
4 margin: 0 0 0 0;
5 width: 100%;
6}
7
8#playground-viewer{
9 height: 90% !important;
10}
11
12/* Controls Container on the left */
13.control-container {
14 width: 150px;
15 overflow-x: hidden;
16 margin: auto;
17 padding-bottom: 50px;
18 box-sizing: border-box;
19 display: flex;
20 flex-direction: column;
21 align-items: flex-start;
22 gap: 1px;
23 margin: 0;
24 padding: 10px 5px 5px 10px;
25 border-right: 1px solid #e0e0e0;
26 background-color: rgba(112, 198, 255, 0.2);
27 /* Light blue background for contrast */
28}
29
30.bottom-headers-wrapper {
31 bottom: -11px !important;
32}
33
34#editorWrapper {
35 width: 100% !important;
36 height: 85% !important;
37 padding-bottom: 50px !important;
38 margin-bottom: 250px !important;
39}
40
41/* WebViewer container on the right */
42#webviewer-1 {
43 margin: 0;
44 padding-bottom: 10px !important;
45
46
47}
48
49/* Responsive Design */
50@media (max-width: 768px) {
51 #viewer {
52 flex-direction: column;
53 }
54
55 .control-container {
56 width: 150px;
57 height: auto;
58 border-right: none;
59 border-bottom: 1px solid #e0e0e0;
60 align-items: center;
61 }
62
63
64}
65
66
67#GenericFileTab{
68 margin-bottom: 50px !important;
69}
70
71/* Gallery Picker Styles */
72.gallery-grid {
73 display: grid;
74 grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
75 gap: 16px;
76 overflow-y: auto;
77 max-height: 100vh;
78 padding: 10px;
79}
80
81.gallery-thumb {
82 cursor: pointer;
83 border: 1px solid #ccc;
84 border-radius: 6px;
85 background: #fff;
86 box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
87 display: flex;
88 flex-direction: column;
89 align-items: center;
90 padding: 8px;
91 transition: box-shadow 0.2s;
92}
93
94.gallery-thumb:hover,
95.gallery-thumb:focus {
96 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
97 border-color: #70c6ff;
98}
99
100.gallery-thumb-img {
101 width: 100px;
102 height: 100px;
103 object-fit: cover;
104 border-radius: 4px;
105 margin-bottom: 6px;
106}
107
108.gallery-thumb-label {
109 font-size: 13px;
110 color: #333;
111 text-align: center;
112 margin-top: 2px;
113 word-break: break-word;
114}
1.bookmark-controls-container {
2 display: flex;
3 flex-direction: column;
4 gap: 10px;
5 max-width: 300px;
6 margin: 0 auto;
7}
8
9.bookmark-title-input,
10.bookmark-page-input {
11 width: 100%;
12 padding: 6px 8px;
13 font-size: 1em;
14 border: 1px solid #ccc;
15 border-radius: 4px;
16}
17
18.bookmark-add-btn {
19 padding: 8px 12px;
20 font-size: 1em;
21 background: #1976d2;
22 color: #fff;
23 border: none;
24 border-radius: 4px;
25 cursor: pointer;
26 font-weight: bold;
27 transition: background 0.2s;
28}
29
30.bookmark-add-btn:hover {
31 background: #1565c0;
32}
33
34.bookmark-controls-container p {
35 margin: 0 0 2px 0;
36}
37
Did you find this helpful?
Trial setup questions?
Ask experts on DiscordNeed other help?
Contact SupportPricing or product questions?
Contact Sales