Lightweight web-based rich text editor (WYSIWYG editor) built with JavaScript and CSS for modern browsers
—
The menu system provides programmatic access to editor toolbar menus for state management and custom interactions.
Update the active state of toolbar menu items based on current selection.
/**
* Update active state of all menu items
* Called automatically when selection changes, but can be called manually
*/
changeActive(): void;Usage Examples:
// Manually update menu states
editor.menus.changeActive();
// Update menu states after programmatic changes
editor.cmd.do('bold');
editor.menus.changeActive(); // Update bold button state
// Update states in custom event handlers
editor.customConfig.onchange = function(html) {
// Update menu states after content changes
setTimeout(() => {
editor.menus.changeActive();
}, 100);
};Get access to individual menu instances for custom interactions.
interface MenusAPI {
/** Individual menu instances, keyed by menu name */
menus: {[menuName: string]: MenuInstance};
/** Update active state of all menus */
changeActive(): void;
}
interface MenuInstance {
/** Menu type: 'click', 'droplist', or 'panel' */
type: string;
/** Menu DOM element */
$elem: Element;
/** Try to change active state (if supported) */
tryChangeActive?(): void;
/** Click handler (for click and panel types) */
onClick?(event: Event): void;
/** Dropdown list (for droplist type) */
droplist?: DroplistInstance;
/** Panel instance (for panel type) */
panel?: PanelInstance;
}Usage Examples:
// Access specific menu instance
const boldMenu = editor.menus.menus.bold;
if (boldMenu) {
console.log('Bold menu type:', boldMenu.type);
console.log('Bold menu element:', boldMenu.$elem);
}
// Check if menu exists
if (editor.menus.menus.italic) {
console.log('Italic menu is available');
}
// Get all available menus
const availableMenus = Object.keys(editor.menus.menus);
console.log('Available menus:', availableMenus);
// Programmatically trigger menu action
const linkMenu = editor.menus.menus.link;
if (linkMenu && linkMenu.onClick) {
// Save selection first
editor.selection.saveRange();
// Trigger link menu
linkMenu.onClick(new Event('click'));
}Simple click-to-execute menus that perform immediate actions.
// Examples of click menus:
// - bold, italic, underline, strikeThrough
// - undo, redo
// - justifyLeft, justifyCenter, justifyRight
const clickMenus = ['bold', 'italic', 'underline', 'strikeThrough', 'undo', 'redo'];
clickMenus.forEach(menuName => {
const menu = editor.menus.menus[menuName];
if (menu && menu.type === 'click') {
console.log(`${menuName} is a click menu`);
}
});Menus that show dropdown lists on hover.
// Examples of droplist menus:
// - head (heading styles)
// - fontSize, fontName
// - foreColor, backColor
// - list (ordered/unordered)
const droplistMenus = ['head', 'fontSize', 'fontName', 'foreColor', 'backColor', 'list'];
droplistMenus.forEach(menuName => {
const menu = editor.menus.menus[menuName];
if (menu && menu.type === 'droplist' && menu.droplist) {
console.log(`${menuName} has a droplist`);
// Access droplist methods
// menu.droplist.show() - show droplist
// menu.droplist.hide() - hide droplist
}
});Menus that open modal panels for complex interactions.
// Examples of panel menus:
// - link (create/edit links)
// - image (insert images)
// - video (embed videos)
// - table (create tables)
// - code (code blocks)
// - emoticon (insert emojis)
const panelMenus = ['link', 'image', 'video', 'table', 'code', 'emoticon'];
panelMenus.forEach(menuName => {
const menu = editor.menus.menus[menuName];
if (menu && menu.type === 'panel' && menu.panel) {
console.log(`${menuName} has a panel`);
// Access panel methods
// menu.panel.show() - show panel
// menu.panel.hide() - hide panel
}
});// Custom function to update specific menu states
function updateMenuStates() {
const menus = editor.menus.menus;
// Update bold menu state
if (menus.bold && menus.bold.tryChangeActive) {
menus.bold.tryChangeActive();
}
// Update italic menu state
if (menus.italic && menus.italic.tryChangeActive) {
menus.italic.tryChangeActive();
}
// Update all menu states
editor.menus.changeActive();
}
// Call after programmatic formatting
editor.cmd.do('bold');
updateMenuStates();// Monitor menu state changes
function monitorMenuState(menuName) {
const menu = editor.menus.menus[menuName];
if (!menu || !menu.$elem) return;
// Check if menu is active
const isActive = menu.$elem.hasClass('w-e-active');
console.log(`${menuName} is ${isActive ? 'active' : 'inactive'}`);
return isActive;
}
// Usage
const isBold = monitorMenuState('bold');
const isItalic = monitorMenuState('italic');
// Monitor multiple menu states
function getActiveMenus() {
const activeMenus = [];
const menus = editor.menus.menus;
Object.keys(menus).forEach(menuName => {
if (monitorMenuState(menuName)) {
activeMenus.push(menuName);
}
});
return activeMenus;
}
const currentlyActive = getActiveMenus();
console.log('Currently active menus:', currentlyActive);// Add custom behavior to existing menus
function enhanceMenus() {
const menus = editor.menus.menus;
// Enhance bold menu
if (menus.bold && menus.bold.$elem) {
menus.bold.$elem.on('click', function() {
console.log('Bold menu clicked');
// Custom analytics, validation, etc.
});
}
// Enhance image menu
if (menus.image && menus.image.$elem) {
menus.image.$elem.on('click', function() {
console.log('Image menu clicked');
// Custom image handling logic
});
}
}
// Call after editor creation
editor.create();
enhanceMenus();// Check which menus are available
function checkMenuAvailability() {
const requiredMenus = ['bold', 'italic', 'link', 'image'];
const availableMenus = Object.keys(editor.menus.menus);
const missing = requiredMenus.filter(menu => !availableMenus.includes(menu));
if (missing.length > 0) {
console.warn('Missing required menus:', missing);
return false;
}
console.log('All required menus are available');
return true;
}
// Validate menu configuration
if (checkMenuAvailability()) {
// Proceed with menu-dependent functionality
console.log('Editor is properly configured');
} else {
// Handle missing menus
console.log('Some menus are missing from configuration');
}// Synchronize menu states with external UI
function syncWithExternalUI() {
// Get current menu states
const menuStates = {
bold: monitorMenuState('bold'),
italic: monitorMenuState('italic'),
underline: monitorMenuState('underline')
};
// Update external UI elements
Object.keys(menuStates).forEach(menuName => {
const externalButton = document.getElementById(`external-${menuName}`);
if (externalButton) {
externalButton.classList.toggle('active', menuStates[menuName]);
}
});
}
// Sync on selection change
editor.customConfig.onchange = function() {
setTimeout(syncWithExternalUI, 150);
};
// Manual sync
function manualSync() {
editor.menus.changeActive();
setTimeout(syncWithExternalUI, 100);
}interface MenusAPI {
/** Collection of individual menu instances */
menus: {[menuName: string]: MenuInstance};
/** Update active state of all menu items */
changeActive(): void;
}
interface MenuInstance {
/** Menu interaction type */
type: 'click' | 'droplist' | 'panel';
/** Menu DOM element */
$elem: Element;
/** Update active state if supported */
tryChangeActive?(): void;
/** Click event handler */
onClick?(event: Event): void;
/** Dropdown list for droplist menus */
droplist?: {
show(): void;
hide(): void;
showTimeoutId?: number;
hideTimeoutId?: number;
};
/** Panel for panel menus */
panel?: {
show(): void;
hide(): void;
};
}The menu system works closely with the configuration system. See Configuration for menu setup:
// Configure available menus
editor.customConfig.menus = [
'head', 'bold', 'fontSize', 'fontName', 'italic', 'underline',
'strikeThrough', 'foreColor', 'backColor', 'link', 'list',
'justify', 'quote', 'emoticon', 'image', 'table', 'video',
'code', 'undo', 'redo'
];
// Create editor
editor.create();
// Access configured menus
const configuredMenus = Object.keys(editor.menus.menus);
console.log('Configured menus:', configuredMenus);Install with Tessl CLI
npx tessl i tessl/npm-wangeditor