0
# Click Handling
1
2
Click event processing for link navigation with configurable behavior and selection options. The click handler provides intelligent link navigation while respecting editor state and user preferences.
3
4
## Capabilities
5
6
### Click Handler Plugin Function
7
8
Creates a ProseMirror plugin that handles click events on links within the editor.
9
10
```typescript { .api }
11
/**
12
* Creates a ProseMirror plugin for handling link clicks
13
* @param options - Configuration options for click behavior
14
* @returns ProseMirror Plugin instance
15
*/
16
function clickHandler(options: ClickHandlerOptions): Plugin;
17
```
18
19
**Usage Examples:**
20
21
```typescript
22
import { Plugin } from "@tiptap/pm/state";
23
import { clickHandler } from "@tiptap/extension-link";
24
25
// Create click handler plugin
26
const linkClickPlugin = clickHandler({
27
type: linkMarkType,
28
editor: editorInstance,
29
enableClickSelection: true,
30
});
31
32
// Plugin is automatically used by Link extension
33
const editor = new Editor({
34
extensions: [
35
Link.configure({
36
openOnClick: true, // Enables the click handler plugin
37
enableClickSelection: false,
38
}),
39
],
40
});
41
```
42
43
### Click Handler Options Interface
44
45
Configuration interface for click handler plugin behavior and features.
46
47
```typescript { .api }
48
interface ClickHandlerOptions {
49
/**
50
* The link mark type from ProseMirror schema
51
*/
52
type: MarkType;
53
54
/**
55
* Tiptap editor instance for command access
56
*/
57
editor: Editor;
58
59
/**
60
* If enabled, clicking a link will select the entire link text
61
* @default false
62
*/
63
enableClickSelection?: boolean;
64
}
65
```
66
67
### Click Behavior Logic
68
69
The click handler implements intelligent behavior based on editor state and link attributes.
70
71
**Click Processing Flow:**
72
73
```typescript
74
// 1. Event validation (left-click only)
75
// 2. Editor editability check
76
// 3. Link element detection (direct or ancestor)
77
// 4. Attribute extraction (href, target, rel)
78
// 5. Optional link selection
79
// 6. Link navigation
80
```
81
82
**Event Filtering:**
83
84
```typescript
85
// Only processes:
86
// - Left mouse button (button === 0)
87
// - When editor is editable
88
// - When click target is a link or contains a link
89
// - When link has valid href attribute
90
91
// Ignores:
92
// - Right-click and middle-click
93
// - Clicks when editor is not editable
94
// - Clicks on non-link elements
95
// - Links without href attributes
96
```
97
98
### Link Detection
99
100
Advanced link element detection that handles various DOM structures.
101
102
```typescript { .api }
103
/**
104
* Link detection algorithm used by click handler
105
* Finds link elements in click event target hierarchy
106
*/
107
interface LinkDetection {
108
// Direct link element
109
target: HTMLAnchorElement;
110
111
// Nested link detection
112
parentTraversal: HTMLElement[];
113
114
// Link identification
115
isLinkElement: (element: HTMLElement) => element is HTMLAnchorElement;
116
}
117
```
118
119
**Detection Examples:**
120
121
```typescript
122
// Direct link click
123
<a href="https://example.com">Click me</a>
124
// ✓ Detected: event.target is HTMLAnchorElement
125
126
// Nested element click
127
<a href="https://example.com">
128
<span>Click me</span>
129
</a>
130
// ✓ Detected: traverses parent elements to find <a>
131
132
// Complex nesting
133
<a href="https://example.com">
134
<strong>
135
<em>Click me</em>
136
</strong>
137
</a>
138
// ✓ Detected: traverses multiple parent levels
139
```
140
141
### Link Selection
142
143
Optional link text selection when `enableClickSelection` is enabled.
144
145
```typescript { .api }
146
/**
147
* Link selection behavior when enableClickSelection is true
148
* Selects the entire link text range before opening
149
*/
150
interface LinkSelection {
151
/** Extend mark range to select entire link */
152
extendMarkRange: (markName: string) => void;
153
154
/** Selection happens before link navigation */
155
selectionTiming: 'before-navigation';
156
}
157
```
158
159
**Usage Examples:**
160
161
```typescript
162
import { Editor } from "@tiptap/core";
163
import { Link } from "@tiptap/extension-link";
164
165
// Enable link selection on click
166
const editor = new Editor({
167
extensions: [
168
Link.configure({
169
openOnClick: true,
170
enableClickSelection: true, // Selects link text before opening
171
}),
172
],
173
});
174
175
// Manual link selection
176
editor.commands.extendMarkRange('link'); // Selects current link
177
```
178
179
### Link Navigation
180
181
Configurable link opening behavior with target and security attributes.
182
183
**Navigation Logic:**
184
185
```typescript
186
// 1. Extract link attributes from DOM or ProseMirror state
187
// 2. Priority: DOM attributes > ProseMirror mark attributes
188
// 3. Open link using window.open with proper target
189
// 4. Respect security attributes (rel="noopener")
190
```
191
192
**Attribute Sources:**
193
194
```typescript
195
// DOM attributes (higher priority)
196
const href = linkElement.href;
197
const target = linkElement.target;
198
199
// ProseMirror mark attributes (fallback)
200
const attrs = getAttributes(view.state, 'link');
201
const href = attrs.href;
202
const target = attrs.target;
203
```
204
205
### Advanced Click Configurations
206
207
Complex click handling configurations for different use cases.
208
209
**Conditional Link Opening:**
210
211
```typescript
212
import { Editor } from "@tiptap/core";
213
import { Link } from "@tiptap/extension-link";
214
215
const editor = new Editor({
216
extensions: [
217
Link.configure({
218
openOnClick: true,
219
enableClickSelection: false,
220
// Custom validation through isAllowedUri affects click behavior
221
isAllowedUri: (url) => {
222
// Only allow opening of safe URLs
223
const safeDomains = ['example.com', 'tiptap.dev'];
224
try {
225
const urlObj = new URL(url);
226
return safeDomains.includes(urlObj.hostname);
227
} catch {
228
return false;
229
}
230
},
231
}),
232
],
233
});
234
```
235
236
**Editor State-Aware Clicking:**
237
238
```typescript
239
// The click handler automatically respects editor state:
240
241
// When editor is not editable:
242
editor.setEditable(false);
243
// Links still clickable (navigation only)
244
245
// When editor is editable:
246
editor.setEditable(true);
247
// Links clickable with optional selection
248
```
249
250
**Custom Click Handling:**
251
252
```typescript
253
// For custom click behavior, you can disable the built-in handler
254
// and implement your own:
255
256
const editor = new Editor({
257
extensions: [
258
Link.configure({
259
openOnClick: false, // Disable built-in click handling
260
}),
261
],
262
});
263
264
// Add custom click listener
265
editor.view.dom.addEventListener('click', (event) => {
266
const target = event.target as HTMLElement;
267
const link = target.closest('a');
268
269
if (link) {
270
event.preventDefault();
271
// Custom logic here
272
console.log('Link clicked:', link.href);
273
274
// Optional: use editor commands
275
if (confirmNavigation(link.href)) {
276
window.open(link.href, '_blank');
277
}
278
}
279
});
280
```
281
282
### Security Considerations
283
284
The click handler includes security features to prevent malicious link behavior.
285
286
**Security Features:**
287
288
```typescript
289
// 1. URL validation using configured isAllowedUri function
290
// 2. Proper target attribute handling
291
// 3. Security attributes preservation (rel="noopener")
292
// 4. XSS prevention through validation
293
// 5. Event sanitization
294
```
295
296
**Safe Link Opening:**
297
298
```typescript
299
// The click handler ensures safe link opening:
300
window.open(href, target || '_blank');
301
302
// With security attributes:
303
// rel="noopener noreferrer" prevents window.opener access
304
// target="_blank" opens in new tab/window
305
```
306
307
### Integration with Link Extension
308
309
The click handler plugin is automatically configured and managed by the Link extension.
310
311
**Automatic Integration:**
312
313
```typescript
314
// When openOnClick is enabled in Link configuration:
315
const editor = new Editor({
316
extensions: [
317
Link.configure({
318
openOnClick: true, // Automatically adds click handler plugin
319
enableClickSelection: true,
320
}),
321
],
322
});
323
324
// The Link extension automatically:
325
// 1. Creates click handler plugin with proper configuration
326
// 2. Passes editor reference and mark type
327
// 3. Configures selection behavior
328
// 4. Manages plugin lifecycle
329
```
330
331
### Performance and UX
332
333
The click handler is optimized for performance and user experience.
334
335
**Performance Features:**
336
337
```typescript
338
// 1. Efficient event filtering (left-click only)
339
// 2. Fast DOM traversal for link detection
340
// 3. Minimal attribute extraction
341
// 4. No unnecessary selection operations
342
// 5. Early returns for invalid states
343
```
344
345
**UX Considerations:**
346
347
```typescript
348
// 1. Respects editor editability state
349
// 2. Optional link text selection for visual feedback
350
// 3. Proper cursor behavior after navigation
351
// 4. Security-conscious link opening
352
// 5. Consistent behavior across different link structures
353
```
354
355
## Types
356
357
```typescript { .api }
358
/** Configuration options for click handler plugin */
359
interface ClickHandlerOptions {
360
type: MarkType;
361
editor: Editor;
362
enableClickSelection?: boolean;
363
}
364
365
/** Link element detection interface */
366
interface LinkElement extends HTMLAnchorElement {
367
href: string;
368
target?: string;
369
rel?: string;
370
}
371
```