Add bookmarks to quickly return to specific pages within your PDF document.
This demo allows you to:
Implementation steps
To add Personal Bookmark capability with WebViewer:
Step 1: Choose your preferred web stack
Step 2: Download any required modules listed in the Demo Dependencies section below
Step 3: Add the ES6 JavaScript sample code provided in this guide
Demo Dependencies
This sample uses the following:
1// ES6 Compliant Syntax
2// GitHub Copilot v1, Claude Sonnet 4 (Preview), 2025-09-17
3// File: showcase-demos/add-personal-bookmark-pdf/index.js
4
5
6import WebViewer from '@pdftron/webviewer';
7
8// Function to initialize and load the WebViewer with user bookmarks feature
9function addUserBookmarks() {
10 const element = document.getElementById('viewer');
11 if (!element) {
12 console.error('Viewer div not found.');
13 return;
14 }
15
16 WebViewer({
17 path: '/lib',
18 initialDoc: 'https://apryse.s3.us-west-1.amazonaws.com/public/files/samples/Report_2011.pdf',
19 licenseKey: 'YOUR_LICENSE_KEY',
20 enableFilePicker: true,
21 }, element).then(instance => {
22
23 instance.UI.enableFeatures([instance.UI.Feature.Initials]);
24
25 // Reset state on document change
26 instance.Core.documentViewer.addEventListener('documentLoaded', () => {
27 instance.UI.openElements(['tabPanel']);
28 instance.UI.setActiveTabInPanel({ tabPanel: 'tabPanel', tabName: 'bookmarksPanel' });
29 instance.UI.enableBookmarkIconShortcutVisibility();
30
31 setBookmarks({});
32 setBookmarkName('My Bookmark');
33 setPageNumber(1);
34 createBookmarkControls();
35 });
36
37 instance.UI.addEventListener('userBookmarksChanged', (e) => {
38 console.log('User bookmarks updated:', e.detail);
39
40 });
41 });
42}
43// Bookmarks state
44let bookmarks = {};
45
46//Usage: 'setBookmarks({ ...bookmarks, [pageNumber]: bookmarkName });'
47function setBookmarks(newBookmarks) {
48 bookmarks = newBookmarks;
49 // Optionally, update the UI or perform other actions here
50}
51
52// Page number state
53let pageNumber = 1;
54function setPageNumber(val) {
55 pageNumber = val;
56}
57
58// Bookmark name state
59let bookmarkName = 'My Bookmark';
60function setBookmarkName(val) {
61 bookmarkName = val;
62}
63
64// Error state
65let error = '';
66function setError(val) {
67 error = val;
68 // if empty, or null, hide error div
69 if (val && val !== '' && val.length !== 0) {
70 console.error('Bookmark Error:', val);
71
72 } else {
73 console.log('Clearing bookmark error');
74 }
75
76 const errorDiv = document.querySelector('#bookmark-error-div');
77 if (errorDiv) {
78 errorDiv.textContent = val;
79 errorDiv.style.display = 'block';
80 }
81}
82
83// Button enabled state
84let isButtonEnabled = true;
85function setIsButtonEnabled(enabled) {
86 isButtonEnabled = enabled;
87 const button = document.querySelector('.bookmark-add-btn');
88 if (button) {
89 button.disabled = !enabled;
90 }
91}
92
93// Function to create a new bookmark
94function createNewBookmark() {
95 const doc = WebViewer.getInstance().Core.documentViewer.getDocument();
96 const bookmarksObject = bookmarks;
97 const bookmarkName = document.querySelector('.bookmark-title-input').value || 'My Bookmark';
98 setPageNumber(document.querySelector('.bookmark-page-input').value || 1);
99
100 console.log('Creating new bookmark at page', pageNumber, 'with name', bookmarkName);
101
102 // validate bookmarksObject exists
103 if (!bookmarksObject) {
104 console.log('Bookmarks feature is not available.');
105 return;
106 }
107
108 // validate doc exists
109 if (!doc) {
110 console.log('Document is not loaded.');
111 return;
112 }
113
114 // validate pageNumber is valid
115 if (isNaN(pageNumber) || pageNumber < 1 || pageNumber > doc.getPageCount()) {
116 setError('Please enter a valid page number.');
117 } else {
118 bookmarks[pageNumber - 1] = bookmarkName;
119 WebViewer.getInstance().UI.importBookmarks(bookmarksObject);
120 setBookmarks({ ...bookmarksObject, [pageNumber - 1]: bookmarkName });
121 setIsButtonEnabled(true);
122 setError('');
123 }
124}
125
126// Function to created bookmark controls
127function createBookmarkControls() {
128 console.log('Loading bookmark controls');
129
130//Checking if controls already exist
131
132 const existingControls = document.getElementById('controls-container');
133 if (existingControls) {
134 resetBookmarkControls();
135 console.log('Bookmark controls already exist');
136 return;
137 }
138
139 // Create a container for all controls (label, dropdown, and buttons)
140 const controlsContainer = document.createElement('div');
141 controlsContainer.className = 'control-container';
142 controlsContainer.id = 'controls-container';
143 const element = document.getElementById('viewer');
144 if (!element) {
145 console.error('Viewer div not found.');
146 return;
147 }
148 element.insertBefore(controlsContainer, element.firstChild);
149
150 console.log('controls.js loaded, createBookmarkControls:', window.createBookmarkControls);
151 // Dynamically load controls.js if not already loaded
152 if (!window.createBookmarkControls) {
153 const script = document.createElement('script');
154 script.src = '/showcase-demos/add-personal-bookmark-pdf/controls.js';
155 script.onload = function () {
156 console.log('Loaded controls', createControls);
157 console.log('createNewBookmarks:', createNewBookmark);
158 console.log('resetBookmarkControls:', resetBookmarkControls);
159
160 createControls('controls-container', createNewBookmark, resetBookmarkControls);
161
162 };
163 document.head.appendChild(script);
164 }
165}
166
167function resetBookmarkControls() {
168
169 setBookmarks({});//Clear bookmarks state
170 setPageNumber(1);
171 setBookmarkName('My Bookmark');
172 setError('');
173 setIsButtonEnabled(true);
174
175 console.log('Bookmark controls reset');
176}
177
178// Initialize the WebViewer and add user bookmarks feature
179addUserBookmarks();
180
181
182
1// ES6 Compliant Syntax
2// GitHub Copilot v1, Claude Sonnet 4 (Preview), 2025-09-17
3// File: showcase-demos/add-personal-bookmark-pdf/controls.js
4
5function createControls(targetDivId, createNewBookmark, resetBookmarkControls) {
6 const container = document.getElementById(targetDivId);
7 if (!container) {
8 console.error('Target div not found:', targetDivId);
9 return;
10 }
11 container.classList.add('bookmark-controls-container');
12
13 // 1. Bolded element with text "Bookmark Details"
14 const detailsP = document.createElement('p');
15 detailsP.textContent = 'Bookmark Details';
16 detailsP.style.fontWeight = 'bold';
17 container.appendChild(detailsP);
18
19 // 2. <p> with text "Bookmark Title:"
20 const titleLabel = document.createElement('p');
21 titleLabel.textContent = 'Bookmark Title:';
22 container.appendChild(titleLabel);
23
24 // 3. Textbox for bookmark title
25 const titleInput = document.createElement('input');
26 titleInput.type = 'text';
27 titleInput.value = 'My Bookmark';
28 titleInput.className = 'bookmark-title-input';
29 container.appendChild(titleInput);
30
31 // 4. <p> with text "Destination Page:"
32 const pageLabel = document.createElement('p');
33 pageLabel.textContent = 'Destination Page:';
34 container.appendChild(pageLabel);
35
36 // 5. Textbox for destination page
37 const pageInput = document.createElement('input');
38 pageInput.type = 'text';
39 pageInput.value = '1';
40 pageInput.className = 'bookmark-page-input';
41 container.appendChild(pageInput);
42
43 // 6. Button to add new bookmark
44 const addButton = document.createElement('button');
45 addButton.textContent = 'Add New Booknark';
46 addButton.className = 'bookmark-add-btn';
47 addButton.onclick = function () {
48 createNewBookmark();
49 };
50 container.appendChild(addButton);
51
52 //Add error div
53 const errorDiv = document.createElement('div');
54 errorDiv.style.color = 'red';
55 errorDiv.style.display = 'none';
56 errorDiv.id = 'bookmark-error-div';
57 container.appendChild(errorDiv);
58}
59
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