Lightweight web-based rich text editor (WYSIWYG editor) built with JavaScript and CSS for modern browsers
—
Text selection and cursor positioning APIs for programmatic text selection control.
Get the current text selection range in the editor.
/**
* Get current selection range
* @returns Range object representing current selection, or null if no selection
*/
getRange(): Range | null;Usage Examples:
// Get current selection
const range = editor.selection.getRange();
if (range) {
console.log('Selection start:', range.startOffset);
console.log('Selection end:', range.endOffset);
console.log('Selected text:', range.toString());
} else {
console.log('No selection');
}
// Check if text is selected
function hasSelection() {
const range = editor.selection.getRange();
return range && !range.collapsed;
}
// Get selection info
function getSelectionInfo() {
const range = editor.selection.getRange();
if (!range) return null;
return {
text: range.toString(),
collapsed: range.collapsed,
startOffset: range.startOffset,
endOffset: range.endOffset
};
}Save the current selection range for later restoration.
/**
* Save current selection range
* @param range - Optional specific range to save, defaults to current selection
*/
saveRange(range?: Range): void;Usage Examples:
// Save current selection
editor.selection.saveRange();
// Save specific range
const range = editor.selection.getRange();
if (range) {
editor.selection.saveRange(range);
}
// Save selection before modal/dialog
function openModal() {
editor.selection.saveRange(); // Save current position
showModal(); // User interaction may lose focus
}
// Save selection in event handler
editor.customConfig.onblur = function() {
editor.selection.saveRange(); // Preserve selection when editor loses focus
};Restore a previously saved selection range.
/**
* Restore previously saved selection
* Must be called after saveRange()
*/
restoreSelection(): void;Usage Examples:
// Save and restore selection pattern
editor.selection.saveRange();
// ... some operation that may change selection
editor.selection.restoreSelection();
// Restore after modal close
function closeModal() {
hideModal();
editor.selection.restoreSelection(); // Restore saved position
}
// Maintain selection during operations
function insertAtSelection(html) {
editor.selection.saveRange();
editor.cmd.do('insertHTML', html);
// Selection automatically moves to after inserted content
}Create a selection range based on a DOM element.
/**
* Create selection range by DOM element
* @param elem - Target DOM element
* @param toStart - Whether to position at start of element
* @param isCollapseToEnd - Whether to collapse selection to end
*/
createRangeByElem(elem: Element, toStart: boolean, isCollapseToEnd: boolean): void;Usage Examples:
// Select entire paragraph
const paragraph = editor.$textElem.find('p').get(0);
if (paragraph) {
editor.selection.createRangeByElem(paragraph, true, false);
}
// Position cursor at start of element
const heading = editor.$textElem.find('h1').get(0);
if (heading) {
editor.selection.createRangeByElem(heading, true, true);
}
// Position cursor at end of element
const lastParagraph = editor.$textElem.find('p').last().get(0);
if (lastParagraph) {
editor.selection.createRangeByElem(lastParagraph, false, true);
}Get the DOM element that contains the current selection.
/**
* Get container element of current selection
* @param range - Optional specific range, defaults to current selection
* @returns DOM element containing the selection
*/
getSelectionContainerElem(range?: Range): Element;Usage Examples:
// Get container of current selection
const container = editor.selection.getSelectionContainerElem();
console.log('Selection is in:', container.tagName);
// Get container of specific range
const range = editor.selection.getRange();
if (range) {
const container = editor.selection.getSelectionContainerElem(range);
console.log('Container:', container);
}
// Check if selection is in specific element type
function isSelectionInElement(tagName) {
const container = editor.selection.getSelectionContainerElem();
return container && container.tagName.toLowerCase() === tagName.toLowerCase();
}
// Usage
if (isSelectionInElement('h1')) {
console.log('Selection is in a heading');
}Get the DOM element at the start of the current selection.
/**
* Get DOM element at selection start
* @param range - Optional specific range, defaults to current selection
* @returns DOM element at start of selection
*/
getSelectionStartElem(range?: Range): Element;Get the DOM element at the end of the current selection.
/**
* Get DOM element at selection end
* @param range - Optional specific range, defaults to current selection
* @returns DOM element at end of selection
*/
getSelectionEndElem(range?: Range): Element;Collapse the current selection range to a single point.
/**
* Collapse current selection to start or end
* @param toStart - Whether to collapse to start (true) or end (false)
*/
collapseRange(toStart?: boolean): void;Get the plain text content of the current selection.
/**
* Get plain text of current selection
* @returns Selected text as string, empty string if no selection
*/
getSelectionText(): string;Check whether the current selection is empty (collapsed).
/**
* Check if current selection is empty/collapsed
* @returns True if selection is empty, false otherwise
*/
isSelectionEmpty(): boolean;Create an empty selection range with a zero-width space marker.
/**
* Create empty range with invisible marker
* Used for maintaining cursor position in empty elements
*/
createEmptyRange(): void;interface SelectionAPI {
/** Get current selection range */
getRange(): Range | null;
/** Save current selection range */
saveRange(range?: Range): void;
/** Restore previously saved selection */
restoreSelection(): void;
/** Create selection range by DOM element */
createRangeByElem(elem: Element, toStart: boolean, isCollapseToEnd: boolean): void;
/** Get container element of selection */
getSelectionContainerElem(range?: Range): Element;
/** Get DOM element at selection start */
getSelectionStartElem(range?: Range): Element;
/** Get DOM element at selection end */
getSelectionEndElem(range?: Range): Element;
/** Collapse selection to start or end */
collapseRange(toStart?: boolean): void;
/** Get plain text of selection */
getSelectionText(): string;
/** Check if selection is empty */
isSelectionEmpty(): boolean;
/** Create empty range with marker */
createEmptyRange(): void;
}class SelectionManager {
constructor(editor) {
this.editor = editor;
this.savedRanges = [];
}
pushSelection() {
const range = this.editor.selection.getRange();
if (range) {
this.savedRanges.push(range.cloneRange());
}
}
popSelection() {
const range = this.savedRanges.pop();
if (range) {
this.editor.selection.saveRange(range);
this.editor.selection.restoreSelection();
}
}
hasSelection() {
const range = this.editor.selection.getRange();
return range && !range.collapsed;
}
}
// Usage
const selectionManager = new SelectionManager(editor);
selectionManager.pushSelection();
// ... operations
selectionManager.popSelection();// Insert content at selection with smart positioning
function smartInsert(html, restoreSelection = true) {
// Save current selection
editor.selection.saveRange();
// Insert content
editor.cmd.do('insertHTML', html);
// Optionally restore selection
if (restoreSelection) {
// Move selection to after inserted content
setTimeout(() => {
const range = editor.selection.getRange();
if (range) {
range.collapse(false); // Collapse to end
editor.selection.saveRange(range);
editor.selection.restoreSelection();
}
}, 0);
}
}
// Usage
smartInsert('<strong>Bold text</strong>');
smartInsert('<img src="image.jpg" alt="Image">', false);// Apply formatting only to selected text
function formatSelection(command, value) {
const range = editor.selection.getRange();
if (!range || range.collapsed) {
console.log('No text selected');
return false;
}
// Save selection
editor.selection.saveRange();
// Apply formatting
editor.cmd.do(command, value);
// Restore selection to see the result
editor.selection.restoreSelection();
return true;
}
// Usage
formatSelection('bold');
formatSelection('foreColor', '#ff0000');// Perform operations based on selection context
function contextAwareOperation() {
const container = editor.selection.getSelectionContainerElem();
const tagName = container.tagName.toLowerCase();
switch (tagName) {
case 'h1':
case 'h2':
case 'h3':
console.log('Selection is in a heading');
// Heading-specific operations
break;
case 'p':
console.log('Selection is in a paragraph');
// Paragraph-specific operations
break;
case 'li':
console.log('Selection is in a list item');
// List-specific operations
break;
default:
console.log('Selection is in:', tagName);
}
}// Utility functions for common selection tasks
const SelectionUtils = {
// Check if entire element is selected
isElementSelected(element) {
const range = editor.selection.getRange();
if (!range) return false;
const selection = window.getSelection();
return selection.containsNode(element, false);
},
// Select entire word at cursor
selectWordAtCursor() {
const range = editor.selection.getRange();
if (!range) return;
range.expand('word');
editor.selection.saveRange(range);
editor.selection.restoreSelection();
},
// Get selected text with formatting preserved
getSelectedHTML() {
const range = editor.selection.getRange();
if (!range) return '';
const container = document.createElement('div');
container.appendChild(range.cloneContents());
return container.innerHTML;
},
// Move cursor to end of editor
moveToEnd() {
const textElem = editor.$textElem.get(0);
if (textElem) {
editor.selection.createRangeByElem(textElem, false, true);
}
}
};
// Usage
if (SelectionUtils.isElementSelected(someElement)) {
console.log('Element is fully selected');
}
SelectionUtils.selectWordAtCursor();
const selectedHTML = SelectionUtils.getSelectedHTML();
SelectionUtils.moveToEnd();Install with Tessl CLI
npx tessl i tessl/npm-wangeditor