@bquery/bquery 1.0.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -7
- package/dist/component/index.d.ts +8 -0
- package/dist/component/index.d.ts.map +1 -1
- package/dist/component.es.mjs +80 -53
- package/dist/component.es.mjs.map +1 -1
- package/dist/core/collection.d.ts +46 -0
- package/dist/core/collection.d.ts.map +1 -1
- package/dist/core/element.d.ts +124 -22
- package/dist/core/element.d.ts.map +1 -1
- package/dist/core/utils.d.ts +13 -0
- package/dist/core/utils.d.ts.map +1 -1
- package/dist/core.es.mjs +298 -55
- package/dist/core.es.mjs.map +1 -1
- package/dist/full.d.ts +2 -2
- package/dist/full.d.ts.map +1 -1
- package/dist/full.es.mjs +38 -33
- package/dist/full.iife.js +1 -1
- package/dist/full.iife.js.map +1 -1
- package/dist/full.umd.js +1 -1
- package/dist/full.umd.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.es.mjs +38 -33
- package/dist/reactive/index.d.ts +2 -2
- package/dist/reactive/index.d.ts.map +1 -1
- package/dist/reactive/signal.d.ts +107 -0
- package/dist/reactive/signal.d.ts.map +1 -1
- package/dist/reactive.es.mjs +92 -55
- package/dist/reactive.es.mjs.map +1 -1
- package/dist/security/sanitize.d.ts.map +1 -1
- package/dist/security.es.mjs +136 -66
- package/dist/security.es.mjs.map +1 -1
- package/package.json +120 -120
- package/src/component/index.ts +414 -360
- package/src/core/collection.ts +454 -339
- package/src/core/element.ts +740 -493
- package/src/core/utils.ts +444 -425
- package/src/full.ts +106 -101
- package/src/index.ts +27 -27
- package/src/reactive/index.ts +22 -9
- package/src/reactive/signal.ts +506 -347
- package/src/security/sanitize.ts +553 -446
package/src/core/element.ts
CHANGED
|
@@ -1,493 +1,740 @@
|
|
|
1
|
-
import { sanitizeHtml } from '../security/sanitize';
|
|
2
|
-
import { applyAll, toElementList } from './shared';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Wrapper for a single DOM element.
|
|
6
|
-
* Provides a chainable, jQuery-like API for DOM manipulation.
|
|
7
|
-
*
|
|
8
|
-
* This class encapsulates a DOM element and provides methods for:
|
|
9
|
-
* - Class manipulation (addClass, removeClass, toggleClass)
|
|
10
|
-
* - Attribute and property access (attr, prop, data)
|
|
11
|
-
* - Content manipulation (text, html, append, prepend)
|
|
12
|
-
* - Style manipulation (css)
|
|
13
|
-
* - Event handling (on, off, once, trigger)
|
|
14
|
-
* - DOM traversal (find, closest, parent, children, siblings)
|
|
15
|
-
*
|
|
16
|
-
* All mutating methods return `this` for method chaining.
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* ```ts
|
|
20
|
-
* $('#button')
|
|
21
|
-
* .addClass('active')
|
|
22
|
-
* .css({ color: 'blue' })
|
|
23
|
-
* .on('click', () => console.log('clicked'));
|
|
24
|
-
* ```
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
if (value === undefined) {
|
|
89
|
-
return this.element
|
|
90
|
-
}
|
|
91
|
-
this.element
|
|
92
|
-
return this;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
* @
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
this
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
* @
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
this
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
* @
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
this
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
* @
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
this
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
* @returns The instance for method chaining
|
|
214
|
-
*/
|
|
215
|
-
|
|
216
|
-
this.
|
|
217
|
-
return this;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
* @
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
* @
|
|
244
|
-
*
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
* @
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
* @
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
*
|
|
344
|
-
*
|
|
345
|
-
* @param
|
|
346
|
-
* @returns The
|
|
347
|
-
*/
|
|
348
|
-
|
|
349
|
-
this.element.
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
*
|
|
355
|
-
*
|
|
356
|
-
* @
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
*
|
|
411
|
-
*
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
*
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
*
|
|
465
|
-
*
|
|
466
|
-
* @returns
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
1
|
+
import { sanitizeHtml } from '../security/sanitize';
|
|
2
|
+
import { applyAll, toElementList } from './shared';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Wrapper for a single DOM element.
|
|
6
|
+
* Provides a chainable, jQuery-like API for DOM manipulation.
|
|
7
|
+
*
|
|
8
|
+
* This class encapsulates a DOM element and provides methods for:
|
|
9
|
+
* - Class manipulation (addClass, removeClass, toggleClass)
|
|
10
|
+
* - Attribute and property access (attr, prop, data)
|
|
11
|
+
* - Content manipulation (text, html, append, prepend)
|
|
12
|
+
* - Style manipulation (css)
|
|
13
|
+
* - Event handling (on, off, once, trigger)
|
|
14
|
+
* - DOM traversal (find, closest, parent, children, siblings)
|
|
15
|
+
*
|
|
16
|
+
* All mutating methods return `this` for method chaining.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* $('#button')
|
|
21
|
+
* .addClass('active')
|
|
22
|
+
* .css({ color: 'blue' })
|
|
23
|
+
* .on('click', () => console.log('clicked'));
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
/** Handler signature for delegated events */
|
|
27
|
+
type DelegatedHandler = (event: Event, target: Element) => void;
|
|
28
|
+
|
|
29
|
+
export class BQueryElement {
|
|
30
|
+
/**
|
|
31
|
+
* Stores delegated event handlers for cleanup via undelegate().
|
|
32
|
+
* Key format: `${event}:${selector}`
|
|
33
|
+
* @internal
|
|
34
|
+
*/
|
|
35
|
+
private readonly delegatedHandlers = new Map<string, Map<DelegatedHandler, EventListener>>();
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Creates a new BQueryElement wrapper.
|
|
39
|
+
* @param element - The DOM element to wrap
|
|
40
|
+
*/
|
|
41
|
+
constructor(private readonly element: Element) {}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Exposes the raw DOM element when direct access is needed.
|
|
45
|
+
* Use sparingly; prefer the wrapper methods for consistency.
|
|
46
|
+
*/
|
|
47
|
+
get raw(): Element {
|
|
48
|
+
return this.element;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Exposes the underlying DOM element.
|
|
53
|
+
* Provided for spec compatibility and read-only access.
|
|
54
|
+
*/
|
|
55
|
+
get node(): Element {
|
|
56
|
+
return this.element;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Add one or more classes. */
|
|
60
|
+
addClass(...classNames: string[]): this {
|
|
61
|
+
this.element.classList.add(...classNames);
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Remove one or more classes. */
|
|
66
|
+
removeClass(...classNames: string[]): this {
|
|
67
|
+
this.element.classList.remove(...classNames);
|
|
68
|
+
return this;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Toggle a class by name. */
|
|
72
|
+
toggleClass(className: string, force?: boolean): this {
|
|
73
|
+
this.element.classList.toggle(className, force);
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Get or set an attribute. */
|
|
78
|
+
attr(name: string, value?: string): string | this {
|
|
79
|
+
if (value === undefined) {
|
|
80
|
+
return this.element.getAttribute(name) ?? '';
|
|
81
|
+
}
|
|
82
|
+
this.element.setAttribute(name, value);
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Get or set a property. */
|
|
87
|
+
prop<T extends keyof Element>(name: T, value?: Element[T]): Element[T] | this {
|
|
88
|
+
if (value === undefined) {
|
|
89
|
+
return this.element[name];
|
|
90
|
+
}
|
|
91
|
+
this.element[name] = value;
|
|
92
|
+
return this;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Read or write data attributes in camelCase. */
|
|
96
|
+
data(name: string, value?: string): string | this {
|
|
97
|
+
const key = name.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
|
|
98
|
+
if (value === undefined) {
|
|
99
|
+
return this.element.getAttribute(`data-${key}`) ?? '';
|
|
100
|
+
}
|
|
101
|
+
this.element.setAttribute(`data-${key}`, value);
|
|
102
|
+
return this;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Get or set text content. */
|
|
106
|
+
text(value?: string): string | this {
|
|
107
|
+
if (value === undefined) {
|
|
108
|
+
return this.element.textContent ?? '';
|
|
109
|
+
}
|
|
110
|
+
this.element.textContent = value;
|
|
111
|
+
return this;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Set HTML content using a sanitized string. */
|
|
115
|
+
/**
|
|
116
|
+
* Sets sanitized HTML content on the element.
|
|
117
|
+
* Uses the security module to sanitize input and prevent XSS attacks.
|
|
118
|
+
*
|
|
119
|
+
* @param value - The HTML string to set (will be sanitized)
|
|
120
|
+
* @returns The instance for method chaining
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```ts
|
|
124
|
+
* $('#content').html('<strong>Hello</strong>');
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
html(value: string): this {
|
|
128
|
+
this.element.innerHTML = sanitizeHtml(value);
|
|
129
|
+
return this;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Sets HTML content without sanitization.
|
|
134
|
+
* Use only when you trust the HTML source completely.
|
|
135
|
+
*
|
|
136
|
+
* @param value - The raw HTML string to set
|
|
137
|
+
* @returns The instance for method chaining
|
|
138
|
+
*
|
|
139
|
+
* @warning This method bypasses XSS protection. Use with caution.
|
|
140
|
+
*/
|
|
141
|
+
htmlUnsafe(value: string): this {
|
|
142
|
+
this.element.innerHTML = value;
|
|
143
|
+
return this;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Gets or sets CSS styles on the element.
|
|
148
|
+
*
|
|
149
|
+
* @param property - A CSS property name or an object of property-value pairs
|
|
150
|
+
* @param value - The value when setting a single property
|
|
151
|
+
* @returns The instance for method chaining
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```ts
|
|
155
|
+
* // Single property
|
|
156
|
+
* $('#box').css('color', 'red');
|
|
157
|
+
*
|
|
158
|
+
* // Multiple properties
|
|
159
|
+
* $('#box').css({ color: 'red', 'font-size': '16px' });
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
css(property: string | Record<string, string>, value?: string): this {
|
|
163
|
+
if (typeof property === 'string') {
|
|
164
|
+
if (value !== undefined) {
|
|
165
|
+
(this.element as HTMLElement).style.setProperty(property, value);
|
|
166
|
+
}
|
|
167
|
+
return this;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
for (const [key, val] of Object.entries(property)) {
|
|
171
|
+
(this.element as HTMLElement).style.setProperty(key, val);
|
|
172
|
+
}
|
|
173
|
+
return this;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Appends HTML or elements to the end of the element.
|
|
178
|
+
*
|
|
179
|
+
* @param content - HTML string or element(s) to append
|
|
180
|
+
* @returns The instance for method chaining
|
|
181
|
+
*/
|
|
182
|
+
append(content: string | Element | Element[]): this {
|
|
183
|
+
this.insertContent(content, 'beforeend');
|
|
184
|
+
return this;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Prepends HTML or elements to the beginning of the element.
|
|
189
|
+
*
|
|
190
|
+
* @param content - HTML string or element(s) to prepend
|
|
191
|
+
* @returns The instance for method chaining
|
|
192
|
+
*/
|
|
193
|
+
prepend(content: string | Element | Element[]): this {
|
|
194
|
+
this.insertContent(content, 'afterbegin');
|
|
195
|
+
return this;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Inserts content before this element.
|
|
200
|
+
*
|
|
201
|
+
* @param content - HTML string or element(s) to insert
|
|
202
|
+
* @returns The instance for method chaining
|
|
203
|
+
*/
|
|
204
|
+
before(content: string | Element | Element[]): this {
|
|
205
|
+
this.insertContent(content, 'beforebegin');
|
|
206
|
+
return this;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Inserts content after this element.
|
|
211
|
+
*
|
|
212
|
+
* @param content - HTML string or element(s) to insert
|
|
213
|
+
* @returns The instance for method chaining
|
|
214
|
+
*/
|
|
215
|
+
after(content: string | Element | Element[]): this {
|
|
216
|
+
this.insertContent(content, 'afterend');
|
|
217
|
+
return this;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Wraps the element with the specified wrapper element or tag.
|
|
222
|
+
*
|
|
223
|
+
* @param wrapper - Tag name string or Element to wrap with
|
|
224
|
+
* @returns The instance for method chaining
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* ```ts
|
|
228
|
+
* $('#content').wrap('div'); // Wraps with <div>
|
|
229
|
+
* $('#content').wrap(document.createElement('section'));
|
|
230
|
+
* ```
|
|
231
|
+
*/
|
|
232
|
+
wrap(wrapper: string | Element): this {
|
|
233
|
+
const wrapperEl = typeof wrapper === 'string' ? document.createElement(wrapper) : wrapper;
|
|
234
|
+
this.element.parentNode?.insertBefore(wrapperEl, this.element);
|
|
235
|
+
wrapperEl.appendChild(this.element);
|
|
236
|
+
return this;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Removes the parent element, keeping this element in its place.
|
|
241
|
+
* Essentially the opposite of wrap().
|
|
242
|
+
*
|
|
243
|
+
* @returns The instance for method chaining
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* ```ts
|
|
247
|
+
* // Before: <div><span id="text">Hello</span></div>
|
|
248
|
+
* $('#text').unwrap();
|
|
249
|
+
* // After: <span id="text">Hello</span>
|
|
250
|
+
* ```
|
|
251
|
+
*/
|
|
252
|
+
unwrap(): this {
|
|
253
|
+
const parent = this.element.parentElement;
|
|
254
|
+
if (parent && parent.parentNode) {
|
|
255
|
+
parent.parentNode.insertBefore(this.element, parent);
|
|
256
|
+
parent.remove();
|
|
257
|
+
}
|
|
258
|
+
return this;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Replaces this element with new content.
|
|
263
|
+
*
|
|
264
|
+
* @param content - HTML string (sanitized) or Element to replace with
|
|
265
|
+
* @returns A new BQueryElement wrapping the replacement element
|
|
266
|
+
*
|
|
267
|
+
* @example
|
|
268
|
+
* ```ts
|
|
269
|
+
* const newEl = $('#old').replaceWith('<div id="new">Replaced</div>');
|
|
270
|
+
* ```
|
|
271
|
+
*/
|
|
272
|
+
replaceWith(content: string | Element): BQueryElement {
|
|
273
|
+
let newEl: Element;
|
|
274
|
+
if (typeof content === 'string') {
|
|
275
|
+
const template = document.createElement('template');
|
|
276
|
+
template.innerHTML = sanitizeHtml(content);
|
|
277
|
+
newEl = template.content.firstElementChild ?? document.createElement('div');
|
|
278
|
+
} else {
|
|
279
|
+
newEl = content;
|
|
280
|
+
}
|
|
281
|
+
this.element.replaceWith(newEl);
|
|
282
|
+
return new BQueryElement(newEl);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Scrolls the element into view with configurable behavior.
|
|
287
|
+
*
|
|
288
|
+
* @param options - ScrollIntoView options or boolean for legacy behavior
|
|
289
|
+
* @returns The instance for method chaining
|
|
290
|
+
*
|
|
291
|
+
* @example
|
|
292
|
+
* ```ts
|
|
293
|
+
* $('#section').scrollTo(); // Smooth scroll
|
|
294
|
+
* $('#section').scrollTo({ behavior: 'instant', block: 'start' });
|
|
295
|
+
* ```
|
|
296
|
+
*/
|
|
297
|
+
scrollTo(options: ScrollIntoViewOptions | boolean = { behavior: 'smooth' }): this {
|
|
298
|
+
this.element.scrollIntoView(options);
|
|
299
|
+
return this;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Removes the element from the DOM.
|
|
304
|
+
*
|
|
305
|
+
* @returns The instance for method chaining (though element is now detached)
|
|
306
|
+
*/
|
|
307
|
+
remove(): this {
|
|
308
|
+
this.element.remove();
|
|
309
|
+
return this;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Clears all child nodes from the element.
|
|
314
|
+
*
|
|
315
|
+
* @returns The instance for method chaining
|
|
316
|
+
*/
|
|
317
|
+
empty(): this {
|
|
318
|
+
this.element.innerHTML = '';
|
|
319
|
+
return this;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Clones the element, optionally with all descendants.
|
|
324
|
+
*
|
|
325
|
+
* @param deep - If true, clone all descendants (default: true)
|
|
326
|
+
* @returns A new BQueryElement wrapping the cloned element
|
|
327
|
+
*/
|
|
328
|
+
clone(deep: boolean = true): BQueryElement {
|
|
329
|
+
return new BQueryElement(this.element.cloneNode(deep) as Element);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Finds all descendant elements matching the selector.
|
|
334
|
+
*
|
|
335
|
+
* @param selector - CSS selector to match
|
|
336
|
+
* @returns Array of matching elements
|
|
337
|
+
*/
|
|
338
|
+
find(selector: string): Element[] {
|
|
339
|
+
return Array.from(this.element.querySelectorAll(selector));
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Finds the first descendant element matching the selector.
|
|
344
|
+
*
|
|
345
|
+
* @param selector - CSS selector to match
|
|
346
|
+
* @returns The first matching element or null
|
|
347
|
+
*/
|
|
348
|
+
findOne(selector: string): Element | null {
|
|
349
|
+
return this.element.querySelector(selector);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Finds the closest ancestor matching the selector.
|
|
354
|
+
*
|
|
355
|
+
* @param selector - CSS selector to match
|
|
356
|
+
* @returns The matching ancestor or null
|
|
357
|
+
*/
|
|
358
|
+
closest(selector: string): Element | null {
|
|
359
|
+
return this.element.closest(selector);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Gets the parent element.
|
|
364
|
+
*
|
|
365
|
+
* @returns The parent element or null
|
|
366
|
+
*/
|
|
367
|
+
parent(): Element | null {
|
|
368
|
+
return this.element.parentElement;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Gets all child elements.
|
|
373
|
+
*
|
|
374
|
+
* @returns Array of child elements
|
|
375
|
+
*/
|
|
376
|
+
children(): Element[] {
|
|
377
|
+
return Array.from(this.element.children);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Gets all sibling elements.
|
|
382
|
+
*
|
|
383
|
+
* @returns Array of sibling elements (excluding this element)
|
|
384
|
+
*/
|
|
385
|
+
siblings(): Element[] {
|
|
386
|
+
const parent = this.element.parentElement;
|
|
387
|
+
if (!parent) return [];
|
|
388
|
+
return Array.from(parent.children).filter((child) => child !== this.element);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Gets the next sibling element.
|
|
393
|
+
*
|
|
394
|
+
* @returns The next sibling element or null
|
|
395
|
+
*/
|
|
396
|
+
next(): Element | null {
|
|
397
|
+
return this.element.nextElementSibling;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Gets the previous sibling element.
|
|
402
|
+
*
|
|
403
|
+
* @returns The previous sibling element or null
|
|
404
|
+
*/
|
|
405
|
+
prev(): Element | null {
|
|
406
|
+
return this.element.previousElementSibling;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Adds an event listener.
|
|
411
|
+
*
|
|
412
|
+
* @param event - Event type to listen for
|
|
413
|
+
* @param handler - Event handler function
|
|
414
|
+
* @returns The instance for method chaining
|
|
415
|
+
*/
|
|
416
|
+
on(event: string, handler: EventListenerOrEventListenerObject): this {
|
|
417
|
+
this.element.addEventListener(event, handler);
|
|
418
|
+
return this;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Adds a one-time event listener that removes itself after firing.
|
|
423
|
+
*
|
|
424
|
+
* @param event - Event type to listen for
|
|
425
|
+
* @param handler - Event handler function
|
|
426
|
+
* @returns The instance for method chaining
|
|
427
|
+
*/
|
|
428
|
+
once(event: string, handler: EventListener): this {
|
|
429
|
+
this.element.addEventListener(event, handler, { once: true });
|
|
430
|
+
return this;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Removes an event listener.
|
|
435
|
+
*
|
|
436
|
+
* @param event - Event type
|
|
437
|
+
* @param handler - The handler to remove
|
|
438
|
+
* @returns The instance for method chaining
|
|
439
|
+
*/
|
|
440
|
+
off(event: string, handler: EventListenerOrEventListenerObject): this {
|
|
441
|
+
this.element.removeEventListener(event, handler);
|
|
442
|
+
return this;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Triggers a custom event on the element.
|
|
447
|
+
*
|
|
448
|
+
* @param event - Event type to trigger
|
|
449
|
+
* @param detail - Optional detail data to include with the event
|
|
450
|
+
* @returns The instance for method chaining
|
|
451
|
+
*/
|
|
452
|
+
trigger(event: string, detail?: unknown): this {
|
|
453
|
+
this.element.dispatchEvent(new CustomEvent(event, { detail, bubbles: true, cancelable: true }));
|
|
454
|
+
return this;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Adds a delegated event listener that only triggers for matching descendants.
|
|
459
|
+
* More efficient than adding listeners to many elements individually.
|
|
460
|
+
*
|
|
461
|
+
* Use `undelegate()` to remove the listener later.
|
|
462
|
+
*
|
|
463
|
+
* @param event - Event type to listen for
|
|
464
|
+
* @param selector - CSS selector to match against event targets
|
|
465
|
+
* @param handler - Event handler function, receives the matched element as context
|
|
466
|
+
* @returns The instance for method chaining
|
|
467
|
+
*
|
|
468
|
+
* @example
|
|
469
|
+
* ```ts
|
|
470
|
+
* // Instead of adding listeners to each button:
|
|
471
|
+
* const handler = (e, target) => console.log('Clicked:', target.textContent);
|
|
472
|
+
* $('#list').delegate('click', '.item', handler);
|
|
473
|
+
*
|
|
474
|
+
* // Later, remove the delegated listener:
|
|
475
|
+
* $('#list').undelegate('click', '.item', handler);
|
|
476
|
+
* ```
|
|
477
|
+
*/
|
|
478
|
+
delegate(
|
|
479
|
+
event: string,
|
|
480
|
+
selector: string,
|
|
481
|
+
handler: (event: Event, target: Element) => void
|
|
482
|
+
): this {
|
|
483
|
+
const key = `${event}:${selector}`;
|
|
484
|
+
const wrapper: EventListener = (e: Event) => {
|
|
485
|
+
const target = (e.target as Element).closest(selector);
|
|
486
|
+
if (target && this.element.contains(target)) {
|
|
487
|
+
handler(e, target);
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
// Store the wrapper so it can be removed later
|
|
492
|
+
if (!this.delegatedHandlers.has(key)) {
|
|
493
|
+
this.delegatedHandlers.set(key, new Map());
|
|
494
|
+
}
|
|
495
|
+
this.delegatedHandlers.get(key)!.set(handler, wrapper);
|
|
496
|
+
|
|
497
|
+
this.element.addEventListener(event, wrapper);
|
|
498
|
+
return this;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Removes a delegated event listener previously added with `delegate()`.
|
|
503
|
+
*
|
|
504
|
+
* @param event - Event type that was registered
|
|
505
|
+
* @param selector - CSS selector that was used
|
|
506
|
+
* @param handler - The original handler function passed to delegate()
|
|
507
|
+
* @returns The instance for method chaining
|
|
508
|
+
*
|
|
509
|
+
* @example
|
|
510
|
+
* ```ts
|
|
511
|
+
* const handler = (e, target) => console.log('Clicked:', target.textContent);
|
|
512
|
+
* $('#list').delegate('click', '.item', handler);
|
|
513
|
+
*
|
|
514
|
+
* // Remove the delegated listener:
|
|
515
|
+
* $('#list').undelegate('click', '.item', handler);
|
|
516
|
+
* ```
|
|
517
|
+
*/
|
|
518
|
+
undelegate(
|
|
519
|
+
event: string,
|
|
520
|
+
selector: string,
|
|
521
|
+
handler: (event: Event, target: Element) => void
|
|
522
|
+
): this {
|
|
523
|
+
const key = `${event}:${selector}`;
|
|
524
|
+
const handlers = this.delegatedHandlers.get(key);
|
|
525
|
+
|
|
526
|
+
if (handlers) {
|
|
527
|
+
const wrapper = handlers.get(handler);
|
|
528
|
+
if (wrapper) {
|
|
529
|
+
this.element.removeEventListener(event, wrapper);
|
|
530
|
+
handlers.delete(handler);
|
|
531
|
+
|
|
532
|
+
// Clean up empty maps
|
|
533
|
+
if (handlers.size === 0) {
|
|
534
|
+
this.delegatedHandlers.delete(key);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
return this;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Checks if the element matches a CSS selector.
|
|
544
|
+
*
|
|
545
|
+
* @param selector - CSS selector to match against
|
|
546
|
+
* @returns True if the element matches the selector
|
|
547
|
+
*/
|
|
548
|
+
matches(selector: string): boolean {
|
|
549
|
+
return this.element.matches(selector);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Checks if the element has a specific class.
|
|
554
|
+
*
|
|
555
|
+
* @param className - Class name to check
|
|
556
|
+
* @returns True if the element has the class
|
|
557
|
+
*/
|
|
558
|
+
hasClass(className: string): boolean {
|
|
559
|
+
return this.element.classList.contains(className);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Shows the element by removing the hidden attribute and setting display.
|
|
564
|
+
*
|
|
565
|
+
* @param display - Optional display value (default: '')
|
|
566
|
+
* @returns The instance for method chaining
|
|
567
|
+
*/
|
|
568
|
+
show(display: string = ''): this {
|
|
569
|
+
this.element.removeAttribute('hidden');
|
|
570
|
+
(this.element as HTMLElement).style.display = display;
|
|
571
|
+
return this;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Hides the element by setting display to 'none'.
|
|
576
|
+
*
|
|
577
|
+
* @returns The instance for method chaining
|
|
578
|
+
*/
|
|
579
|
+
hide(): this {
|
|
580
|
+
(this.element as HTMLElement).style.display = 'none';
|
|
581
|
+
return this;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Toggles the visibility of the element.
|
|
586
|
+
*
|
|
587
|
+
* @param force - Optional force show (true) or hide (false)
|
|
588
|
+
* @returns The instance for method chaining
|
|
589
|
+
*/
|
|
590
|
+
toggle(force?: boolean): this {
|
|
591
|
+
const isHidden = (this.element as HTMLElement).style.display === 'none';
|
|
592
|
+
const shouldShow = force ?? isHidden;
|
|
593
|
+
return shouldShow ? this.show() : this.hide();
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Focuses the element.
|
|
598
|
+
*
|
|
599
|
+
* @returns The instance for method chaining
|
|
600
|
+
*/
|
|
601
|
+
focus(): this {
|
|
602
|
+
(this.element as HTMLElement).focus();
|
|
603
|
+
return this;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Blurs (unfocuses) the element.
|
|
608
|
+
*
|
|
609
|
+
* @returns The instance for method chaining
|
|
610
|
+
*/
|
|
611
|
+
blur(): this {
|
|
612
|
+
(this.element as HTMLElement).blur();
|
|
613
|
+
return this;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Gets or sets the value of form elements.
|
|
618
|
+
*
|
|
619
|
+
* @param newValue - Optional value to set
|
|
620
|
+
* @returns The current value when getting, or the instance when setting
|
|
621
|
+
*/
|
|
622
|
+
val(newValue?: string): string | this {
|
|
623
|
+
const input = this.element as HTMLInputElement;
|
|
624
|
+
if (newValue === undefined) {
|
|
625
|
+
return input.value ?? '';
|
|
626
|
+
}
|
|
627
|
+
input.value = newValue;
|
|
628
|
+
return this;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* Serializes form data to a plain object.
|
|
633
|
+
* Only works on form elements; returns empty object for non-forms.
|
|
634
|
+
*
|
|
635
|
+
* @returns Object with form field names as keys and values
|
|
636
|
+
*
|
|
637
|
+
* @example
|
|
638
|
+
* ```ts
|
|
639
|
+
* // For a form with <input name="email" value="test@example.com">
|
|
640
|
+
* const data = $('#myForm').serialize();
|
|
641
|
+
* // { email: 'test@example.com' }
|
|
642
|
+
* ```
|
|
643
|
+
*/
|
|
644
|
+
serialize(): Record<string, string | string[]> {
|
|
645
|
+
const form = this.element as HTMLFormElement;
|
|
646
|
+
if (form.tagName.toLowerCase() !== 'form') {
|
|
647
|
+
return {};
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
const result: Record<string, string | string[]> = {};
|
|
651
|
+
const formData = new FormData(form);
|
|
652
|
+
|
|
653
|
+
for (const [key, value] of formData.entries()) {
|
|
654
|
+
if (typeof value !== 'string') continue; // Skip File objects
|
|
655
|
+
|
|
656
|
+
if (key in result) {
|
|
657
|
+
// Handle multiple values (e.g., checkboxes)
|
|
658
|
+
const existing = result[key];
|
|
659
|
+
if (Array.isArray(existing)) {
|
|
660
|
+
existing.push(value);
|
|
661
|
+
} else {
|
|
662
|
+
result[key] = [existing, value];
|
|
663
|
+
}
|
|
664
|
+
} else {
|
|
665
|
+
result[key] = value;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
return result;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Serializes form data to a URL-encoded query string.
|
|
674
|
+
*
|
|
675
|
+
* @returns URL-encoded string suitable for form submission
|
|
676
|
+
*
|
|
677
|
+
* @example
|
|
678
|
+
* ```ts
|
|
679
|
+
* const queryString = $('#myForm').serializeString();
|
|
680
|
+
* // 'email=test%40example.com&name=John'
|
|
681
|
+
* ```
|
|
682
|
+
*/
|
|
683
|
+
serializeString(): string {
|
|
684
|
+
const form = this.element as HTMLFormElement;
|
|
685
|
+
if (form.tagName.toLowerCase() !== 'form') {
|
|
686
|
+
return '';
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
const formData = new FormData(form);
|
|
690
|
+
const params = new URLSearchParams();
|
|
691
|
+
|
|
692
|
+
for (const [key, value] of formData.entries()) {
|
|
693
|
+
if (typeof value === 'string') {
|
|
694
|
+
params.append(key, value);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
return params.toString();
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* Gets the bounding client rectangle of the element.
|
|
703
|
+
*
|
|
704
|
+
* @returns The element's bounding rectangle
|
|
705
|
+
*/
|
|
706
|
+
rect(): DOMRect {
|
|
707
|
+
return this.element.getBoundingClientRect();
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
/**
|
|
711
|
+
* Gets the offset dimensions (width, height, top, left).
|
|
712
|
+
*
|
|
713
|
+
* @returns Object with offset dimensions
|
|
714
|
+
*/
|
|
715
|
+
offset(): { width: number; height: number; top: number; left: number } {
|
|
716
|
+
const el = this.element as HTMLElement;
|
|
717
|
+
return {
|
|
718
|
+
width: el.offsetWidth,
|
|
719
|
+
height: el.offsetHeight,
|
|
720
|
+
top: el.offsetTop,
|
|
721
|
+
left: el.offsetLeft,
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
/**
|
|
726
|
+
* Internal method to insert content at a specified position.
|
|
727
|
+
* @internal
|
|
728
|
+
*/
|
|
729
|
+
private insertContent(content: string | Element | Element[], position: InsertPosition) {
|
|
730
|
+
if (typeof content === 'string') {
|
|
731
|
+
this.element.insertAdjacentHTML(position, sanitizeHtml(content));
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
const elements = toElementList(content);
|
|
736
|
+
applyAll(elements, (el) => {
|
|
737
|
+
this.element.insertAdjacentElement(position, el);
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
}
|