How can I help you?
How to Customize the context menu in PDF Viewer in React
25 Feb 202624 minutes to read
The PDF Viewer supports extensive customization of the context menu, including reaching specific goals like adding new items, hiding default options, and handling custom click events.
Add Custom Context Menu Items
You can add custom options to the context menu using the addCustomMenu() method. This is typically implemented during the documentLoad event.
Implementation Guide
- Define the menu items as an array of objects.
- Call the
addCustomMenumethod within thedocumentLoadevent handler.
export function App() {
let menuItems = [
{
text: 'Search In Google',
id: 'search_in_google',
iconCss: 'e-icons e-search'
},
{
text: 'Lock Annotation',
iconCss: 'e-icons e-lock',
id: 'lock_annotation'
},
{
text: 'Unlock Annotation',
iconCss: 'e-icons e-unlock',
id: 'unlock_annotation'
},
{
text: 'Lock Form Field',
iconCss: 'e-icons e-lock',
id: 'read_only_true'
},
{
text: 'Unlock Form Field',
iconCss: 'e-icons e-unlock',
id: 'read_only_false'
},
];
function documentLoad(args) {
var viewer = document.getElementById('container').ej2_instances[0];
viewer.addCustomMenu(menuItems, false);
}
return (
<div>
<div className='control-section'>
{/* Render the PDF Viewer */}
<PdfViewerComponent
id="container"
documentPath="https://cdn.syncfusion.com/content/pdf/pdf-succinctly.pdf"
resourceUrl="https://cdn.syncfusion.com/ej2/31.2.2/dist/ej2-pdfviewer-lib"
documentLoad={documentLoad}
customContextMenuSelect={customContextMenuSelect}
customContextMenuBeforeOpen={customContextMenuBeforeOpen}
height='640px'>
<Inject services={[Toolbar, Magnification, Navigation, Annotation, LinkAnnotation, BookmarkView,
ThumbnailView, Print, TextSelection, TextSearch, FormFields, FormDesigner]} />
</PdfViewerComponent>
</div>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('sample'));
root.render(<App />);Handle Click Events for Custom Menu Items
The customContextMenuSelect() method defines actions for custom menu items.
function customContextMenuSelect(args) {
var viewer = document.getElementById('container').ej2_instances[0];
switch (args.id) {
case 'search_in_google':
for (var i = 0; i < viewer.textSelectionModule.selectionRangeArray.length; i++) {
var content = viewer.textSelectionModule.selectionRangeArray[i].textContent;
if ((viewer.textSelectionModule.isTextSelection) && (\/\S\/.test(content))) {
window.open('http://google.com/search?q=' + content);
}
}
break;
case 'lock_annotation':
lockAnnotations(args);
break;
case 'unlock_annotation':
unlockAnnotations(args);
break;
case 'read_only_true':
setReadOnlyTrue(args);
break;
case 'read_only_false':
setReadOnlyFalse(args);
break;
default:
break;
}
}
function lockAnnotations(args) {
for (var i = 0; i < viewer.annotationCollection.length; i++) {
if (viewer.annotationCollection[i].uniqueKey === viewer.selectedItems.annotations[0].id) {
viewer.annotationCollection[i].annotationSettings.isLock = true;
viewer.annotationCollection[i].isCommentLock = true;
viewer.annotation.editAnnotation(viewer.annotationCollection[i]);
}
args.cancel = false;
}
}
function unlockAnnotations(args) {
for (var i = 0; i < viewer.annotationCollection.length; i++) {
if (viewer.annotationCollection[i].uniqueKey === viewer.selectedItems.annotations[0].id) {
viewer.annotationCollection[i].annotationSettings.isLock = false;
viewer.annotationCollection[i].isCommentLock = false;
viewer.annotation.editAnnotation(viewer.annotationCollection[i]);
}
args.cancel = false;
}
}
function setReadOnlyTrue(args) {
var viewer = document.getElementById('container').ej2_instances[0];
var selectedFormFields = viewer.selectedItems.formFields;
for (var i = 0; i < selectedFormFields.length; i++) {
var selectedFormField = selectedFormFields[i];
if (selectedFormField) {
viewer.formDesignerModule.updateFormField(selectedFormField, {
isReadOnly: true,
});
}
args.cancel = false;
}
}
function setReadOnlyFalse(args) {
var viewer = document.getElementById('container').ej2_instances[0];
var selectedFormFields = viewer.selectedItems.formFields;
for (var i = 0; i < selectedFormFields.length; i++) {
var selectedFormField = selectedFormFields[i];
if (selectedFormField) {
viewer.formDesignerModule.updateFormField(selectedFormField, {
isReadOnly: false,
});
}
args.cancel = false;
}
}Dynamic Context Menu Customization
The customContextMenuBeforeOpen() event allows for dynamic showing or hiding of items based on selection or document state.
function customContextMenuBeforeOpen(args) {
for (var i = 0; i < args.ids.length; i++) {
var search = document.getElementById(args.ids[i]);
var viewer = document.getElementById('container').ej2_instances[0];
if (search) {
search.style.display = 'none';
if (args.ids[i] === 'search_in_google' && (viewer.textSelectionModule) && viewer.textSelectionModule.isTextSelection) {
search.style.display = 'block';
} else if (args.ids[i] === "lock_annotation" || args.ids[i] === "unlock_annotation") {
var isLockOption = args.ids[i] === "lock_annotation";
for (var j = 0; j < viewer.selectedItems.annotations.length; j++) {
var selectedAnnotation = viewer.selectedItems.annotations[j];
if (selectedAnnotation && selectedAnnotation.annotationSettings) {
var shouldDisplay = (isLockOption && !selectedAnnotation.annotationSettings.isLock) ||
(!isLockOption && selectedAnnotation.annotationSettings.isLock);
search.style.display = shouldDisplay ? 'block' : 'none';
}
}
} else if (args.ids[i] === "read_only_true" && viewer.selectedItems.formFields.length !== 0) {
var selectedFormField = viewer.selectedItems.formFields[0].isReadonly;
search.style.display = selectedFormField ? 'none' : 'block';
} else if (args.ids[i] === "read_only_false" && viewer.selectedItems.formFields.length !== 0) {
var selectedFormField = viewer.selectedItems.formFields[0].isReadonly;
search.style.display = selectedFormField ? 'block' : 'none';
} else if (args.ids[i] === 'formfield properties' && viewer.selectedItems.formFields.length !== 0) {
search.style.display = 'block';
}
}
}
}Disable the Context Menu Entirely
The context menu in the PDF Viewer can be fully disabled by setting the contextMenuOption property to None.
<PdfViewerComponent
id="pdfViewer"
documentPath="https://cdn.syncfusion.com/content/pdf/pdf-succinctly.pdf"
height="100%"
width="100%"
contextMenuOption='None' >
<Inject
services={[
Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print, TextSelection, TextSearch, Annotation, FormDesigner, FormFields
]}
/>
</PdfViewerComponent>The following is the output of the custom context menu with customization.
import * as ReactDOM from 'react-dom';
import * as React from 'react';
import './index.css';
import { PdfViewerComponent, Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print, TextSelection, Annotation, TextSearch, FormFields, FormDesigner, PageOrganizer, Inject } from '@syncfusion/ej2-react-pdfviewer';
import { CheckBoxComponent } from '@syncfusion/ej2-react-buttons';
export function App() {
var viewer;
var enableObj;
var positionObj;
let menuItems = [
{
text: 'Search In Google',
id: 'search_in_google',
iconCss: 'e-icon e-search'
},
{
text: 'Lock Annotation',
iconCss: 'e-icon e-lock',
id: 'lock_annotation'
},
{
text: 'Unlock Annotation',
iconCss: 'e-icon e-unlock',
id: 'unlock_annotation'
},
{
text: 'Lock Form Fields',
iconCss: 'e-icon e-lock',
id: 'read_only_true'
},
{
text: 'Unlock Form Fields',
iconCss: 'e-icon e-unlock',
id: 'read_only_false'
},
];
return (<div>
<tbody>
<tr>
<td>Hide Default Context Menu</td>
<td>
<CheckBoxComponent ref={(scope) => { enableObj = scope; }} id="hide-default-context-menu" change={contextmenuHelper}></CheckBoxComponent>
</td>
</tr>
<tr>
<td>Add Custom option at bottom</td>
<td>
<CheckBoxComponent ref={(scope) => { positionObj = scope; }} id="show-custom-menu-bottom" change={contextmenuHelper}></CheckBoxComponent>
</td>
</tr>
</tbody>
<div className='control-section'>
<PdfViewerComponent ref={(scope) => { viewer = scope; }} id="container" documentPath="https://cdn.syncfusion.com/content/pdf/pdf-succinctly.pdf" resourceUrl = "https://cdn.syncfusion.com/ej2/25.1.35/dist/ej2-pdfviewer-lib" documentLoad={documentLoad} customContextMenuSelect = {customContextMenuSelect} customContextMenuBeforeOpen = {customContextMenuBeforeOpen} style=height>
<Inject services={[Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print, TextSelection, TextSearch, Annotation, FormFields, FormDesigner, PageOrganizer]} />
</PdfViewerComponent>
</div>
</div>);
function documentLoad(args) {
viewer.addCustomMenu(menuItems, false, false);
}
function customContextMenuSelect(args) {
switch (args.id) {
case 'search_in_google':
for (var i = 0; i < viewer.textSelectionModule.selectionRangeArray.length; i++) {
var content = viewer.textSelectionModule.selectionRangeArray[i].textContent;
if ((viewer.textSelectionModule.isTextSelection) && (/\S/.test(content))) {
window.open('http://google.com/search?q=' + content);
}
}
break;
case 'lock_annotation':
lockAnnotations(args);
break;
case 'unlock_annotation':
unlockAnnotations(args);
break;
case 'read_only_true':
setReadOnlyTrue(args);
break;
case 'read_only_false':
setReadOnlyFalse(args);
break;
default:
break;
}
}
function customContextMenuBeforeOpen(args) {
for (var i = 0; i < args.ids.length; i++) {
var search = document.getElementById(args.ids[i]);
if (search) {
search.style.display = 'none';
if (args.ids[i] === 'search_in_google' && (viewer.textSelectionModule) && viewer.textSelectionModule.isTextSelection) {
search.style.display = 'block';
} else if (args.ids[i] === "lock_annotation" || args.ids[i] === "unlock_annotation") {
var isLockOption = args.ids[i] === "lock_annotation";
for (var j = 0; j < viewer.selectedItems.annotations.length; j++) {
var selectedAnnotation = viewer.selectedItems.annotations[j];
if (selectedAnnotation && selectedAnnotation.annotationSettings) {
var shouldDisplay = (isLockOption && !selectedAnnotation.annotationSettings.isLock) ||
(!isLockOption && selectedAnnotation.annotationSettings.isLock);
search.style.display = shouldDisplay ? 'block' : 'none';
}
}
} else if ((args.ids[i] === "read_only_true" || args.ids[i] === "read_only_false") && viewer.selectedItems.formFields.length !== 0) {
var isReadOnlyOption = args.ids[i] === "read_only_true";
for (var k = 0; k < viewer.selectedItems.formFields.length; k++) {
var selectedFormFields = viewer.selectedItems.formFields[k];
if (selectedFormFields) {
var selectedFormField = viewer.selectedItems.formFields[k].isReadonly;
var displayMenu = (isReadOnlyOption && !selectedFormField) || (!isReadOnlyOption && selectedFormField);
search.style.display = displayMenu ? 'block' : 'none';
}
}
} else if (args.ids[i] === 'formfield properties' && viewer.selectedItems.formFields.length !== 0) {
search.style.display = 'block';
}
}
}
}
function lockAnnotations(args) {
for (var i = 0; i < viewer.annotationCollection.length; i++) {
if (viewer.annotationCollection[i].uniqueKey === viewer.selectedItems.annotations[0].id) {
viewer.annotationCollection[i].annotationSettings.isLock = true;
viewer.annotationCollection[i].isCommentLock = true;
viewer.annotation.editAnnotation(viewer.annotationCollection[i]);
}
args.cancel = false;
}
}
function unlockAnnotations(args) {
for (var i = 0; i < viewer.annotationCollection.length; i++) {
if (viewer.annotationCollection[i].uniqueKey === viewer.selectedItems.annotations[0].id) {
viewer.annotationCollection[i].annotationSettings.isLock = false;
viewer.annotationCollection[i].isCommentLock = false;
viewer.annotation.editAnnotation(viewer.annotationCollection[i]);
}
args.cancel = false;
}
}
function setReadOnlyTrue(args) {
var selectedFormFields = viewer.selectedItems.formFields;
for (var i = 0; i < selectedFormFields.length; i++) {
var selectedFormField = selectedFormFields[i];
if (selectedFormField) {
viewer.formDesignerModule.updateFormField(selectedFormField, {
isReadOnly: true,
});
}
args.cancel = false;
}
}
function setReadOnlyFalse(args) {
var selectedFormFields = viewer.selectedItems.formFields;
for (var i = 0; i < selectedFormFields.length; i++) {
var selectedFormField = selectedFormFields[i];
if (selectedFormField) {
viewer.formDesignerModule.updateFormField(selectedFormField, {
isReadOnly: false,
});
}
args.cancel = false;
}
}
function contextmenuHelper(args) {
viewer.addCustomMenu(menuItems,enableObj.checked, positionObj.checked);
}
}
const root = ReactDOM.createRoot(document.getElementById('sample'));
root.render(<App />);import * as ReactDOM from 'react-dom';
import * as React from 'react';
import './index.css';
import { PdfViewerComponent, Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print, TextSelection, Annotation, TextSearch, FormFields, FormDesigner, PageOrganizer, Inject } from '@syncfusion/ej2-react-pdfviewer';
import { CheckBoxComponent } from '@syncfusion/ej2-react-buttons';
export function App() {
var viewer: any;
var enableObj: any;
var positionObj: any;
let menuItems = [
{
text: 'Search In Google',
id: 'search_in_google',
iconCss: 'e-icon e-search'
},
{
text: 'Lock Annotation',
iconCss: 'e-icon e-lock',
id: 'lock_annotation'
},
{
text: 'Unlock Annotation',
iconCss: 'e-icon e-unlock',
id: 'unlock_annotation'
},
{
text: 'Lock Form Fields',
iconCss: 'e-icon e-lock',
id: 'read_only_true'
},
{
text: 'Unlock Form Fields',
iconCss: 'e-icon e-unlock',
id: 'read_only_false'
},
];
return (<div>
<tbody>
<tr>
<td>Hide Default Context Menu</td>
<td>
<CheckBoxComponent ref={(scope) => { enableObj = scope; }} id="hide-default-context-menu" change={contextmenuHelper}></CheckBoxComponent>
</td>
</tr>
<tr>
<td>Add Custom option at bottom</td>
<td>
<CheckBoxComponent ref={(scope) => { positionObj = scope; }} id="show-custom-menu-bottom" change={contextmenuHelper}></CheckBoxComponent>
</td>
</tr>
</tbody>
<div className='control-section'>
<PdfViewerComponent
ref={(scope) => { viewer = scope; }}
id="container"
documentPath="https://cdn.syncfusion.com/content/pdf/pdf-succinctly.pdf"
resourceUrl = "https://cdn.syncfusion.com/ej2/25.1.35/dist/ej2-pdfviewer-lib"
documentLoad={documentLoad}
customContextMenuSelect = {customContextMenuSelect}
customContextMenuBeforeOpen = {customContextMenuBeforeOpen}
style=height>
<Inject services={[Toolbar, Magnification, Navigation, LinkAnnotation, BookmarkView, ThumbnailView, Print, TextSelection, TextSearch, Annotation, FormFields, FormDesigner, PageOrganizer]} />
</PdfViewerComponent>
</div>
</div>);
function documentLoad(args: any) {
viewer.addCustomMenu(menuItems, false, false);
}
function customContextMenuSelect(args: any) {
switch (args.id) {
case 'search_in_google':
for (var i = 0; i < viewer.textSelectionModule.selectionRangeArray.length; i++) {
var content = viewer.textSelectionModule.selectionRangeArray[i].textContent;
if ((viewer.textSelectionModule.isTextSelection) && (/\S/.test(content))) {
window.open('http://google.com/search?q=' + content);
}
}
break;
case 'lock_annotation':
lockAnnotations(args);
break;
case 'unlock_annotation':
unlockAnnotations(args);
break;
case 'read_only_true':
setReadOnlyTrue(args);
break;
case 'read_only_false':
setReadOnlyFalse(args);
break;
default:
break;
}
}
function customContextMenuBeforeOpen(args: any) {
for (var i = 0; i < args.ids.length; i++) {
var search = document.getElementById(args.ids[i]);
if (search) {
search.style.display = 'none';
if (args.ids[i] === 'search_in_google' && (viewer.textSelectionModule) && viewer.textSelectionModule.isTextSelection) {
search.style.display = 'block';
} else if (args.ids[i] === "lock_annotation" || args.ids[i] === "unlock_annotation") {
var isLockOption = args.ids[i] === "lock_annotation";
for (var j = 0; j < viewer.selectedItems.annotations.length; j++) {
var selectedAnnotation = viewer.selectedItems.annotations[j];
if (selectedAnnotation && selectedAnnotation.annotationSettings) {
var shouldDisplay = (isLockOption && !selectedAnnotation.annotationSettings.isLock) ||
(!isLockOption && selectedAnnotation.annotationSettings.isLock);
search.style.display = shouldDisplay ? 'block' : 'none';
}
}
} else if ((args.ids[i] === "read_only_true" || args.ids[i] === "read_only_false") && viewer.selectedItems.formFields.length !== 0) {
var isReadOnlyOption = args.ids[i] === "read_only_true";
for (var k = 0; k < viewer.selectedItems.formFields.length; k++) {
var selectedFormFields = viewer.selectedItems.formFields[k];
if (selectedFormFields) {
var selectedFormField = viewer.selectedItems.formFields[k].isReadonly;
var displayMenu = (isReadOnlyOption && !selectedFormField) || (!isReadOnlyOption && selectedFormField);
search.style.display = displayMenu ? 'block' : 'none';
}
}
} else if (args.ids[i] === 'formfield properties' && viewer.selectedItems.formFields.length !== 0) {
search.style.display = 'block';
}
}
}
}
function lockAnnotations(args: any) {
for (var i = 0; i < viewer.annotationCollection.length; i++) {
if (viewer.annotationCollection[i].uniqueKey === viewer.selectedItems.annotations[0].id) {
viewer.annotationCollection[i].annotationSettings.isLock = true;
viewer.annotationCollection[i].isCommentLock = true;
viewer.annotation.editAnnotation(viewer.annotationCollection[i]);
}
args.cancel = false;
}
}
function unlockAnnotations(args: any) {
for (var i = 0; i < viewer.annotationCollection.length; i++) {
if (viewer.annotationCollection[i].uniqueKey === viewer.selectedItems.annotations[0].id) {
viewer.annotationCollection[i].annotationSettings.isLock = false;
viewer.annotationCollection[i].isCommentLock = false;
viewer.annotation.editAnnotation(viewer.annotationCollection[i]);
}
args.cancel = false;
}
}
function setReadOnlyTrue(args: any) {
var selectedFormFields = viewer.selectedItems.formFields;
for (var i = 0; i < selectedFormFields.length; i++) {
var selectedFormField = selectedFormFields[i];
if (selectedFormField) {
viewer.formDesignerModule.updateFormField(selectedFormField, {
isReadOnly: true,
});
}
args.cancel = false;
}
}
function setReadOnlyFalse(args: any) {
var selectedFormFields = viewer.selectedItems.formFields;
for (var i = 0; i < selectedFormFields.length; i++) {
var selectedFormField = selectedFormFields[i];
if (selectedFormField) {
viewer.formDesignerModule.updateFormField(selectedFormField, {
isReadOnly: false,
});
}
args.cancel = false;
}
}
function contextmenuHelper(args: any) {
viewer.addCustomMenu(menuItems,enableObj.checked, positionObj.checked);
}
}
const root = ReactDOM.createRoot(document.getElementById('sample'));
root.render(<App />);NOTE
To set up the server-backed PDF Viewer, add the following
serviceUrlwithin the <div> element in either theindex.TSXorindex.JSXfile: serviceUrl=”https://document.syncfusion.com/web-services/pdf-viewer/api/pdfviewer”.