Handle Element Selection
Learn how to work with user selections, handle selection changes, and create responsive interfaces that react to what users select in their Adobe Express documents.
Getting Started with Selections
Selections in Adobe Express represent the elements (nodes) that users have currently selected in their document. The selection system provides access to what's selected, the ability to change selections programmatically, and events to respond to selection changes.
All selection operations use the Document API and run in the document sandbox environment. This means your selection code should be placed in your code.js file, not in your main iframe panel code.
Make sure your manifest.json includes "documentSandbox": "code.js" in the entry points to set up the document sandbox environment.
Quick Start Example
Copied to your clipboard// sandbox/code.jsimport { editor } from "express-document-sdk";// Check if anything is selectedif (editor.context.hasSelection) {console.log(`Selected ${editor.context.selection.length} item(s)`);} else {console.log("Nothing is selected");}
Copied to your clipboard// sandbox/code.tsimport { editor } from "express-document-sdk";// Check if anything is selectedif (editor.context.hasSelection) {console.log(`Selected ${editor.context.selection.length} item(s)`);} else {console.log("Nothing is selected");}
Understanding Selections
In Adobe Express, the selection system provides:
- Current selection access - Get what's currently selected
- Selection modification - Programmatically change selections
- Selection events - React to selection changes
- Selection filtering - Handle locked/non-editable content
The selection system automatically enforces constraints like artboard boundaries and hierarchy rules. See the Best Practices & Guidelines section for complete details on selection rules and restrictions.
Basic Selection Operations
Core operations for working with selections.
Getting the Current Selection
Copied to your clipboard// sandbox/code.jsimport { editor } from "express-document-sdk";// Get the current selectionconst selection = editor.context.selection;console.log("Selected nodes:", selection.length);// Process each selected nodeselection.forEach((node, index) => {console.log(`Node ${index + 1}: ${node.type}`);// Common node properties you can accessconsole.log(" Position:", node.translation);console.log(" Size:", { width: node.width, height: node.height });});
Copied to your clipboard// sandbox/code.tsimport { editor, Node, EditorEvent } from "express-document-sdk";// Get the current selectionconst selection: readonly Node[] = editor.context.selection;console.log("Selected nodes:", selection.length);// Process each selected nodeselection.forEach((node: Node, index: number) => {console.log(`Node ${index + 1}: ${node.type}`);// Common node properties you can accessconsole.log(" Position:", node.translation);console.log(" Size:", { width: node.width, height: node.height });});
Programmatic Selection
Copied to your clipboard// sandbox/code.jsimport { editor } from "express-document-sdk";// Create and select a single elementconst rectangle = editor.createRectangle();rectangle.width = 100;rectangle.height = 100;rectangle.translation = { x: 50, y: 50 };// Add to documenteditor.context.insertionParent.children.append(rectangle);// Select the rectangle (single element)editor.context.selection = rectangle;// OR using array syntax: editor.context.selection = [rectangle];console.log("Rectangle is now selected");
Copied to your clipboard// code.tsimport { editor, RectangleNode, ContainerNode } from "express-document-sdk";// Create a simple rectangle to demonstrate selectionconst rectangle: RectangleNode = editor.createRectangle();rectangle.width = 100;rectangle.height = 100;rectangle.translation = { x: 50, y: 50 };// Add to documentconst insertionParent: ContainerNode = editor.context.insertionParent;insertionParent.children.append(rectangle);// Select the rectangle (single element)editor.context.selection = rectangle;// OR using array syntax: editor.context.selection = [rectangle];console.log("Rectangle is now selected");
Multiple Selection
Copied to your clipboard// sandbox/code.jsimport { editor } from "express-document-sdk";// Create multiple elementsconst rectangle = editor.createRectangle();rectangle.width = 80;rectangle.height = 80;rectangle.translation = { x: 50, y: 50 };const ellipse = editor.createEllipse();ellipse.rx = 40;ellipse.ry = 40;ellipse.translation = { x: 200, y: 50 };// Add both to documentconst parent = editor.context.insertionParent;parent.children.append(rectangle, ellipse);// Select both elements at onceeditor.context.selection = [rectangle, ellipse];console.log("Multiple elements selected:", editor.context.selection.length);
Copied to your clipboard// sandbox/code.tsimport { editor, RectangleNode, EllipseNode, ContainerNode } from "express-document-sdk";// Create multiple simple elementsconst rectangle: RectangleNode = editor.createRectangle();rectangle.width = 80;rectangle.height = 80;rectangle.translation = { x: 50, y: 50 };const ellipse: EllipseNode = editor.createEllipse();ellipse.rx = 40;ellipse.ry = 40;ellipse.translation = { x: 200, y: 50 };// Add both to documentconst parent: ContainerNode = editor.context.insertionParent;parent.children.append(rectangle, ellipse);// Select both elements at onceeditor.context.selection = [rectangle, ellipse];console.log("Multiple elements selected:", editor.context.selection.length);
Clearing the Selection
Copied to your clipboard// sandbox/code.jsimport { editor } from "express-document-sdk";// Clear the selection - both ways workeditor.context.selection = [];// OR: editor.context.selection = undefined;console.log("Selection cleared");console.log("Has selection:", editor.context.hasSelection); // false
Copied to your clipboard// sandbox/code.tsimport { editor } from "express-document-sdk";// Clear the selection - both ways workeditor.context.selection = [];// OR: editor.context.selection = undefined;console.log("Selection cleared");console.log("Has selection:", editor.context.hasSelection); // false
Selection Events
Respond to selection changes to create dynamic UIs that update based on what's selected.
Basic Selection Change Handler
Copied to your clipboard// sandbox/code.jsimport { editor, EditorEvent } from "express-document-sdk";// Listen for selection changesconst handlerId = editor.context.on(EditorEvent.selectionChange, () => {const selection = editor.context.selection;console.log("Selection changed!");console.log("New selection count:", selection.length);if (selection.length === 0) {console.log("Nothing selected");} else if (selection.length === 1) {console.log("One item selected:", selection[0].type);} else {console.log("Multiple items selected");}});// Store handlerId if you need to unregister laterconsole.log("Selection handler registered:", handlerId);
Copied to your clipboard// sandbox/code.tsimport { editor, Node, EditorEvent } from "express-document-sdk";// Listen for selection changesconst handlerId: string = editor.context.on(EditorEvent.selectionChange, () => {const selection: readonly Node[] = editor.context.selection;console.log("Selection changed!");console.log("New selection count:", selection.length);if (selection.length === 0) {console.log("Nothing selected");} else if (selection.length === 1) {console.log("One item selected:", selection[0].type);} else {console.log("Multiple items selected");}});// Store handlerId if you need to unregister laterconsole.log("Selection handler registered:", handlerId);
Properties Panel Example
Dynamic properties panel based on selection:
Copied to your clipboard// sandbox/code.jsimport { editor, EditorEvent } from "express-document-sdk";function updatePropertiesPanel() {const selection = editor.context.selection;if (selection.length === 0) {console.log("Properties Panel: Show 'Nothing Selected' state");return;}if (selection.length === 1) {const node = selection[0];console.log("Properties Panel: Show properties for", node.type);// Show different properties based on node typeif (node.type === "Text") {console.log(" - Show font controls");console.log(" - Show text color picker");} else if (node.type === "Rectangle" || node.type === "Ellipse") {console.log(" - Show fill color picker");console.log(" - Show stroke controls");}// Common properties for all nodesconsole.log(" - Show position controls");console.log(" - Show size controls");} else {console.log("Properties Panel: Show multi-selection options");console.log(` - ${selection.length} items selected`);console.log(" - Show alignment tools");console.log(" - Show group option");}}// Register the handlereditor.context.on(EditorEvent.selectionChange, updatePropertiesPanel);// Call once on startup to initializeupdatePropertiesPanel();
Copied to your clipboard// sandbox/code.tsimport { editor, Node, TextNode } from "express-document-sdk";function updatePropertiesPanel(): void {const selection: readonly Node[] = editor.context.selection;if (selection.length === 0) {console.log("Properties Panel: Show 'Nothing Selected' state");return;}if (selection.length === 1) {const node: Node = selection[0];console.log("Properties Panel: Show properties for", node.type);// Show different properties based on node typeif (node.type === "Text") {console.log(" - Show font controls");console.log(" - Show text color picker");} else if (node.type === "Rectangle" || node.type === "Ellipse") {console.log(" - Show fill color picker");console.log(" - Show stroke controls");}// Common properties for all nodesconsole.log(" - Show position controls");console.log(" - Show size controls");} else {console.log("Properties Panel: Show multi-selection options");console.log(` - ${selection.length} items selected`);console.log(" - Show alignment tools");console.log(" - Show group option");}}// Register the handlereditor.context.on(EditorEvent.selectionChange, updatePropertiesPanel);// Call once on startup to initializeupdatePropertiesPanel();
Event Handler Cleanup
⚠️ Important: Always clean up event handlers to prevent memory leaks.
Copied to your clipboard// sandbox/code.jsimport { editor, EditorEvent } from "express-document-sdk";// Store handler IDs so you can unregister them laterlet selectionHandlerId = null;function startListening() {// Register handler and store the IDselectionHandlerId = editor.context.on(EditorEvent.selectionChange, () => {console.log("Selection changed!");// Handle selection change});console.log("✅ Selection handler registered");}function stopListening() {// Clean up the handlerif (selectionHandlerId) {editor.context.off(EditorEvent.selectionChange, selectionHandlerId);selectionHandlerId = null;console.log("✅ Selection handler cleaned up");}}// Start listeningstartListening();// Clean up when your add-on is being destroyed or reset// stopListening();
Copied to your clipboard// sandbox/code.tsimport { editor, EditorEvent } from "express-document-sdk";// Store handler IDs so you can unregister them laterlet selectionHandlerId: string | null = null;function startListening(): void {// Register handler and store the IDselectionHandlerId = editor.context.on(EditorEvent.selectionChange, () => {console.log("Selection changed!");// Handle selection change});console.log("✅ Selection handler registered");}function stopListening(): void {// Clean up the handlerif (selectionHandlerId) {editor.context.off(EditorEvent.selectionChange, selectionHandlerId);selectionHandlerId = null;console.log("✅ Selection handler cleaned up");}}// Start listeningstartListening();// Clean up when your add-on is being destroyed or reset// stopListening();
Advanced Selection Techniques
Advanced patterns for complex add-ons.
Working with Locked/Non-Editable Elements
Handle selections that include locked or non-editable content:
Copied to your clipboard// sandbox/code.jsimport { editor, EditorEvent } from "express-document-sdk";function analyzeCompleteSelection() {const selection = editor.context.selection;const fullSelection = editor.context.selectionIncludingNonEditable;return {editableCount: selection.length,totalCount: fullSelection.length,lockedCount: fullSelection.length - selection.length,types: [...new Set(selection.map(node => node.type))], // Unique typeshasText: selection.some(node => node.type === "Text"),hasShapes: selection.some(node =>node.type === "Rectangle" || node.type === "Ellipse"),isEmpty: !editor.context.hasSelection};}// Example: Dynamic UI updates based on detailed analysiseditor.context.on(EditorEvent.selectionChange, () => {const analysis = analyzeCompleteSelection();console.log("📊 Detailed Selection Info:");console.log(` Editable: ${analysis.editableCount}`);if (analysis.lockedCount > 0) {console.log(` Locked: ${analysis.lockedCount}`);}console.log(` Types: ${analysis.types.join(", ")}`);// Enable specific tools based on contentif (analysis.hasText) {console.log("🔤 Text formatting tools available");}if (analysis.hasShapes) {console.log("🔷 Shape styling tools available");}if (analysis.editableCount > 1) {console.log("📐 Alignment tools available");}});
Copied to your clipboard// code.tsimport { editor, Node, EditorEvent } from "express-document-sdk";interface DetailedSelectionAnalysis {editableCount: number;totalCount: number;lockedCount: number;types: string[];hasText: boolean;hasShapes: boolean;isEmpty: boolean;}function analyzeSelection(): DetailedSelectionAnalysis {const selection: readonly Node[] = editor.context.selection;const fullSelection: readonly Node[] = editor.context.selectionIncludingNonEditable;return {editableCount: selection.length,totalCount: fullSelection.length,lockedCount: fullSelection.length - selection.length,types: [...new Set(selection.map((node: Node) => node.type))], // Unique typeshasText: selection.some((node: Node) => node.type === "Text"),hasShapes: selection.some((node: Node) =>node.type === "Rectangle" || node.type === "Ellipse"),isEmpty: !editor.context.hasSelection};}// Example: Dynamic UI updates based on detailed analysiseditor.context.on(EditorEvent.selectionChange, () => {const analysis: DetailedSelectionAnalysis = analyzeSelection();console.log("📊 Detailed Selection Info:");console.log(` Editable: ${analysis.editableCount}`);if (analysis.lockedCount > 0) {console.log(` Locked: ${analysis.lockedCount}`);}console.log(` Types: ${analysis.types.join(", ")}`);// Enable specific tools based on contentif (analysis.hasText) {console.log("🔤 Text formatting tools available");}if (analysis.hasShapes) {console.log("🔷 Shape styling tools available");}if (analysis.editableCount > 1) {console.log("📐 Alignment tools available");}});
Practical Selection Patterns
Real-world patterns for building selection-based features in your add-on.
Performing Actions on Selected Elements
Common patterns for applying changes to selected elements:
Copied to your clipboard// sandbox/code.jsimport { editor, colorUtils } from "express-document-sdk";// Function to apply red color to selected textfunction applyRedToSelectedText() {const selection = editor.context.selection;// Filter for text nodes onlyconst textNodes = selection.filter(node => node.type === "Text");if (textNodes.length === 0) {console.log("No text nodes selected");return;}// Apply red color to all selected textconst redColor = colorUtils.fromHex("#FF0000");textNodes.forEach(textNode => {textNode.fullContent.applyCharacterStyles({ color: redColor });});console.log(`Applied red color to ${textNodes.length} text nodes`);}// Function to group selected elementsfunction groupSelection() {const selection = editor.context.selection;if (selection.length < 2) {console.log("Need at least 2 elements to create a group");return;}// Create a groupconst group = editor.createGroup();// Add selected elements to the groupselection.forEach(node => {// Remove from current parent and add to groupnode.removeFromParent();group.children.append(node);});// Add group to the documenteditor.context.insertionParent.children.append(group);// Select the new groupeditor.context.selection = group;console.log(`Created group with ${selection.length} elements`);}// Register handlers for different actionseditor.context.on(EditorEvent.selectionChange, () => {const selection = editor.context.selection;// Update UI or enable/disable actions based on selectionif (selection.length === 0) {console.log("No selection - disable all actions");} else if (selection.length === 1) {console.log("Single selection - enable individual actions");} else {console.log("Multiple selection - enable group actions");}});
Copied to your clipboard// sandbox/code.tsimport { editor, colorUtils, Node, TextNode, GroupNode, ContainerNode } from "express-document-sdk";// Function to apply red color to selected textfunction applyRedToSelectedText(): void {const selection: readonly Node[] = editor.context.selection;// Filter for text nodes onlyconst textNodes = selection.filter((node: Node): node is TextNode =>node.type === "Text");if (textNodes.length === 0) {console.log("No text nodes selected");return;}// Apply red color to all selected textconst redColor = colorUtils.fromHex("#FF0000");textNodes.forEach((textNode: TextNode) => {textNode.fullContent.applyCharacterStyles({ color: redColor });});console.log(`Applied red color to ${textNodes.length} text nodes`);}// Function to group selected elementsfunction groupSelection(): void {const selection: readonly Node[] = editor.context.selection;if (selection.length < 2) {console.log("Need at least 2 elements to create a group");return;}// Create a groupconst group: GroupNode = editor.createGroup();// Add selected elements to the groupselection.forEach((node: Node) => {// Remove from current parent and add to groupnode.removeFromParent();group.children.append(node);});// Add group to the documentconst insertionParent: ContainerNode = editor.context.insertionParent;insertionParent.children.append(group);// Select the new groupeditor.context.selection = group;console.log(`Created group with ${selection.length} elements`);}// Register handlers for different actionseditor.context.on(EditorEvent.selectionChange, () => {const selection: readonly Node[] = editor.context.selection;// Update UI or enable/disable actions based on selectionif (selection.length === 0) {console.log("No selection - disable all actions");} else if (selection.length === 1) {console.log("Single selection - enable individual actions");} else {console.log("Multiple selection - enable group actions");}});
Advanced: Selection State Management
Track selection history and manage complex selection states:
Copied to your clipboard// sandbox/code.jsimport { editor, EditorEvent } from "express-document-sdk";class SelectionManager {constructor() {this.selectionHistory = [];this.handlerId = null;this.startListening();}startListening() {this.handlerId = editor.context.on(EditorEvent.selectionChange, () => {const selection = editor.context.selection;// Store selection in history (limit to last 10)this.selectionHistory.push([...selection]);if (this.selectionHistory.length > 10) {this.selectionHistory.shift();}console.log("Selection history length:", this.selectionHistory.length);this.notifySelectionChange(selection);});}notifySelectionChange(selection) {// Custom logic based on selectionif (selection.length === 0) {this.onNoSelection();} else if (selection.length === 1) {this.onSingleSelection(selection[0]);} else {this.onMultipleSelection(selection);}}onNoSelection() {console.log("No elements selected");// Disable context-sensitive UI}onSingleSelection(node) {console.log("Single element selected:", node.type);// Enable single-element actions}onMultipleSelection(selection) {console.log("Multiple elements selected:", selection.length);// Enable multi-element actions}restorePreviousSelection() {if (this.selectionHistory.length >= 2) {const previousSelection = this.selectionHistory[this.selectionHistory.length - 2];editor.context.selection = previousSelection;}}stopListening() {if (this.handlerId) {editor.context.off(EditorEvent.selectionChange, this.handlerId);this.handlerId = null;}}}// Usageconst selectionManager = new SelectionManager();
Copied to your clipboard// code.tsimport { editor, Node, EditorEvent } from "express-document-sdk";class SelectionManager {private selectionHistory: Node[][] = [];private handlerId: string | null = null;constructor() {this.startListening();}startListening(): void {this.handlerId = editor.context.on(EditorEvent.selectionChange, () => {const selection: readonly Node[] = editor.context.selection;// Store selection in history (limit to last 10)this.selectionHistory.push([...selection]);if (this.selectionHistory.length > 10) {this.selectionHistory.shift();}console.log("Selection history length:", this.selectionHistory.length);this.notifySelectionChange(selection);});}private notifySelectionChange(selection: readonly Node[]): void {// Custom logic based on selectionif (selection.length === 0) {this.onNoSelection();} else if (selection.length === 1) {this.onSingleSelection(selection[0]);} else {this.onMultipleSelection(selection);}}private onNoSelection(): void {console.log("No elements selected");// Disable context-sensitive UI}private onSingleSelection(node: Node): void {console.log("Single element selected:", node.type);// Enable single-element actions}private onMultipleSelection(selection: readonly Node[]): void {console.log("Multiple elements selected:", selection.length);// Enable multi-element actions}restorePreviousSelection(): void {if (this.selectionHistory.length >= 2) {const previousSelection = this.selectionHistory[this.selectionHistory.length - 2];editor.context.selection = previousSelection;}}stopListening(): void {if (this.handlerId) {editor.context.off(EditorEvent.selectionChange, this.handlerId);this.handlerId = null;}}}// Usageconst selectionManager = new SelectionManager();
Best Practices & Guidelines
Selection Handler Restrictions
Document Modification Restrictions
Never modify the document inside selection change handlers! This can crash the application.
✅ Safe in selection handlers:
- Update UI panels
- Log information
- Analyze selection
- Enable/disable buttons
- Send data to UI panel
❌ Never do in selection handlers:
- Create, delete, or modify nodes
- Change document structure
- Set properties on selected elements
Selection System Rules
Adobe Express enforces these constraints:
- Artboard constraint: Only nodes within the current artboard can be selected
- Hierarchy filtering: Cannot select both parent and child nodes simultaneously
- Locked node handling: Locked nodes are excluded from main selection but available in
selectionIncludingNonEditable - Automatic filtering: System automatically filters out invalid selections
Performance Guidelines
- Keep handlers fast: Minimize processing time and avoid heavy computations in selection callbacks
- Essential work only: Only perform UI updates, logging, or data analysis in selection handlers
- Always clean up: Unregister event handlers when done using
editor.context.off()to prevent memory leaks
Communicating with Your UI Panel
To create responsive interfaces, you'll need to communicate selection changes from the document sandbox to your UI panel. This allows you to update buttons, property panels, and other UI elements based on what the user has selected.
For complete details on setting up bidirectional communication between your document sandbox and UI panel, see the Communication API reference.
Quick Reference
Common Selection Operations
Copied to your clipboard// Get current selectionconst selection = editor.context.selection;// Check if anything is selectedif (editor.context.hasSelection) { /* ... */ }// Select a single elementeditor.context.selection = node;// Select multiple elementseditor.context.selection = [node1, node2, node3];// Clear selectioneditor.context.selection = [];// Get selection including locked nodesconst fullSelection = editor.context.selectionIncludingNonEditable;
Selection Event Handling
Copied to your clipboard// Register selection change handlerconst handlerId = editor.context.on(EditorEvent.selectionChange, () => {const selection = editor.context.selection;// Handle selection change});// Clean up handlereditor.context.off(EditorEvent.selectionChange, handlerId);
Common Selection Patterns
Copied to your clipboard// Access first selected elementconst node = editor.context.selection[0];// Filter selection by typeconst textNodes = selection.filter(node => node.type === "Text");// Check selection countif (selection.length === 0) { /* nothing selected */ }if (selection.length === 1) { /* single selection */ }if (selection.length > 1) { /* multiple selection */ }// Check for specific node typesconst hasText = selection.some(node => node.type === "Text");const hasShapes = selection.some(node =>["Rectangle", "Ellipse"].includes(node.type));
FAQs
Q: How do I get the current selection?
A: Use editor.context.selection to get an array of currently selected nodes.
Q: How do I listen for selection changes?
A: Use editor.context.on(EditorEvent.selectionChange, callback) to register a selection change handler.
Q: How do I programmatically select elements?
A: Set editor.context.selection = node for single elements or editor.context.selection = [node1, node2] for multiple elements.
Q: What's the difference between selection and selectionIncludingNonEditable?
A: selection only includes editable nodes, while selectionIncludingNonEditable also includes locked/non-editable nodes.
Q: Can I modify the document in a selection change callback?
A: No, avoid making document changes in selection change callbacks as it may destabilize the application.
Q: How do I clear the selection?
A: Set editor.context.selection = [] or editor.context.selection = undefined.
Q: What are the selection rules?
A: Nodes must be within the current artboard, ancestors cannot be selected with descendants, and locked nodes are filtered out.
Q: How do I unregister selection event handlers?
A: Use editor.context.off(EditorEvent.selectionChange, handlerId) with the ID returned from the on() method.
Related Topics
- Context API Reference - Complete API documentation for the Context class
- Communication APIs - Learn how to communicate between document sandbox and UI panel
- Group Elements - Working with selections to create and manage groups
- Position Elements - Positioning and transforming selected elements
- Use Text - Examples of working with text selections using
editor.context.selection[0] - EditorEvent Enumeration - All available editor events
- Node API Reference - Understanding the Node class used in selections
