File Coverage

lib/HTML/Object/DOM/Element.pm
Criterion Covered Total %
statement 517 1446 35.7
branch 197 1184 16.6
condition 87 523 16.6
subroutine 72 172 41.8
pod 103 108 95.3
total 976 3433 28.4


line stmt bran cond sub pod time code
1             ##----------------------------------------------------------------------------
2             ## HTML Object - ~/lib/HTML/Object/DOM/Element.pm
3             ## Version v0.3.0
4             ## Copyright(c) 2022 DEGUEST Pte. Ltd.
5             ## Author: Jacques Deguest <jack@deguest.jp>
6             ## Created 2021/12/13
7             ## Modified 2023/05/07
8             ## All rights reserved
9             ##
10             ##
11             ## This program is free software; you can redistribute it and/or modify it
12             ## under the same terms as Perl itself.
13             ##----------------------------------------------------------------------------
14             package HTML::Object::DOM::Element;
15             BEGIN
16             {
17 29     29   103441 use strict;
  29         63  
  29         827  
18 29     29   153 use warnings;
  29         64  
  29         895  
19 29     29   1359 use HTML::Object::DOM::Node qw( TEXT_NODE COMMENT_NODE );
  29         63  
  29         658  
20 29     29   9617 use parent qw( HTML::Object::DOM::Node );
  29         69  
  29         184  
21 29     29   2034 use vars qw( @EXPORT_OK $VERSION );
  29         59  
  29         1617  
22 29     29   11094 use HTML::Object::Exception;
  29         76  
  29         215  
23 29     29   7715 use Nice::Try;
  29         59  
  29         269  
24 29     29   112423766 use Scalar::Util ();
  29         69  
  29         928  
25 29     29   873 use URI;
  29         4601  
  29         1411  
26 29     29   225 use Want;
  29         59  
  29         3754  
27 29     29   150 our @EXPORT_OK = qw(
28             DOCUMENT_POSITION_IDENTICAL DOCUMENT_POSITION_DISCONNECTED
29             DOCUMENT_POSITION_PRECEDING DOCUMENT_POSITION_FOLLOWING DOCUMENT_POSITION_CONTAINS
30             DOCUMENT_POSITION_CONTAINED_BY DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC
31             );
32 29         591 our $VERSION = 'v0.3.0';
33             };
34              
35 29     29   185 use strict;
  29         68  
  29         749  
36 29     29   155 use warnings;
  29         62  
  29         167179  
37              
38             sub init
39             {
40 346     346 1 3576 my $self = shift( @_ );
41 346         2462 $self->{contentEditable} = 1;
42 346         1032 $self->{_init_strict_use_sub} = 1;
43 346 50       2383 $self->SUPER::init( @_ ) || return( $self->pass_error );
44             # internal trigger, essentially to be triggered when an attribute is being updated
45             # so the TokenList object can be updated
46 346 50 33     2775 $self->{_internal_attribute_callbacks} = {} if( !exists( $self->{_internal_attribute_callbacks} ) || ref( $self->{_internal_attribute_callbacks} ) ne 'HASH' );
47             $self->{_internal_attribute_callbacks}->{class} = sub
48             {
49 0     0   0 my( $this, $val ) = @_;
50 0         0 my $list;
51 0 0       0 return if( !( $list = $this->{_class_list} ) );
52 0         0 $list->update( $val );
53 346         3140 };
54 346         2114 return( $self );
55             }
56              
57             # Note: accessKey -> property
58 0     0 1 0 sub accessKey : lvalue { return( shift->_set_get_property( 'accesskey', @_ ) ); }
59              
60             # Note: accessKeyLabel -> property
61 0     0 1 0 sub accessKeyLabel : lvalue { return( shift->_set_get_property( 'accessKeyLabel', @_ ) ); }
62              
63             sub after
64             {
65 3     3 1 23 my $self = shift( @_ );
66 3 50       15 return( $self ) if( !scalar( @_ ) );
67 3         11 my $parent = $self->parent;
68 3 50       69 return( $self->error( "No parent set for this element, so you cannot set this \"after\" method." ) ) if( !$parent );
69 3         14 my $pos = $parent->children->pos( $self );
70             # If a HTML::Object::DOM::DocumentFragment object is provided, its children are
71             # copied to the list and its own children array is emptied.
72 3         422 my $list = $self->_get_from_list_of_elements_or_html( @_ );
73             $list->foreach(sub
74             {
75 4     4   71 $_->parent( $parent );
76 4         142 $parent->children->splice( $pos + 1, 0, $_ );
77 4         303 $pos++;
78             # Required, because $pos++ as the last execution in this anon sub somehow returns defined and false
79 4         12 return(1);
80 3         32 });
81 3         91 $parent->reset(1);
82 3         32 return( $self );
83             }
84              
85             sub append
86             {
87 6     6 1 1501 my $self = shift( @_ );
88 6 50       25 return( $self ) if( !scalar( @_ ) );
89             # If a HTML::Object::DOM::DocumentFragment object is provided, its children are
90             # copied to the list and its own children array is emptied.
91 6         61 my $list = $self->_get_from_list_of_elements_or_html( @_ );
92 6         33 my $children = $self->children;
93             $list->foreach(sub
94             {
95 7     7   296 $_->parent( $self );
96 7         267 $children->push( $_ );
97 6         774 });
98 6         1422 $self->reset(1);
99 6         32 return( $list );
100             }
101              
102 0     0 0 0 sub assignedSlot { return; }
103              
104 0     0 1 0 sub attachInternals { return; }
105              
106             # Note: attributeStyleMap -> property
107 0     0 1 0 sub attributeStyleMap : lvalue { return( shift->_set_get_property( 'style', @_ ) ); }
108              
109             sub before
110             {
111             my $self = shift( @_ );
112             return( $self ) if( !scalar( @_ ) );
113             my $parent = $self->parent;
114             return( $self->error( "No parent set for this element, so you cannot set this \"before\" method." ) ) if( !$parent );
115             my $pos = $parent->children->pos( $self );
116             $pos--;
117             # If a HTML::Object::DOM::DocumentFragment object is provided, its children are
118             # copied to the list and its own children array is emptied.
119             my $list = $self->_get_from_list_of_elements_or_html( @_ );
120             $list->foreach(sub
121             {
122             $_->parent( $parent );
123             $parent->children->splice( $pos + 1, 0, $_ );
124             $pos++;
125             });
126             $parent->reset(1);
127             return( $self );
128             }
129              
130 2     2 1 34 sub blur { return; }
131              
132             # NOTE: HTML element property read-pnly
133 2     3 1 7 sub childElementCount { return( $_[0]->children->grep(sub{ $_[0]->_isa( $_ => 'HTML::Object::DOM::Element' ) })->length ); }
  2     0   8  
134              
135             # NOTE: HTML element property
136             # <https://developer.mozilla.org/en-US/docs/Web/API/Element/classList>
137             sub classList
138             {
139 2     0 1 37 my $self = shift( @_ );
140 2 50       6 unless( $self->{_class_list} )
141             {
142 2         167 my $classes = $self->attr( 'class' );
143 2         23 require HTML::Object::TokenList;
144 2   0     14 $self->{_class_list} = HTML::Object::TokenList->new( $classes, element => $self, attribute => 'class' ) ||
145             return( $self->pass_error( HTML::Object::TokenList->error ) );
146             }
147 3         45 return( $self->{_class_list} );
148             }
149              
150             # NOTE: Property
151             sub className : lvalue { return( shift->_set_get_callback({
152             get => sub
153             {
154 3     0   91 my $self = shift( @_ );
155 3         263 return( $self->new_scalar( $self->attr( 'class' ) ) );
156             },
157             set => sub
158             {
159 2     0   47 my $self = shift( @_ );
160 2         6 my $arg = shift( @_ );
161 0         0 $self->attr( class => $arg );
162 0         0 $self->reset(1);
163 0         0 return( $self->new_scalar( $arg ) );
164             }
165 0     0 1 0 }, @_ ) ); }
166              
167 0     0 1 0 sub clientHeight { return; }
168              
169 0     0 1 0 sub clientLeft { return; }
170              
171 0     0 1 0 sub clientTop { return; }
172              
173 0     0 1 0 sub clientWidth { return; }
174              
175 0     0 1 0 sub click { return( shift->trigger( 'click' ) ); }
176              
177             # TODO: closest: expand the support for xpath
178             sub closest
179             {
180             my $self = shift( @_ );
181             # Right now, only support a tag name.
182             my $what = shift( @_ ) || return( $self->error( "No value provided to find ancestor." ) );
183             $what = lc( $what );
184             my $lineage = $self->lineage;
185             my $result = $lineage->grep(sub{ $_->tag eq $what });
186             return( $result->first );
187             }
188              
189             # Taken from HTML::TreeBuilder::XPpath
190             sub cmp
191             {
192 37     37 1 135 my( $a, $b ) = @_;
193             # comparison with the root (in $b, or processed in HTML::Object::Root)
194 37 50       351 return( -1 ) if( $b->isa( 'HTML::Object::Root' ) );
195              
196 37 50       181 return(0) if( $a->eid eq $b->eid );
197             # easy cases
198 37 50       509 return( 0 ) if( $a == $b );
199             # a starts after b
200 37 50       281 return( 1 ) if( $a->is_inside( $b ) );
201             # a starts before b
202 40 50       3006 return( -1 ) if( $b->is_inside( $a ) );
203              
204             # lineage does not include the element itself
205 37         1307 my $a_pile = $a->lineage->unshift( $a );
206 36         400 my $b_pile = $b->lineage->unshift( $b );
207             # $a->debug(4);
208            
209             # the 2 elements are not in the same twig
210 36 50       441 unless( $a_pile->last == $b_pile->last )
211             {
212 0 0       0 warnings::warn( "2 nodes not in the same pile: " . ref( $a ) . " - " . ref( $b ) . "\n" ) if( warnings::enabled( 'HTML::Object' ) );
213             # print "a: ", $a->string_value, "\nb: ", $b->string_value, "\n";
214 0         0 return;
215             }
216              
217             # find the first non common ancestors (they are siblings)
218 36         204 my $a_anc = $a_pile->pop;
219 36         2027 my $b_anc = $b_pile->pop;
220              
221 36         1710 while( $a_anc == $b_anc )
222             {
223 106         508 $a_anc = $a_pile->pop;
224 106         5720 $b_anc = $b_pile->pop;
225             }
226              
227 36 50 33     430 if( defined( $a_anc->rank ) && defined( $b_anc->rank ) )
228             {
229 0         0 return( $a_anc->rank <=> $b_anc->rank );
230             }
231             else
232             {
233             # from there move left and right and figure out the order
234 36         28997 my( $a_prev, $a_next, $b_prev, $b_next ) = ( $a_anc, $a_anc, $b_anc, $b_anc );
235 36         66 while()
236             {
237 52   100     306 $a_prev = $a_prev->getPreviousSibling || return( -1 );
238 46 50       11995 return( 1 ) if( $a_prev == $b_anc );
239 46   50     318 $a_next = $a_next->getNextSibling || return( 1 );
240 46 50       241353 return( -1 ) if( $a_next == $b_anc );
241 46   50     272 $b_prev = $b_prev->getPreviousSibling || return( 1 );
242 46 100       11948 return( -1 ) if( $b_prev == $a_next );
243 29   50     159 $b_next = $b_next->getNextSibling || return( -1 );
244 29 100       46798 return( 1 ) if( $b_next == $a_prev );
245             }
246             }
247             }
248              
249             # Note: contentEditable -> property
250 0     4 1 0 sub contentEditable : lvalue { return( shift->_set_get_property( 'contentEditable', @_ ) ); }
251              
252             # NOTE: dataset -> property
253             sub dataset
254             {
255 2     2 1 946 my $self = shift( @_ );
256 2 100       16 return( $self->{_data_map} ) if( $self->{_data_map} );
257 1 50       4 $self->_load_class( 'HTML::Object::ElementDataMap' ) ||
258             return( $self->pass_error );
259 1   50     48 my $map = HTML::Object::ElementDataMap->new( $self ) ||
260             return( $self->pass_error( HTML::Object::ElementDataMap->error ) );
261 1         9 return( $self->{_data_map} = $map );
262             }
263              
264             # Note: dir -> property
265 0     0 1 0 sub dir : lvalue { return( shift->_set_get_property( 'dir', @_ ) ); }
266              
267             # Note: draggable -> property
268 0     0 1 0 sub draggable : lvalue { return( shift->_set_get_property( { attribute => 'draggable', is_boolean => 1 }, @_ ) ); }
269              
270             # Note: enterKeyHint -> property
271 0     0 1 0 sub enterKeyHint : lvalue { return( shift->_set_get_property( 'enterKeyHint', @_ ) ); }
272              
273             sub firstElementChild
274             {
275 1     1 1 3 my $self = shift( @_ );
276 1         6 my $children = $self->children;
277 1         62 my $elem;
278             $children->foreach(sub
279             {
280 4 100   4   76 if( $_->isa( 'HTML::Object::DOM::Element' ) )
281             {
282 1         3 $elem = $_;
283 1         1 return;
284             }
285 3         6 return(1);
286 1         10 });
287 1 50       22 return( $self->new_null ) if( !defined( $elem ) );
288 1         3 return( $elem );
289             }
290              
291 0     0 1 0 sub focus { return; }
292              
293 43     43 1 40859 sub getAttribute { return( shift->attributes->get( shift( @_ ) ) ); }
294              
295             # We return a clone version to be safe, since we rely on this, so if this get messed up
296             # things will go awry
297 0     0 1 0 sub getAttributeNames { return( shift->attributes_sequence->clone ); }
298              
299             sub getAttributeNode
300             {
301 4     4 1 44259 my $self = shift( @_ );
302 4   50     25 my $name = shift( @_ ) || return;
303             # new_null is a nifty method inherited from Module::Generic
304             # It returns the right value based on the caller's expectation
305 4 50       27 return( $self->new_null ) if( !$self->attributes->exists( $name ) );
306 4         2664 my $val = $self->attributes->get( $name );
307 4         2517 my $att = $self->new_attribute( name => $name, value => $val, element => $self );
308 4         16 return( $att );
309             }
310              
311 0     0 1 0 sub getAttributeNodeNS { return; }
312              
313 0     0 1 0 sub getAttributeNS { return; }
314              
315             # Note: method getAttributes is inherited
316              
317             # Note: method getChildNodes is inherited
318              
319             sub getElementById
320             {
321 0     0 1 0 my( $self, $id ) = @_;
322 0 0 0     0 return( $self->error( "No id was provided to get its corresponding element." ) ) if( !defined( $id ) || !CORE::length( $id ) );
323 0         0 return( $self->look_down( id => $id )->first );
324             }
325              
326             sub getElementsByClassName
327             {
328 0     0 1 0 my $self = shift( @_ );
329 0         0 my @args = ();
330 0 0       0 if( scalar( @_ ) == 1 )
331             {
332 0         0 @args = split( /[[:blank:]\h]+/, $_[0] );
333             }
334             else
335             {
336 0         0 @args = @_;
337             }
338 0         0 my $results = $self->new_array;
339 0         0 my $test = $self->new_array( \@args )->unique(1);
340 0         0 my $totalClassRequired = $test->length->scalar;
341             # Nothing to do somehow
342 0 0       0 return( $results ) if( !$totalClassRequired );
343            
344 0         0 my $seen = {};
345 0         0 my $crawl;
346             $crawl = sub
347             {
348 0     0   0 my $kid = shift( @_ );
349             $kid->children->foreach(sub
350             {
351 0         0 my $e = shift( @_ );
352             # Avoid looping
353 0         0 my $addr = Scalar::Util::refaddr( $e );
354 0 0       0 return(1) if( CORE::exists( $seen->{ $addr } ) );
355 0         0 $seen->{ $addr }++;
356 0 0       0 if( $e->attributes->exists( 'class' ) )
357             {
358 0         0 my $val = $self->new_scalar( $e->attributes->get( 'class' ) );
359 0         0 $val->trim( qr/[[:blank:]\h]+/ );
360 0 0       0 if( !$val->is_empty )
361             {
362 0         0 my $classes = $val->split( qr/[[:blank:]\h]+/ );
363 0         0 my $found = 0;
364             $test->foreach(sub
365             {
366 0 0       0 $found++ if( $classes->has( $_ ) );
367 0         0 });
368 0 0       0 $results->push( $e ) if( $found == $totalClassRequired );
369             }
370             }
371 0         0 $crawl->( $e );
372             # Always return true
373 0         0 return(1);
374 0         0 });
375 0         0 };
376 0         0 $crawl->( $self );
377 0         0 return( $results );
378             }
379              
380             sub getElementsByTagName
381             {
382 19     19 1 2492 my $self = shift( @_ );
383 19         51 my $name = shift( @_ );
384 19         88 my $results = $self->new_array;
385             # Nothing to do somehow
386 19 50 33     504 return( $self->error( "No name was provided for getElementsByTagName()" ) ) if( !defined( $name ) || !CORE::length( "$name" ) );
387            
388 19         48 my $seen = {};
389 19         42 my $crawl;
390             $crawl = sub
391             {
392 276     276   515 my $kid = shift( @_ );
393             $kid->children->foreach(sub
394             {
395 257         10564 my $e = shift( @_ );
396             # Avoid looping
397 257         532 my $addr = Scalar::Util::refaddr( $e );
398 257 50       717 return(1) if( CORE::exists( $seen->{ $addr } ) );
399 257         677 $seen->{ $addr }++;
400 257 100       989 if( $e->tag eq $name )
401             {
402 21         17656 $results->push( $e );
403             }
404 257         216280 $crawl->( $e );
405             # Always return true
406 257         21832 return(1);
407 276         984 });
408 19         121 };
409 19         66 $crawl->( $self );
410 19         474 return( $results );
411             }
412              
413             # Credits John Resig
414             # <https://johnresig.com/blog/comparing-document-position/>
415             # Original by PPK quirksmode.org
416             sub getElementsByTagNames
417             {
418 0     0 1 0 my $self = shift( @_ );
419 0         0 my $this;
420 0         0 my $results = $self->new_array;
421 0 0 0     0 if( scalar( @_ ) == 1 && !ref( $_[0] ) )
    0 0        
    0          
422             {
423 0         0 $this = [split( /[[:blank:]\h]+/, $this )];
424             }
425             elsif( scalar( @_ ) == 1 && $self->_is_array( $this ) )
426             {
427             # Good as-is
428             }
429             # list of elements
430             elsif( scalar( @_ ) > 1 )
431             {
432 0         0 $this = [@_];
433             }
434             else
435             {
436 0         0 return( $results );
437             }
438            
439 0         0 my $tags = $self->new_array( $this );
440             $tags->foreach(sub
441             {
442 0     0   0 my $elems = $self->getElementsByTagName( $_ );
443 0 0       0 $results->push( $elems->list ) if( !$elems->is_empty );
444 0         0 });
445 0         0 $results->unique(1);
446 0         0 return( $results );
447             }
448              
449             # sub getFirstChild { return( shift->children->first ); }
450             # Note: method getFirstChild is inherited
451              
452             # Note: method getLastChild is inherited
453              
454             sub getLocalName
455             {
456 0     0 0 0 my $self = shift( @_ );
457 0         0 ( my $name = $self->tag ) =~ s{^.*:}{};
458 0         0 return( $name );
459             }
460              
461 381     381 1 28738 sub getName { return( shift->tag ); }
462              
463             # sub getNextSibling { return( shift->right->first ); }
464             # Note: method getNextSibling is inherited
465              
466             sub getNodePath
467             {
468 0     0 0 0 my $self = shift( @_ );
469 0         0 my $a;
470             my $init;
471 0 0       0 if( @_ )
472             {
473 0         0 $a = shift( @_ );
474             }
475             else
476             {
477 0         0 $a = $self->new_array;
478 0         0 $init = 1;
479             }
480 0 0 0     0 return if( $self->isa( 'HTML::Object::Text' ) || $self->isa( 'HTML::Object::Comment' ) || $self->isa( 'HTML::Object::Declaration' ) );
      0        
481 0         0 my $tag = $self->tag;
482 0         0 my $parent = $self->parent;
483 0 0       0 if( !defined( $parent ) )
484             {
485 0 0 0     0 return( $a ) if( !defined( $tag ) || $tag CORE::eq '_document' );
486 0         0 $a->unshift( $tag );
487 0         0 return( $a );
488             }
489 0         0 my $nth = 0;
490 0         0 my $pos = 0;
491             $parent->children->foreach(sub
492             {
493 0 0   0   0 if( $_->tag CORE::eq $self->tag )
494             {
495 0         0 $nth++;
496 0 0       0 if( $_->eid CORE::eq $self->eid )
497             {
498 0         0 $pos = $nth;
499             }
500             }
501             # Continue to the next one
502 0         0 return( 1 );
503 0         0 });
504 0 0       0 $a->unshift( $nth > 1 ? "${tag}\[${pos}\]" : $tag );
505 0 0       0 return( $parent->getNodePath( $a ) ) unless( $init );
506 0         0 my $xpath = '/' . $a->join( '/' );
507 0         0 return( $xpath );
508             }
509              
510             sub getParentNode
511             {
512 0     0 1 0 my $self = shift( @_ );
513 0   0     0 return( $self->parent || $self->new_root( root => $self ) );
514             }
515              
516             # Note: getPreviousSibling is inherited
517              
518             sub getValue
519             {
520 0     0 0 0 my $self = shift( @_ );
521             # return( $self->text ) if( $self->isCommentNode );
522 0 0       0 return( $self->value ) if( $self->isCommentNode );
523 0         0 return( $self->as_text );
524             }
525              
526 0     0 1 0 sub hasAttribute { return( shift->attributes->has( shift( @_ ) ) ); }
527              
528 0     0 1 0 sub hasAttributes { return( !shift->attributes->is_empty ); }
529              
530             # Note: hidden -> property
531 0     0 1 0 sub hidden : lvalue { return( shift->_set_get_property( { attribute => 'hidden', is_boolean => 1 }, @_ ) ); }
532              
533 0     0 1 0 sub hidePopover { return; }
534              
535             # Note: inert -> property
536 0     0 1 0 sub inert : lvalue { return( shift->_set_get_property( { attribute => 'inert', is_boolean => 1 }, @_ ) ); }
537              
538             sub innerHTML : lvalue { return( shift->_set_get_callback({
539             get => sub
540             {
541 0     0   0 my $self = shift( @_ );
542             # Create a new document, because we want to use the document object as_string function which produce a string of its children, and no need to reproduce it here
543 0         0 my $doc = $self->new_document;
544 0         0 $doc->children( $self->children );
545 0         0 return( $doc->as_string );
546             },
547             set => sub
548             {
549 0     0   0 my $self = shift( @_ );
550 0         0 my $this = shift( @_ );
551 0         0 my $children;
552 0 0 0     0 if( !ref( $this ) ||
    0 0        
      0        
553             ( ref( $this ) && overload::Overloaded( $this ) && overload::Method( $this, '""' ) ) )
554             {
555 0         0 my $p = $self->new_parser;
556 0   0     0 my $res = $p->parse_data( "$this" ) ||
557             die( "Error while parsing html data provided: " . $p->error );
558 0         0 $children = $res->children;
559             }
560             # We are provided with an element, so we set it as our inner html
561             elsif( $self->_is_a( $this => 'HTML::Object::Element' ) )
562             {
563             # If a HTML::Object::DOM::DocumentFragment object is provided, its children are
564             # copied to the list and its own children array is emptied.
565 0 0       0 if( $self->_is_a( $this => 'HTML::Object::DOM::DocumentFragment' ) )
566             {
567 0         0 $children = $this->children->clone;
568 0         0 $this->children->reset;
569             }
570             else
571             {
572 0         0 my $child = $this->clone;
573 0         0 $children = $self->new_array( $child );
574             }
575             }
576             else
577             {
578 0 0       0 die( "I was expecting some html data in replacement of html for this element \"" . $self->tag . "\", but instead got '" . ( CORE::length( $this ) > 1024 ? ( CORE::substr( $this, 0, 1024 ) . '...' ) : $this ) . "'." );
579             }
580            
581             $children->foreach(sub
582             {
583 0         0 $_->parent( $self );
584 0         0 });
585 0         0 $self->children( $children );
586 0         0 $self->reset(1);
587 0         0 return(1);
588             }
589 0     0 1 0 }, @_ ) ); }
590              
591             # Note: innerText -> property
592             sub innerText : lvalue { return( shift->_set_get_callback({
593             get => sub
594             {
595 0     0   0 my $self = shift( @_ );
596             # Create a new document, because we want to use the document object as_string function which produce a string of its children, and no need to reproduce it here
597 0         0 my $txt = $self->as_trimmed_text;
598 0         0 my $obj = $self->new_scalar( \$txt );
599 0         0 return( $obj );
600             },
601             set => sub
602             {
603 0     0   0 my $self = shift( @_ );
604 0         0 my $this = shift( @_ );
605 0         0 my $children;
606             # We are provided with an element, so we set it as our inner html
607 0 0 0     0 if( $self->_is_a( $this => 'HTML::Object::DOM::Text' ) )
    0 0        
      0        
608             {
609 0         0 $children = $self->new_array( $this );
610             }
611             elsif( !ref( $this ) ||
612             ( ref( $this ) && overload::Overloaded( $this ) && overload::Method( $this, '""' ) ) )
613             {
614 0         0 $this =~ s,\n,<br />\n,gs;
615 0   0     0 my $txt = $self->new_text( value => $this ) || die( $self->error );
616 0         0 $children = $self->new_array( $txt );
617             }
618             else
619             {
620 0 0       0 die( "I was expecting some text data in replacement of html for this element \"" . $self->tag . "\", but instead got '" . ( CORE::length( $this ) > 1024 ? ( CORE::substr( $this, 0, 1024 ) . '...' ) : $this ) . "'." );
621             }
622            
623             $children->foreach(sub
624             {
625 0         0 $_->parent( $self );
626 0         0 });
627 0         0 $self->children( $children );
628 0         0 $self->reset(1);
629 0         0 return(1);
630             }
631 0     0 1 0 }, @_ ) ); }
632              
633             # Note: inputMode -> property
634 0     0 1 0 sub inputMode : lvalue { return( shift->_set_get_property( 'inputMode', @_ ) ); }
635              
636             sub insertAdjacentElement
637             {
638 0     0 1 0 my $self = shift( @_ );
639 0         0 my( $pos, $elem ) = @_;
640 0 0 0     0 return( $self->error({
641             message => 'No position was provided',
642             code => 500,
643             class => 'HTML::Object::SyntaxError',
644             }) ) if( !defined( $pos ) || !CORE::length( "$pos" ) );
645             # Return error if the element provided is either undefined , or an empty string
646 0 0 0     0 return( $self->error({
      0        
647             message => "No element was provided.",
648             code => 500,
649             class => 'HTML::Object::TypeError',
650             }) ) if( !defined( $elem ) || ( !ref( $elem ) && !CORE::length( $elem ) ) );
651 0         0 $pos = lc( "$pos" );
652             # Error if the position string provided is of an unknown value.
653 0 0       0 return( $self->error({
654             message => "Position provided \"$pos\" is not a recognised value. Use beforebegin, afterbegin, beforeend or afterend",
655             code => 500,
656             class => 'HTML::Object::SyntaxError',
657             }) ) if( $pos !~ /^(?:beforebegin|afterbegin|beforeend|afterend)$/ );
658             # Error if the element value provided is not an element object.
659 0 0       0 return( $self->error({
660             message => "Element provided (" . overload::StrVal( $elem ) . ") is not an HTML::Object::DOM::Element object.",
661             code => 500,
662             class => 'HTML::Object::TypeError',
663             }) ) if( !$self->_is_a( $elem => 'HTML::Object::DOM::Element' ) );
664 0         0 my $parent = $self->parent;
665 0 0 0     0 return( $self->error({
      0        
666             message => "Current object has no parent, so the provided element cannot be inserted before or after it.",
667             code => 500,
668             class => 'HTML::Object::HierarchyRequestError',
669             }) ) if( !$parent && ( $pos eq 'beforebegin' || $pos eq 'afterend' ) );
670 0 0       0 if( $pos eq 'beforebegin' )
    0          
    0          
    0          
671             {
672 0         0 my $offset = $parent->children->pos( $self );
673 0 0       0 if( !defined( $offset ) )
674             {
675 0         0 return( $self->error({
676             message => "The current element (" . overload::StrVal( $self ) . ") could not be found in its parent element (" . overload::StrVal( $parent ) . ") whose tag is \"" . $parent->tag . "\".",
677             code => 500,
678             class => 'HTML::Object::HierarchyRequestError',
679             }) );
680             }
681             else
682             {
683 0         0 $parent->splice( $offset, 0, $elem );
684             }
685             }
686             elsif( $pos eq 'beforeend' )
687             {
688 0         0 $self->children->push( $elem );
689             }
690             elsif( $pos eq 'afterbegin' )
691             {
692 0         0 $self->children->unshift( $elem );
693             }
694             elsif( $pos eq 'afterend' )
695             {
696 0         0 my $offset = $parent->children->pos( $self );
697 0 0       0 if( !defined( $offset ) )
698             {
699 0         0 return( $self->error({
700             message => "The current element (" . overload::StrVal( $self ) . ") could not be found in its parent element (" . overload::StrVal( $parent ) . ") whose tag is \"" . $parent->tag . "\".",
701             code => 500,
702             class => 'HTML::Object::HierarchyRequestError',
703             }) );
704             }
705 0         0 $parent->splice( ++$offset, 0, $elem );
706             }
707 0         0 return( $elem );
708             }
709              
710             sub insertAdjacentHTML
711             {
712 0     0 1 0 my $self = shift( @_ );
713 0         0 my( $pos, $html ) = @_;
714 0 0 0     0 return( $self->error({
715             message => 'No position was provided',
716             code => 500,
717             class => 'HTML::Object::SyntaxError',
718             }) ) if( !defined( $pos ) || !CORE::length( "$pos" ) );
719             # Return error if the element provided is either undefined , or an empty string
720 0 0 0     0 return( $self->error({
721             message => "No html string was provided to insert.",
722             code => 500,
723             class => 'HTML::Object::TypeError',
724             }) ) if( !defined( $html ) || !CORE::length( "$html" ) );
725 0 0 0     0 return( $self->error({
726             message => "A reference (" . ref( $html ) . ") was provided instead of an HTML string.",
727             code => 500,
728             class => 'HTML::Object::TypeError',
729             }) ) if( ref( $html ) && !overload::Method( $html, '""' ) );
730 0         0 $html = "$html";
731 0   0     0 my $p = $self->new_parser || return( $self->pass_error );
732 0   0     0 my $doc = $p->parse_data( $html ) || return( $self->pass_error( $p->error ) );
733 0         0 my $parent = $self->parent;
734 0 0 0     0 return( $self->error({
      0        
735             message => "Current object has no parent, so the provided html nodes cannot be inserted before or after it.",
736             code => 500,
737             class => 'HTML::Object::HierarchyRequestError',
738             }) ) if( !$parent && ( $pos eq 'beforebegin' || $pos eq 'afterend' ) );
739 0 0       0 if( $pos eq 'beforebegin' )
    0          
    0          
    0          
740             {
741 0         0 my $offset = $parent->children->pos( $self );
742 0 0       0 if( !defined( $offset ) )
743             {
744 0         0 return( $self->error({
745             message => "The current element (" . overload::StrVal( $self ) . ") could not be found in its parent element (" . overload::StrVal( $parent ) . ") whose tag is \"" . $parent->tag . "\".",
746             code => 500,
747             class => 'HTML::Object::HierarchyRequestError',
748             }) );
749             }
750             $doc->children->foreach(sub
751             {
752 0     0   0 my $elem = shift( @_ );
753 0         0 $elem->parent( $parent );
754 0         0 $parent->children->splice( $offset, 0, $elem );
755 0         0 $offset++;
756 0         0 });
757             }
758             elsif( $pos eq 'beforeend' )
759             {
760             $doc->children->foreach(sub
761             {
762 0     0   0 my $elem = shift( @_ );
763 0         0 $elem->parent( $self );
764 0         0 $self->children->push( $elem );
765 0         0 });
766             }
767             elsif( $pos eq 'afterbegin' )
768             {
769 0         0 my $offset = -1;
770             $doc->children->foreach(sub
771             {
772 0     0   0 my $elem = shift( @_ );
773 0         0 $elem->parent( $self );
774 0         0 $self->children->splice( ++$offset, 0, $elem );
775 0         0 });
776             # $self->children->unshift( $elem );
777             }
778             elsif( $pos eq 'afterend' )
779             {
780 0         0 my $offset = $parent->children->pos( $self );
781 0 0       0 if( !defined( $offset ) )
782             {
783 0         0 return( $self->error({
784             message => "The current element (" . overload::StrVal( $self ) . ") could not be found in its parent element (" . overload::StrVal( $parent ) . ") whose tag is \"" . $parent->tag . "\".",
785             code => 500,
786             class => 'HTML::Object::HierarchyRequestError',
787             }) );
788             }
789             $doc->children->foreach(sub
790             {
791 0     0   0 my $elem = shift( @_ );
792 0         0 $elem->parent( $parent );
793 0         0 $parent->children->splice( ++$offset, 0, $elem );
794 0         0 });
795             }
796 0         0 return( $doc->children );
797             }
798              
799             sub insertAdjacentText
800             {
801 0     0 1 0 my $self = shift( @_ );
802 0         0 my $pos = shift( @_ );
803 0 0 0     0 return( $self->error({
804             message => 'No position was provided',
805             code => 500,
806             class => 'HTML::Object::SyntaxError',
807             }) ) if( !defined( $pos ) || !CORE::length( "$pos" ) );
808 0         0 my $text;
809 0 0 0     0 if( !scalar( @_ ) ||
      0        
      0        
      0        
810             ( scalar( @_ ) == 1 && !defined( $_[0] ) ) ||
811             ( scalar( @_ ) > 1 && !CORE::length( $text = join( '', @_ ) ) ) )
812             {
813 0         0 return( $self->error({
814             message => "No text was provided.",
815             code => 500,
816             class => 'HTML::Object::TypeError',
817             }) );
818             }
819 0 0 0     0 return( $self->error({
820             message => "A reference (" . ref( $text ) . ") was provided instead of an text string.",
821             code => 500,
822             class => 'HTML::Object::TypeError',
823             }) ) if( ref( $text ) && !overload::Method( $text, '""' ) );
824 0         0 my $node = $self->new_text( value => "$text" );
825 0         0 my $parent = $self->parent;
826 0 0 0     0 return( $self->error({
      0        
827             message => "Current object has no parent, so the provided text cannot be inserted before or after it.",
828             code => 500,
829             class => 'HTML::Object::HierarchyRequestError',
830             }) ) if( !$parent && ( $pos eq 'beforebegin' || $pos eq 'afterend' ) );
831 0 0       0 if( $pos eq 'beforebegin' )
    0          
    0          
    0          
832             {
833 0         0 my $offset = $parent->children->pos( $self );
834 0 0       0 if( !defined( $offset ) )
835             {
836 0         0 return( $self->error({
837             message => "The current element (" . overload::StrVal( $self ) . ") could not be found in its parent element (" . overload::StrVal( $parent ) . ") whose tag is \"" . $parent->tag . "\".",
838             code => 500,
839             class => 'HTML::Object::HierarchyRequestError',
840             }) );
841             }
842             else
843             {
844 0         0 $parent->splice( $offset, 0, $node );
845             }
846             }
847             elsif( $pos eq 'beforeend' )
848             {
849 0         0 $self->children->push( $node );
850             }
851             elsif( $pos eq 'afterbegin' )
852             {
853 0         0 $self->children->unshift( $node );
854             }
855             elsif( $pos eq 'afterend' )
856             {
857 0         0 my $offset = $parent->children->pos( $self );
858 0 0       0 if( !defined( $offset ) )
859             {
860 0         0 return( $self->error({
861             message => "The current element (" . overload::StrVal( $self ) . ") could not be found in its parent element (" . overload::StrVal( $parent ) . ") whose tag is \"" . $parent->tag . "\".",
862             code => 500,
863             class => 'HTML::Object::HierarchyRequestError',
864             }) );
865             }
866 0         0 $parent->splice( ++$offset, 0, $node );
867             }
868 0         0 return( $node );
869             }
870              
871             sub is_inside
872             {
873 72     72 1 140 my $self = shift( @_ );
874 72 50       204 return( 0 ) if( !scalar( @_ ) );
875 72         160 my @elems = @_;
876 72         138 my @literals = ();
877 72         249 for( my $i = 0; $i < scalar( @elems ); $i++ )
878             {
879 72 50 33     380 return( $self->error( "The element provided (", overload::StrVal( $elems[$i] ), ") is not an HTML::Object::Element object." ) ) if( ref( $elems[$i] ) && ( !$self->_is_object( $elems[$i] ) || !$elems[$i]->isa( 'HTML::Object::Element' ) ) );
      33        
880 72 50       1171 push( @literals, splice( @elems, $i, 1 ) ) if( !ref( $elems[$i] ) );
881             }
882             # We need to ensure the literals provided, if any, are in lowercase
883             # @$lit{ @literals } = (1) x scalar( @literals );
884 72         188 my $lit = +{ map( lc( $_ ), @literals ) };
885 72         149 my $obj = +{ map{ $_->eid => 1 } @elems };
  72         203  
886 72         141 my $parent = $self;
887             # Check if ourself for any of our parent are a match of any of the element given
888 72         200 while( $parent )
889             {
890 284 50 33     5519 return( 1 ) if( exists( $obj->{ $parent->eid } ) || exists( $lit->{ $parent->tag } ) );
891 284         256592 $parent = $parent->parent;
892             }
893 72         1708 return( 0 );
894             }
895              
896 1     1 1 664 sub isAttributeNode { return(0); }
897              
898 1 50   1 1 5 sub isCommentNode { return( shift->tag CORE::eq '_comment' ? 1 : 0 ); }
899              
900             sub isContentEditable
901             {
902 0     0 1 0 my $self = shift( @_ );
903 0 0       0 return( $self->contentEditable ? $self->true : $self->false );
904             }
905              
906 378 50   378 1 1325 sub isElementNode { return( shift->tag->substr( 0, 1 ) CORE::eq '_' ? 0 : 1 ); }
907              
908 1     1 1 27 sub isNamespaceNode { return(0); }
909              
910 1     1 1 5 sub isPINode { return(0); }
911              
912 1 50   1 1 5 sub isProcessingInstructionNode { return( shift->tag CORE::eq '_pi' ? 1 : 0 ); }
913              
914 1 50   1 1 5 sub isTextNode { return( shift->tag CORE::eq '_text' ? 1 : 0 ); }
915              
916             # Note: lang -> property
917 0     0 1 0 sub lang : lvalue { return( shift->_set_get_property( 'lang', @_ ) ); }
918              
919             sub lastElementChild
920             {
921 1     1 1 3 my $self = shift( @_ );
922 1         5 my $children = $self->children;
923 1         63 my $elem;
924             $children->reverse->foreach(sub
925             {
926 4 100   4   161 if( $_->isa( 'HTML::Object::DOM::Element' ) )
927             {
928 1         3 $elem = $_;
929 1         2 return;
930             }
931 3         18 return(1);
932 1         6 });
933 1 50       22 return( $self->new_null ) if( !defined( $elem ) );
934 1         11 return( $elem );
935             }
936              
937 0     0 1 0 sub localName { return( shift->getName ); }
938              
939             sub matches
940             {
941             my $self = shift( @_ );
942             my $selector = shift( @_ );
943             my $opts = $self->_get_args_as_hash( @_ );
944             my $params = {};
945             # The only supported parameter by HTML::Selector::XPath
946             $params->{root} = CORE::delete( $opts->{root} ) if( CORE::exists( $opts->{root} ) );
947             if( !$self->{_xp} )
948             {
949             $self->_load_class( 'HTML::Object::XPath' ) ||
950             return( $self->pass_error );
951             my $xp = HTML::Object::XPath->new;
952             $self->{_xp} = $xp;
953             }
954             $self->_load_class( 'HTML::Selector::XPath', { version => '0.20' } ) ||
955             return( $self->pass_error );
956             my $xpath;
957             try
958             {
959             my $sel = HTML::Selector::XPath->new( $selector, %$params );
960             $xpath = $sel->to_xpath( %$params );
961             }
962             catch( $e )
963             {
964             return( $self->error( "Error trying to get the xpath value for selector \"$selector\": $e" ) );
965 29     29   254 }
  29         78  
  29         129282  
966             my $xp = $self->{_xp};
967             $self->message( 4, "Calling xp->matches for xpath '$xpath' with context '", $self->as_string, "'" );
968             return( $xp->matches( $self, $xpath, $self ) );
969             }
970              
971 3     3 1 724 sub namespaceURI { return; }
972              
973             sub new_attribute
974             {
975             my $self = shift( @_ );
976             $self->_load_class( 'HTML::Object::DOM::Attribute' ) || return( $self->pass_error );
977             my $att = HTML::Object::DOM::Attribute->new( @_ ) ||
978             return( $self->pass_error( HTML::Object::DOM::Attribute->error ) );
979             return( $att );
980             }
981              
982             sub new_closing
983             {
984 79     79 1 38917 my $self = shift( @_ );
985 79 50       537 $self->_load_class( 'HTML::Object::DOM::Closing' ) || return( $self->pass_error );
986 79   33     4452 my $e = HTML::Object::DOM::Closing->new( @_ ) ||
987             return( $self->pass_error( HTML::Object::DOM::Closing->error ) );
988 79         976 return( $e );
989             }
990              
991             sub new_collection
992             {
993             my $self = shift( @_ );
994             $self->_load_class( 'HTML::Object::DOM::Collection' ) || return( $self->pass_error );
995             my $e = HTML::Object::DOM::Collection->new( @_ ) ||
996             return( $self->pass_error( HTML::Object::DOM::Collection->error ) );
997             return( $e );
998             }
999              
1000             sub new_comment
1001             {
1002 7     4 1 25 my $self = shift( @_ );
1003 7 50       44 $self->_load_class( 'HTML::Object::DOM::Comment' ) || return( $self->pass_error );
1004 7   50     661 my $e = HTML::Object::DOM::Comment->new( @_ ) ||
1005             return( $self->pass_error( HTML::Object::DOM::Comment->error ) );
1006 7         44 return( $e );
1007             }
1008              
1009             sub new_document
1010             {
1011 3     0 1 25 my $self = shift( @_ );
1012 3 50       3732 $self->_load_class( 'HTML::Object::DOM::Document' ) || return( $self->pass_error );
1013 3   33     11 my $e = HTML::Object::DOM::Document->new( debug => $self->debug ) ||
1014             return( $self->pass_error( HTML::Object::DOM::Document->error ) );
1015 3         7 return( $e );
1016             }
1017              
1018             sub new_element
1019             {
1020 3     0 1 7 my $self = shift( @_ );
1021 3   0     16 my $tag = shift( @_ ) || return( $self->error( "No tag was provided to create an element." ) );
1022 0   0     0 my $dict = HTML::Object->get_definition( $tag ) || return( $self->pass_error( HTML::Object->error ) );
1023             my $e = HTML::Object::DOM::Element->new({
1024             is_empty => $dict->{is_empty},
1025             tag => $dict->{tag},
1026 3   33     6 debug => $self->debug,
1027             }) || return( $self->pass_error( HTML::Object::DOM::Element->error ) );
1028 3         12 return( $e );
1029             }
1030              
1031             sub new_nodelist
1032             {
1033 3     0 1 7 my $self = shift( @_ );
1034 3 50       22 $self->_load_class( 'HTML::Object::DOM::NodeList' ) || return( $self->pass_error );
1035 3   33     7 my $list = HTML::Object::DOM::NodeList->new( @_ ) ||
1036             return( $self->pass_error( HTML::Object::DOM::NodeList->error ) );
1037 3         23 return( $list );
1038             }
1039              
1040             sub new_parser
1041             {
1042             my $self = shift( @_ );
1043             $self->_load_class( 'HTML::Object::DOM' ) || return( $self->pass_error );
1044             my $p = HTML::Object::DOM->new( debug => $self->debug ) ||
1045             return( $self->pass_error( HTML::Object::DOM->error ) );
1046             return( $p );
1047             }
1048              
1049             sub new_space
1050             {
1051 8     5 1 67 my $self = shift( @_ );
1052 8 50       389 $self->_load_class( 'HTML::Object::DOM::Space' ) || return( $self->pass_error );
1053 8   66     316 my $e = HTML::Object::DOM::Space->new( @_ ) ||
1054             return( $self->pass_error( HTML::Object::DOM::Space->error ) );
1055 8         58 return( $e );
1056             }
1057              
1058             sub new_text
1059             {
1060 17     14 1 292 my $self = shift( @_ );
1061 17 50       101 $self->_load_class( 'HTML::Object::DOM::Text' ) || return( $self->pass_error );
1062 17   33     830 my $e = HTML::Object::DOM::Text->new( @_ ) ||
1063             return( $self->pass_error( HTML::Object::DOM::Text->error ) );
1064 17         210 return( $e );
1065             }
1066              
1067             sub nextElementSibling
1068             {
1069 1     1 1 727 my $self = shift( @_ );
1070 1         12 my $all = $self->right;
1071 1         59770 for( my $i = 0; $i < scalar( @$all ); $i++ )
1072             {
1073 2 100       202 return( $all->[$i] ) if( $self->_is_a( $all->[$i] => 'HTML::Object::DOM::Element' ) );
1074             }
1075 0         0 return( $self->new_null );
1076             }
1077              
1078             # Note: noModule -> property
1079 0     0 1 0 sub noModule : lvalue { return( shift->_set_get_property( { attribute => 'noModule', is_boolean => 1 }, @_ ) ); }
1080              
1081             # Note: nonce -> property
1082 0     0 1 0 sub nonce : lvalue { return( shift->_set_get_property( 'nonce', @_ ) ); }
1083              
1084             # Note: offsetHeight -> property
1085 0     0 1 0 sub offsetHeight : lvalue { return( shift->_set_get_property( 'offsetheight', @_ ) ); }
1086              
1087             # Note: offsetLeft -> property
1088 0     0 1 0 sub offsetLeft : lvalue { return( shift->_set_get_property( 'offsetleft', @_ ) ); }
1089              
1090             # Note: offsetParent -> property
1091 0     0 1 0 sub offsetParent { return( shift->parent( @_ ) ); }
1092              
1093             # Note: offsetTop -> property
1094 0     0 1 0 sub offsetTop : lvalue { return( shift->_set_get_property( 'offsettop', @_ ) ); }
1095              
1096             # Note: offsetWidth -> property
1097 0     0 1 0 sub offsetWidth : lvalue { return( shift->_set_get_property( 'offsetwidth', @_ ) ); }
1098              
1099 0     0 0 0 sub onerror : lvalue { return( shift->_set_get_code( '_error_handler', @_ ) ); }
1100              
1101             # Note: Property
1102             sub outerHTML : lvalue { return( shift->_set_get_callback({
1103             get => sub
1104             {
1105 6     6   3867 my $self = shift( @_ );
1106 6         52 return( $self->as_string );
1107             },
1108             set => sub
1109             {
1110 2     2   1106 my $self = shift( @_ );
1111 5         22 my $this = shift( @_ );
1112 2         10 my $children;
1113             my $pos;
1114 5         14 my $parent = $self->parent;
1115 2 50       42 $pos = $parent->children->pos( $self ) if( $parent );
1116 2         190 my $dummy;
1117 5 100 33     37 if( !ref( $this ) ||
    100 33        
      33        
1118             ( ref( $this ) && overload::Overloaded( $this ) && overload::Method( $this, '""' ) ) )
1119             {
1120             # User provided an empty string, so we just remove the element
1121 3 0       19 if( !CORE::length( $this ) )
1122             {
1123 3 0       9 if( defined( $pos ) )
1124             {
1125 3         12 $parent->children->splice( $pos, 0 );
1126             # If this element has a closing tag in the dom, we remove it too
1127 0 0       0 if( my $close = $self->close_tag )
1128             {
1129 0         0 $parent->children->remove( $close );
1130 0         0 $close->parent( undef );
1131             }
1132 0         0 $self->parent( undef );
1133 0         0 $self->parent->reset(1);
1134 0         0 $dummy = 1;
1135             }
1136             # Fallback
1137             else
1138             {
1139 0         0 $dummy = 0;
1140             }
1141 0         0 return( $dummy );
1142             }
1143             else
1144             {
1145 0         0 my $p = $self->new_parser;
1146 0   0     0 my $res = $p->parse_data( "$this" ) ||
1147             return( $self->error( "Error while parsing html data provided: ", $p->error ) );
1148 0         0 $children = $res->children;
1149 0 0 0     0 if( !$children->is_empty && defined( $pos ) )
1150             {
1151             $children->foreach(sub
1152             {
1153 0         0 $_->parent( $parent );
1154 0         0 });
1155 0         0 $parent->children->splice( $pos, 1, $children->list );
1156             # If this element has a closing tag in the dom, we remove it too
1157 0 0       0 if( my $close = $self->close_tag )
1158             {
1159 0         0 $parent->children->remove( $close );
1160 0         0 $close->parent( undef );
1161             }
1162 0         0 $parent->reset(1);
1163 0         0 $dummy = 1;
1164             }
1165             else
1166             {
1167 0         0 $dummy = 0;
1168             }
1169 0         0 return( $dummy );
1170             }
1171             }
1172             # We are provided with an element, so we set it as our inner html
1173             elsif( $self->_is_a( $this => 'HTML::Object::Element' ) )
1174             {
1175             # If a HTML::Object::DOM::DocumentFragment object is provided, its children are
1176             # copied to the list and its own children array is emptied.
1177 2 50       305 if( $self->_is_a( $this => 'HTML::Object::DOM::DocumentFragment' ) )
1178             {
1179 0         0 my $copy = $this->children->clone;
1180 0         0 $this->children->reset;
1181 0 0       0 if( defined( $pos ) )
1182             {
1183 0         0 $parent->children->splice( $pos, 1, $copy->list );
1184             $copy->children->foreach(sub
1185             {
1186 0         0 $_->parent( $parent );
1187 0         0 });
1188             # The element itself is being replace, so we remove out own parent
1189 0         0 $self->parent->reset(1);
1190 0         0 $self->parent( undef );
1191             # If this element has a closing tag in the dom, we remove it too
1192 0 0       0 if( my $close = $self->close_tag )
1193             {
1194 0         0 $parent->children->remove( $close );
1195 3         13 $close->parent( undef );
1196             }
1197 0         0 $dummy = 1;
1198             }
1199             else
1200             {
1201 3         170 $dummy = 0;
1202             }
1203             }
1204             else
1205             {
1206 2         70 my $child = $this->clone;
1207 2 50       24 if( defined( $pos ) )
1208             {
1209 2         11 $parent->children->splice( $pos, 1, $child );
1210             # Add the closing tag if any
1211 2 50       200 if( my $close = $child->close_tag )
1212             {
1213 2         202 $parent->children->splice( $pos + 1, 0, $close );
1214             # $parent->children->splice( 2, 0, $close );
1215             }
1216 2         168 $child->parent( $parent );
1217             # The element itself is being replace, so we remove out own parent
1218 2         76 $self->parent->reset(1);
1219 2         7 $self->parent( undef );
1220             # If this element has a closing tag in the dom, we remove it too
1221 2 50       41 if( my $close = $self->close_tag )
1222             {
1223 2         53 $parent->children->remove( $close );
1224 2         1789 $close->parent( undef );
1225             }
1226 2         51 $dummy = 1;
1227             }
1228             else
1229             {
1230 0         0 $dummy = 0;
1231             }
1232             }
1233 2         7 return( $dummy );
1234             }
1235             else
1236             {
1237 0 50       0 die( "I was expecting some html data in replacement of html for this element \"" . $self->tag . "\", but instead got '" . ( CORE::length( $this ) > 1024 ? ( CORE::substr( $this, 0, 1024 ) . '...' ) : $this ) . "'." );
1238             }
1239             }
1240 8     8 1 1219 }, @_ ) ); }
1241              
1242             # Note: outerText -> property
1243             sub outerText : lvalue { return( shift->_set_get_callback({
1244             get => sub
1245             {
1246 0     0   0 my $self = shift( @_ );
1247             # Create a new document, because we want to use the document object as_string function which produce a string of its children, and no need to reproduce it here
1248 0         0 my $txt = $self->as_trimmed_text;
1249 0         0 my $obj = $self->new_scalar( \$txt );
1250 3         20 return( $obj );
1251             },
1252             set => sub
1253             {
1254 0     0   0 my $self = shift( @_ );
1255 0         0 my $this = shift( @_ );
1256 0         0 my $element;
1257             # We are provided with an element, so we set it as our inner html
1258 0 50 33     0 if( $self->_is_a( $this => 'HTML::Object::DOM::Text' ) )
    0 33        
      0        
1259             {
1260 3         12 $element = $this->clone;
1261             }
1262             elsif( !ref( $this ) ||
1263             ( ref( $this ) && overload::Overloaded( $this ) && overload::Method( $this, '""' ) ) )
1264             {
1265 3         10 $this =~ s,\n,<br />\n,gs;
1266 3   0     43 $element = $self->new_text( value => $this ) || die( $self->error );
1267             }
1268             else
1269             {
1270 3 50       241 die( "I was expecting some text data in replacement of html for this element \"" . $self->tag . "\", but instead got '" . ( CORE::length( $this ) > 1024 ? ( CORE::substr( $this, 0, 1024 ) . '...' ) : $this ) . "'." );
1271             }
1272            
1273 0         0 my $parent = $self->parent;
1274 0         0 my $pos = $parent->children->pos( $self );
1275 0 0       0 if( !defined( $pos ) )
1276             {
1277 0         0 die( "Unable to find the current element among its parent's children." );
1278             }
1279            
1280 0         0 $element->parent( $parent );
1281 0         0 $parent->splice( $pos, 1, $element );
1282 0         0 $self->parent( undef() );
1283 0         0 $parent->reset(1);
1284 0         0 my $dummy = 'dummy';
1285 0         0 return( $dummy );
1286             }
1287 0     0 1 0 }, @_ ) ); }
1288              
1289             # Note: popover -> property
1290             sub popover : lvalue { return( shift->_set_get_callback({
1291             get => sub
1292             {
1293 0     0   0 my $self = shift( @_ );
1294 0         0 my $val = $self->_set_get_property( 'popover' );
1295 0 0       0 return( $val ) if( defined( $val ) );
1296 0         0 return( $self->root->_set_get_property( 'popover' ) );
1297             },
1298             set => sub
1299             {
1300 0     0   0 my $self = shift( @_ );
1301 0         0 my $arg = shift( @_ );
1302 0         0 $self->_set_get_property( 'popover', $arg );
1303 0         0 return( $arg );
1304             }
1305 0     0 1 0 }, @_ ) ); }
1306              
1307 0     0 1 0 sub prefix { return; }
1308              
1309             sub prepend
1310             {
1311             my $self = shift( @_ );
1312             return( $self->error({
1313             message => "No data to prepend was provided.",
1314             code => 500,
1315             class => 'HTML::Object::SyntaxError',
1316             }) ) if( !scalar( @_ ) );
1317             # If a HTML::Object::DOM::DocumentFragment object is provided, its children are
1318             # copied to the list and its own children array is emptied.
1319             my $list = $self->_get_from_list_of_elements_or_html( @_ );
1320             my $children = $self->children;
1321             my $pos = -1;
1322             $list->foreach(sub
1323             {
1324             $_->parent( $self );
1325             $children->splice( ++$pos, 0, $_ );
1326             });
1327             $self->reset(1);
1328             return( $list );
1329             }
1330              
1331             sub previousElementSibling
1332             {
1333 1     1 1 12 my $self = shift( @_ );
1334 1         4 my $all = $self->left->reverse;
1335 1         6 for( my $i = 0; $i < scalar( @$all ); $i++ )
1336             {
1337 1 50       4 return( $all->[$i] ) if( $self->_is_a( $all->[$i] => 'HTML::Object::DOM::Element' ) );
1338             }
1339 1         43 return( $self->new_null );
1340             }
1341              
1342             # Note: properties -> property experimental
1343 1     1 1 6 sub properties { return( shift->new_array ); }
1344              
1345             sub querySelector
1346             {
1347 1     0 1 12 my $self = shift( @_ );
1348 1         25 my @sels = @_;
1349 1 0       221 return( $self->error({
1350             message => "No CSS selector was provided to query.",
1351             code => 500,
1352             class => 'HTML::Object::SyntaxError',
1353             }) ) if( !scalar( @sels ) );
1354            
1355 1         2 foreach my $sel ( @sels )
1356             {
1357 0   0     0 my $results = $self->find( $sel, { root => '.' } ) ||
1358             return( $self->pass_error({ class => 'HTML::Object::SyntaxError' }) );
1359 0 0       0 return( $results->first ) if( !$results->is_empty );
1360             }
1361 0         0 return( $self->new_null );
1362             }
1363              
1364             sub querySelectorAll
1365             {
1366 0     0 1 0 my $self = shift( @_ );
1367 0         0 my @sels = @_;
1368 0 0       0 return( $self->error({
1369             message => "No CSS selector was provided to query.",
1370             code => 500,
1371             class => 'HTML::Object::SyntaxError',
1372             }) ) if( !scalar( @sels ) );
1373            
1374 0         0 my $results = $self->new_array;
1375 0         0 foreach my $sel ( @sels )
1376             {
1377 0   0     0 my $elems = $self->find( $sel, { root => './' } ) ||
1378             return( $self->pass_error({ class => 'HTML::Object::SyntaxError' }) );
1379 0 0       0 $results->push( $elems->list ) if( !$elems->is_empty );
1380             }
1381 0         0 $results->unique(1);
1382 0         0 return( $results );
1383             }
1384              
1385             sub remove
1386             {
1387             my $self = shift( @_ );
1388             my $parent = $self->parent;
1389             return( $self->error({
1390             message => "This element has no parent, and thus cannot be removed.",
1391             code => 500,
1392             class => 'HTML::Object::HierarchyRequestError',
1393             }) ) if( !$parent );
1394             my $pos = $parent->children->pos( $self );
1395             return( $self->error({
1396             message => "This element could not be found among its parent's children.",
1397             code => 500,
1398             class => 'HTML::Object::HierarchyRequestError',
1399             }) ) if( !defined( $pos ) );
1400             $parent->children->splice( $pos, 1 );
1401             $parent->reset(1);
1402             return( $self->true );
1403             }
1404              
1405             sub removeAttribute
1406             {
1407 3     3 1 58 my $self = shift( @_ );
1408 3         15 my $name = shift( @_ );
1409 3 50 0     68 return( $self->error({
1410             message => "No attribute name was provided.",
1411             code => 500,
1412             class => 'HTML::Object::SyntaxError',
1413             }) ) if( !defined( $name ) || !CORE::length( $name ) );
1414 3 50       10 if( $self->attributes->has( $name ) )
1415             {
1416 3         255 $self->attributes->remove( $name );
1417 3         9 $self->attributes_sequence->remove( $name );
1418 3         389 return( $self );
1419             }
1420 3         54 return;
1421             }
1422              
1423             sub removeAttributeNode
1424             {
1425 0     0 1 0 my $self = shift( @_ );
1426 0   0     0 my $node = shift( @_ ) || return( $self->error({
1427             message => "No attribute node was provided to remove.",
1428             code => 500,
1429             class => 'HTML::Object::SyntaxError',
1430             }) );
1431 0 0       0 return( $self->error({
1432             message => "Object provided is not an attribute node.",
1433             code => 500,
1434             class => 'HTML::Object::SyntaxError',
1435             }) ) if( !$self->_is_a( $node => 'HTML::Object::DOM::Attribute' ) );
1436 0         0 my $name = $node->name;
1437 0 0       0 return( $self->error({
1438             message => "Attribute node provided has no name value.",
1439             code => 500,
1440             class => 'HTML::Object::SyntaxError',
1441             }) ) if( $name->is_empty );
1442 0         0 return( $self->removeAttribute( $name ) );
1443             }
1444              
1445 0     0 1 0 sub removeAttributeNS { return; }
1446              
1447             sub replaceChildren
1448             {
1449 0     0 1 0 my $self = shift( @_ );
1450 0         0 my $results = $self->new_array;
1451 0         0 my $children = $self->children;
1452 0 0       0 if( !scalar( @_ ) )
1453             {
1454 0         0 $results->push( $children->list );
1455 0         0 $children->reset;
1456             $results->foreach(sub
1457             {
1458 0     0   0 $_->parent( undef );
1459 0         0 });
1460 0         0 return( $results );
1461             }
1462 0   0     0 my $new = $self->_list_to_nodes( @_ ) || return( $self->pass_error({ class => 'HTML::Object::SyntaxError' }) );
1463            
1464             # We take some care to keep the same original array, so that if it is used or
1465             # referenced elsewhere it continues to be valid, as a 'live' array of (new) elements
1466             $children->foreach(sub
1467             {
1468 0     0   0 $_->parent( undef() );
1469 0         0 });
1470 0         0 $results->push( $children->list );
1471             # We empty it, and pu the new content inside
1472 0         0 $children->reset;
1473            
1474             $new->foreach(sub
1475             {
1476 0     0   0 $_->parent( $self );
1477 0         0 $children->push( $_ );
1478 0         0 });
1479             # Return the old set
1480 0         0 return( $results );
1481             }
1482              
1483             sub replaceWith
1484             {
1485             my $self = shift( @_ );
1486             return( $self->error({
1487             message => "No data was provided to replace this element.",
1488             code => 500,
1489             class => 'HTML::Object::SyntaxError',
1490             }) ) if( !scalar( @_ ) );
1491             my $parent = $self->parent;
1492             return( $self->error({
1493             message => "Current object does not have a parent",
1494             code => 500,
1495             class => 'HTML::Object::HierarchyRequestError',
1496             }) ) if( !$parent );
1497             my $new = $self->_list_to_nodes( @_ ) || return( $self->pass_error({ class => 'HTML::Object::SyntaxError' }) );
1498             my $pos = $parent->children->pos( $self );
1499             $parent->children->splice( $pos, 1, $new->list );
1500             $new->foreach(sub
1501             {
1502             $_->parent( $parent );
1503             });
1504             return( $new );
1505             }
1506              
1507 0     0 1 0 sub scrollHeight { return; }
1508              
1509 0     0 1 0 sub scrollLeft { return; }
1510              
1511 0     0 1 0 sub scrollTop { return; }
1512              
1513 0     0 1 0 sub scrollWidth { return; }
1514              
1515             sub setAttribute
1516             {
1517 3     3 1 4450 my $self = shift( @_ );
1518 3         11 my( $name, $value ) = @_;
1519 3 50 33     23 return( $self->error({
1520             message => "No attribute name was provided.",
1521             code => 500,
1522             class => 'HTML::Object::SyntaxError',
1523             }) ) if( !defined( $name ) || !CORE::length( $name ) );
1524            
1525             # Inherited from HTML::Object::Element
1526 3 50       33 if( !$self->is_valid_attribute( $name ) )
1527             {
1528 0         0 return( $self->error({
1529             message => "Attribute name provided \"$name\" contains illegal characters.",
1530             code => 500,
1531             class => 'HTML::Object::InvalidCharacterError',
1532             }) );
1533             }
1534 3         27 $name = lc( $name );
1535            
1536 3 50       14 if( !defined( $value ) )
1537             {
1538 0         0 return( $self->removeAttribute( $name ) );
1539             }
1540 3         15 $self->attributes->set( $name => $value );
1541 3 50       1765 $self->attributes_sequence->push( $name ) if( !$self->attributes_sequence->has( $name ) );
1542 3         113209 return( $self );
1543             }
1544              
1545             sub setAttributeNode
1546             {
1547 0     0 1 0 my $self = shift( @_ );
1548 0         0 my $att = shift( @_ );
1549 0 0       0 return( $self->error({
1550             message => "No attribute name was provided.",
1551             code => 500,
1552             class => 'HTML::Object::SyntaxError',
1553             }) ) if( !defined( $att ) );
1554 0 50       0 return( $self->error({
1555             message => "Attribute node provided (", overload::StrVal( $att ), ") is not actually an HTML::Object::DOM::Attribute object.",
1556             code => 500,
1557             class => 'HTML::Object::TypeError',
1558             }) ) if( !$self->_is_a( $att => 'HTML::Object::DOM::Attribute' ) );
1559 0 50 0     0 return( $self->error({
1560             message => "Attribute node object provided has no attribute name set.",
1561             code => 500,
1562             class => 'HTML::Object::SyntaxError',
1563             }) ) if( !$att->name->defined || $att->name->is_empty );
1564 0         0 my $old = $self->getAttributeNode( $att->name );
1565 0 0       0 $self->setAttribute( $att->name, $att->value ) || return( $self->pass_error );
1566 0         0 return( $old );
1567             }
1568              
1569             # setHTML is a mutator only
1570             sub setHTML : lvalue { return( shift->_set_get_callback({
1571             set => sub
1572             {
1573 4     4   2364 my $self = shift( @_ );
1574 4         10 my $this = shift( @_ );
1575 4 50 33     38 if( !defined( $this ) || !CORE::length( $this ) )
1576             {
1577 0         0 die( "No html provided." );
1578             }
1579            
1580 4         7 my $children;
1581 4 100       20 if( $self->_is_a( $this => 'HTML::Object::Element' ) )
1582             {
1583 2 50       78 if( $self->_is_a( $this => 'HTML::Object::DOM::DocumentFragment' ) )
1584             {
1585 0         0 $children = $this->children->clone;
1586 0         0 $this->children->reset;
1587             # DocumentFragment children are not cloned, but moved as per the specification
1588             $children->foreach(sub
1589             {
1590 0         0 $_->detach;
1591 0         0 });
1592             }
1593             else
1594             {
1595 2         78 my $clone = $this->clone;
1596 2         29 $children = $self->new_array( $clone );
1597 2 50       59 $children->push( $clone->close_tag ) if( $clone->close_tag );
1598             }
1599             }
1600             else
1601             {
1602 2 0 0     39 if( ref( $this ) && ( !$self->_is_object( $this ) || ( $self->_is_object( $this ) && !overload::Method( $this, '""' ) ) ) )
      33        
1603             {
1604 0         0 die( "I was expecting some HTML data, but got '" . overload::StrVal( $this ) . "'" );
1605             }
1606 2         15 my $p = $self->new_parser;
1607 2   50     15 my $res = $p->parse_data( "$this" ) ||
1608             die( "Error while parsing html data provided: " . $p->error );
1609 2         23 $children = $res->children;
1610             }
1611             $children->foreach(sub
1612             {
1613 6         104 $_->parent( $self );
1614 4         251 });
1615 4         166 $self->children( $children );
1616 4         1549 return( $self );
1617             }
1618 4     4 1 2957 }, @_ ) ); }
1619              
1620 0     0 1 0 sub shadowRoot { return; }
1621              
1622 0     0 1 0 sub showPopover { return; }
1623              
1624             # Note: spellcheck -> property
1625 0     0 1 0 sub spellcheck : lvalue { return( shift->_set_get_property( { attribute => 'spellcheck', is_boolean => 1 }, @_ ) ); }
1626              
1627             sub string_value
1628             {
1629             my $self = shift( @_ );
1630             my $type = $self->nodeType;
1631             if( $type == TEXT_NODE || $type == COMMENT_NODE )
1632             {
1633             return( $self->value );
1634             }
1635             else
1636             {
1637             return( $self->as_text );
1638             }
1639             }
1640              
1641             # Note: style -> property
1642 0     0 1 0 sub style { return( shift->new_hash ); }
1643              
1644             # Note: property
1645 0     0 1 0 sub tabIndex : lvalue { return( shift->_set_get_property( 'tabindex', @_ ) ); }
1646              
1647 0     0 1 0 sub tagName { return( shift->getName ); }
1648              
1649             # Note: title -> property
1650 0     0 1 0 sub title : lvalue { return( shift->_set_get_property( 'title', @_ ) ); }
1651              
1652             sub to_number
1653             {
1654             my $self = shift( @_ );
1655             return( $self->new_number( $self->as_text ) );
1656             }
1657              
1658             # Based on the polyfill provided by Mozilla at:
1659             # <https://developer.mozilla.org/en-US/docs/Web/API/Element/toggleAttribute>
1660             # because, otherwise, as of 2021-12-15, the description of the use of 'force' is cryptic
1661             sub toggleAttribute
1662             {
1663 0     0 1 0 my $self = shift( @_ );
1664 0         0 my( $name, $force ) = @_;
1665 0 0 0     0 return( $self->error({
1666             message => "No attribute name was provided.",
1667             code => 500,
1668             class => 'HTML::Object::SyntaxError',
1669             }) ) if( !defined( $name ) || !CORE::length( $name ) );
1670            
1671             # Inherited from HTML::Object::Element
1672 0 0       0 if( !$self->is_valid_attribute( $name ) )
1673             {
1674 0         0 return( $self->error({
1675             message => "Attribute name provided \"$name\" contains illegal characters.",
1676             code => 500,
1677             class => 'HTML::Object::InvalidCharacterError',
1678             }) );
1679             }
1680 0         0 $name = lc( $name );
1681 0 0       0 if( $self->attribute->has( $name ) )
1682             {
1683 0 0 0     0 return( $self->true ) if( defined( $force ) && $force );
1684 0         0 $self->removeAttribute( $name );
1685 0         0 return( $self->false );
1686             }
1687 0 0 0     0 return( $self->false ) if( defined( $force ) && !$force );
1688 0         0 $self->setAttribute( $name => '' );
1689 0         0 return( $self->true );
1690             }
1691              
1692 0     0 1 0 sub togglePopover { return; }
1693              
1694             sub toString { return( shift->as_string ); }
1695              
1696             # Note: translate -> property
1697 0     0 1 0 sub translate : lvalue { return( shift->_set_get_property( { attribute => 'translate', is_boolean => 1 }, @_ ) ); }
1698              
1699             # Used by HTML::Object::DOM::Element::*
1700 1     1   8 sub _get_parent_form { return( shift->closest( 'form' ) ); }
1701              
1702             # Note: moved _list_to_nodes to HTML::Object::DOM::Node to make it also available to HTML::Object::DOM::Declaration
1703              
1704             # Used by HTML::Object::DOM::Element::Anchor and HTML::Object::DOM::Element::Area
1705             sub _set_get_anchor_uri
1706             {
1707 27     27   41 my $self = shift( @_ );
1708 27         65 my $link = $self->href;
1709             # We constantly get a new URI object, because the value of the href attribute may have been altered by other means
1710 27 50 33     4022 try
  27         39  
  27         38  
  27         108  
  0         0  
  27         56  
  27         84  
  27         45  
1711 27     27   35 {
1712 27 100       79 return( $link ) if( $self->_is_a( $link => 'URI' ) );
1713 1 50 33     20 return( ( defined( $link ) && CORE::length( "$link" ) ) ? URI->new( $link ) : URI->new );
1714             }
1715 27 0 0     116 catch( $e )
  0 0 33     0  
  0 0       0  
  27 0       47  
  27 50       38  
  27 50       32  
  27 0       33  
  27 0       70  
  0 0       0  
  0 50       0  
  0 50       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 0       0  
  0 50       0  
  0         0  
  0         0  
  0         0  
  27         60  
  0         0  
  27         52  
  0         0  
  0         0  
  27         897  
  27         77  
  27         51  
  27         56  
  0         0  
  0         0  
  0         0  
  0         0  
1716 0     0   0 {
1717 0         0 return( $self->error( "Unable to create a URI object from \"$link\" (", overload::StrVal( $link ), "): $e" ) );
1718 29 0 0 29   239 }
  29 0 0     75  
  29 0 33     38112  
  0 0 33     0  
  0 0 33     0  
  0 0 33     0  
  0 0 33     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  27 0       45  
  0 0       0  
  27 50       274  
  27 50       109  
  27 50       62  
  0 0       0  
  0 50       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  27         237  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
1719             }
1720              
1721             sub _set_get_form_attribute : lvalue
1722             {
1723 0     0   0 my $self = shift( @_ );
1724 0         0 my $attr = shift( @_ );
1725              
1726             return( $self->_set_get_callback({
1727             get => sub
1728             {
1729 0     0   0 my $form = $self->_get_parent_form;
1730 0 0       0 if( !defined( $form ) )
1731             {
1732 0         0 return;
1733             }
1734 0         0 my $code = $form->can( $attr );
1735 0 0       0 if( !defined( $code ) )
1736             {
1737 0         0 die( "Form object has no method \"$attr\"." );
1738             }
1739            
1740 0         0 my $rv = $code->( $form );
1741 0         0 return( $rv );
1742             },
1743             set => sub
1744             {
1745 0     0   0 my $arg = shift( @_ );
1746 0         0 my $ctx = $_;
1747 0         0 my $form = $self->_get_parent_form;
1748 0 0       0 if( !defined( $form ) )
1749             {
1750 0         0 return;
1751             }
1752 0         0 my $code = $form->can( $attr );
1753 0 0       0 if( !defined( $code ) )
1754             {
1755 0         0 die( "Form object has no method \"$attr\"." );
1756             }
1757            
1758 0         0 $code->( $form, $arg );
1759 0 0       0 return( $arg ) if( $ctx->{assign} );
1760 0         0 return( $self );
1761             },
1762 0         0 }, @_ ) );
1763             }
1764              
1765             # _set_get_property has been moved up in HTML::Object::Element
1766             # Note: private method to set or get attribute as an lvalue method for DOM properties in HTML::Object::DOM::Element::* and also for some DOM List abstract class like HTML::Object::DOM::List
1767             sub _set_get_property : lvalue
1768             {
1769 176     176   321 my $self = shift( @_ );
1770 176         265 my $attr = shift( @_ );
1771            
1772 176         278 my $def = {};
1773             # If the $attr parameter is an hash reference, it is used to provide more information
1774             # such as whether this property is a boolean
1775 176 100       499 if( ref( $attr ) eq 'HASH' )
1776             {
1777 45         63 $def = $attr;
1778 45         82 $attr = $def->{attribute};
1779             }
1780 176   100     868 $def->{is_boolean} //= 0;
1781            
1782             return( $self->_set_get_callback({
1783             get => sub
1784             {
1785 133     133   80312 my $self = shift( @_ );
1786 133 50 66     933 if( $def->{is_datetime} )
    50          
    100          
    100          
1787             {
1788 0         0 my $val = $self->attr( $attr );
1789 0 0 0     0 try
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
1790 0         0 {
1791 0         0 my $dt = $self->_parse_timestamp( $val );
1792 0 0       0 return( $self->pass_error ) if( !defined( $dt ) );
1793 0         0 return( $dt );
1794             }
1795 0 0 0     0 catch( $e )
  0 0 0     0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
1796 0         0 {
1797 0         0 return( $self->error( "Unable to parse datetime value \"$val\": $e" ) );
1798 29 0 0 29   249 }
  29 0 0     68  
  29 0 0     31168  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
1799             }
1800             elsif( $def->{is_number} )
1801             {
1802 0         0 my $val = $self->attr( $attr );
1803 0 0       0 return if( !$self->_is_number( $val ) );
1804 0 0       0 if( $val =~ /^(\d{1,10})(?:\.\d+)?$/ )
1805             {
1806 0         0 my $dt = $self->_parse_timestamp( $val );
1807 0 0       0 return( $dt ) if( ref( $dt ) );
1808             }
1809 0         0 return( $self->new_number( $val ) );
1810             }
1811             elsif( $def->{is_uri} )
1812             {
1813 31         95 my $val = $self->attr( $attr );
1814             # We constantly get a new URI object, because the value of the href attribute may have been altered by other means
1815 31 50 33     16185 try
  31         58  
  31         37  
  31         107  
  0         0  
  31         48  
  31         59  
  31         60  
1816 31         54 {
1817 31 100       89 return( $val ) if( $self->_is_a( $val => 'URI' ) );
1818 1 50       9 return if( !defined( $val ) );
1819 0 0       0 return( $val ) if( !CORE::length( "$val" ) );
1820 0         0 return( URI->new( "$val" ) );
1821             }
1822 31 0 0     120 catch( $e )
  0 0 33     0  
  0 0       0  
  31 0       48  
  31 0       35  
  31 0       34  
  31 0       48  
  31 0       82  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 0       0  
  0 50       0  
  0         0  
  0         0  
  0         0  
  31         64  
  0         0  
  31         43  
  0         0  
  0         0  
  31         997  
  31         88  
  31         54  
  31         59  
  0         0  
  0         0  
  0         0  
  0         0  
1823 0         0 {
1824 0         0 return( $self->error( "Unable to create a URI object from \"$val\" (", overload::StrVal( $val ), "): $e" ) );
1825 29 0 0 29   220 }
  29 0 0     73  
  29 0 33     34466  
  0 0 66     0  
  0 0 33     0  
  0 0 33     0  
  0 0 33     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  31 0       69  
  0 0       0  
  31 50       347  
  31 50       113  
  31 50       71  
  0 0       0  
  0 50       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  31         228  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
1826             }
1827             elsif( $def->{callback} && ref( $def->{callback} ) eq 'CODE' )
1828             {
1829 11         32 return( $def->{callback}->( $self, $attr ) );
1830             }
1831 91         318 return( $self->attr( $attr ) );
1832             },
1833             set => sub
1834             {
1835 43     43   24033 my $self = shift( @_ );
1836 43         89 my $arg = shift( @_ );
1837 43 50 66     371 if( $def->{is_boolean} )
    50          
    100          
    100          
1838             {
1839             # Any true value works, even in the web browser
1840 0 0       0 if( $arg )
1841             {
1842             # it is ok to set an empty value
1843 0         0 $self->attr( $attr => '' );
1844             }
1845             else
1846             {
1847             # Passing undef implies it will be removed. See HTML::Object::Element
1848 0         0 $self->attr( $attr => undef );
1849             }
1850             }
1851             elsif( $def->{is_datetime} )
1852             {
1853 0         0 $self->attr( $attr => "$arg" );
1854             }
1855             # form target
1856             elsif( $def->{is_uri} )
1857             {
1858 1 50 33     2 try
  1         2  
  1         1  
  1         4  
  0         0  
  1         2  
  1         4  
  1         1  
1859 1         3 {
1860 1         4 my $uri = URI->new( $arg );
1861 1         184 $self->attr( $attr => $uri );
1862             }
1863 1 0 50     5 catch( $e )
  1 0 33     5  
  1 0       3  
  1 0       1  
  1 0       2  
  1 0       1  
  1 0       2  
  1 0       4  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 0       0  
  0 50       0  
  0         0  
  0         0  
  0         0  
  1         4  
  0         0  
  1         1  
  0         0  
  0         0  
  1         3  
  1         4  
  1         2  
  1         2  
  0         0  
  0         0  
  0         0  
  0         0  
1864 0         0 {
1865 0         0 die( "Unable to create an URI with \"$arg\": $e" );
1866 29 0 0 29   234 }
  29 0 0     73  
  29 0 33     38989  
  0 0 33     0  
  0 0 33     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 33     0  
  0 0 33     0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  1 0       3  
  0 0       0  
  1 0       40  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 50       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 50       0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  1         4  
  0         0  
  0         0  
  0         0  
  0         0  
  1         3  
1867             }
1868             # Used for <option>
1869             elsif( $def->{callback} && ref( $def->{callback} ) eq 'CODE' )
1870             {
1871 1         4 $def->{callback}->( $self, $attr => $arg );
1872             }
1873             else
1874             {
1875 41         150 $self->attr( $attr => $arg );
1876             }
1877 43         135 $self->reset(1);
1878 43         154 return( $arg );
1879             }
1880 176         3505 }, @_ ) );
1881             }
1882              
1883             # Used by HTML::Object::DOM::Element::Anchor and HTML::Object::DOM::Element::Area
1884             sub _set_get_uri_property : lvalue
1885             {
1886 24     24   47 my $self = shift( @_ );
1887 24         37 my $prop = shift( @_ );
1888 24         62 my $uri = $self->_set_get_anchor_uri;
1889 24         186 my $map =
1890             {
1891             hash => 'fragment',
1892             # URI's host_port is tolerant just like DOM's host is. Even if no port is provided, it will not complain
1893             host => 'host_port',
1894             hostname => 'host',
1895             pathname => 'path',
1896             password => 'userinfo',
1897             protocol => 'scheme',
1898             search => 'query',
1899             username => 'userinfo',
1900             };
1901            
1902             return( $self->_set_get_callback({
1903             get => sub
1904             {
1905 14     14   6821 my $self = shift( @_ );
1906             # If there is an URI, we use it as a alue storage
1907             # It is convenient and let the user modify it directly if he wants.
1908 14 50       52 if( ref( $uri ) )
1909             {
1910 14 50 33     41 try
  14         14  
  14         17  
  14         39  
  0         0  
  14         16  
  14         23  
  14         20  
1911 14         14 {
1912 14 100       40 my $meth = exists( $map->{ $prop } ) ? $map->{ $prop } : $prop;
1913 14         75 my $code = $uri->can( $meth );
1914             # User trying to access URI method like host port, etc on a generic URI
1915             # which is ok for method like path, query, fragment
1916             # So we convert what would otherwise be an error into an undef returned, meaning no value
1917 14 100       36 if( !defined( $code ) )
1918             {
1919 2 50       5 if( $uri->isa( 'URI::_generic' ) )
1920             {
1921 2         5 return( $self->{ $prop } );
1922             }
1923             else
1924             {
1925 0         0 return( $self->error( "URI object has no method \"$meth\"." ) );
1926             }
1927             }
1928 12         56 my $val = $code->( $uri );
1929             # We assign the value from the URI method in case, the user would have modified the URI object directly
1930             # We need to stay synchronised.
1931 12 100 100     349 if( $prop eq 'username' || $prop eq 'password' )
    100          
    100          
    100          
1932             {
1933 2 50       8 if( defined( $val ) )
1934             {
1935 2         10 @$self{qw( username password )} = split( /:/, $val, 2 );
1936             }
1937             else
1938             {
1939 0         0 $self->{username} = undef;
1940 0         0 $self->{password} = undef;
1941             }
1942 2         10 return( $self->{ $prop } );
1943             }
1944             # We add back the colon, because URI stores the scheme without it, but our 'protocol' method returns the scheme with it.
1945             elsif( $prop eq 'protocol' )
1946             {
1947 3 50       13 $val .= ':' if( defined( $val ) );
1948             }
1949             elsif( $prop eq 'hash' )
1950             {
1951 2 100       9 substr( $val, 0, 0, '#' ) if( defined( $val ) );
1952             }
1953             elsif( $prop eq 'search' )
1954             {
1955 1 50       4 substr( $val, 0, 0, '?' ) if( defined( $val ) );
1956             }
1957 10         37 return( $self->{ $prop } = $val );
1958             }
1959 14 0 0     72 catch( $e )
  0 0 33     0  
  0 0       0  
  14 0       18  
  14 0       16  
  14 0       15  
  14 0       23  
  14 0       34  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 0       0  
  0 50       0  
  0         0  
  0         0  
  0         0  
  14         36  
  0         0  
  14         25  
  0         0  
  0         0  
  14         30  
  14         41  
  14         25  
  14         45  
  0         0  
  0         0  
  0         0  
  0         0  
1960 0         0 {
1961 0         0 die( "Unable to get value for URI method \"${prop}\": $e" );
1962 29 0 0 29   268 }
  29 0 0     76  
  29 0 33     7097  
  0 0 33     0  
  0 0 33     0  
  0 0 33     0  
  0 0 33     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  14 0       30  
  0 0       0  
  14 50       101  
  14 50       52  
  14 50       40  
  0 0       0  
  0 50       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  14         170  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
1963             }
1964 0         0 return( $self->{ $prop } );
1965             },
1966             set => sub
1967             {
1968 10     10   3201 my $self = shift( @_ );
1969 10         22 my $arg = shift( @_ );
1970 10         23 $self->{ $prop } = $arg;
1971 10 50       27 if( ref( $uri ) )
1972             {
1973 10         16 my $uri_class = ref( $uri ); # URI::https or maybe URI::_generic ?
1974 10 50 33     18 try
  10         16  
  10         17  
  10         35  
  0         0  
  10         28  
  10         19  
  10         18  
1975 10         13 {
1976 10 100 100     91 if( $prop eq 'username' || $prop eq 'password' )
    100          
    100          
    100          
1977             {
1978 29     29   273 no warnings 'uninitialized';
  29         73  
  29         32578  
1979 2         19 $arg = join( ':', @$self{ qw( username password ) } );
1980             }
1981             elsif( $prop eq 'protocol' )
1982             {
1983             # Remove the trailing colon, because URI scheme method takes it without it
1984 1         3 $arg =~ s/\:$//;
1985             }
1986             elsif( $prop eq 'hash' )
1987             {
1988 2         7 $arg =~ s/^\#//;
1989             }
1990             elsif( $prop eq 'search' )
1991             {
1992 1         3 $arg =~ s/^\?//;
1993             }
1994 10 100       31 my $meth = exists( $map->{ $prop } ) ? $map->{ $prop } : $prop;
1995 10         70 my $code = $uri->can( $meth );
1996             # User trying to access URI method like host port, etc on a generic URI
1997             # which is ok for method like path, query, fragment
1998             # So we convert what would otherwise be an error into an undef returned, meaning no value
1999 10 100       29 if( !defined( $code ) )
2000             {
2001 2 50       8 if( $uri->isa( 'URI::_generic' ) )
2002             {
2003 2         6 return( $self->{ $prop } );
2004             }
2005             else
2006             {
2007 0         0 return( $self->error( "URI object has no method \"$meth\"." ) );
2008             }
2009             }
2010 8         29 $code->( $uri, $arg );
2011             # If the URI object was generic and we switched it to a non-generic one by setting the schem
2012             # We also set other properties if we have them
2013 8 50 66     1882 if( $prop eq 'protocol' && $uri_class eq 'URI::_generic' )
2014             {
2015 0 0 0     0 if( $self->{hostname} )
    0          
2016             {
2017 0         0 $uri->host_port( $self->{hostname} );
2018             }
2019             elsif( $self->{host} || $self->{port} )
2020             {
2021 0 0       0 $uri->host( $self->{host} ) if( $self->{host} );
2022 0 0       0 $uri->port( $self->{port} ) if( $self->{port} );
2023             }
2024 0 0 0     0 if( $self->{username} || $self->{password} )
2025             {
2026 0         0 $uri->userinfo( join( ':', @$self{qw( username password )} ) );
2027             }
2028             }
2029 8         32 $self->attr( href => $uri );
2030             }
2031 10 0 50     48 catch( $e )
  8 0 33     27  
  8 0       28  
  10 0       17  
  10 0       12  
  10 0       13  
  10 0       16  
  10 0       37  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 0       0  
  0 50       0  
  0         0  
  0         0  
  0         0  
  10         27  
  0         0  
  10         17  
  0         0  
  0         0  
  10         32  
  10         34  
  10         22  
  10         35  
  0         0  
  0         0  
  0         0  
  0         0  
2032 0         0 {
2033 0         0 die( "Unable to set value \"${arg}\" for URI method \"${prop}\": $e" );
2034 29 0 0 29   228 }
  29 0 0     70  
  29 0 33     6209  
  0 0 66     0  
  0 0 66     0  
  0 0 33     0  
  0 0 33     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 33     0  
  0 0 33     0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  10 0       27  
  0 0       0  
  10 50       358  
  2 50       8  
  2 50       4  
  0 0       0  
  0 100       0  
  0 50       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 50       0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  2         25  
  0         0  
  8         30  
  0         0  
  0         0  
  0         0  
  0         0  
  8         29  
2035             }
2036 8         28 $self->reset(1);
2037 8         19 $self->{ $prop } = $arg;
2038 8         23 $self->attr( href => $uri );
2039 8         41 return( $arg );
2040             }
2041 24         292 }, @_ ) );
2042             }
2043              
2044             1;
2045             # NOTE: POD
2046             __END__
2047              
2048             =encoding utf-8
2049              
2050             =head1 NAME
2051              
2052             HTML::Object::DOM::Element - HTML Object
2053              
2054             =head1 SYNOPSIS
2055              
2056             use HTML::Object::DOM::Element;
2057             my $this = HTML::Object::DOM::Element->new ||
2058             die( HTML::Object::DOM::Element->error, "\n" );
2059              
2060             =head1 VERSION
2061              
2062             v0.3.0
2063              
2064             =head1 DESCRIPTION
2065              
2066             This module represents an HTML element and contains also all the methods for L<DOM nodes|https://developer.mozilla.org/en-US/docs/Web/API/Node>. It is inherited by all other element objects in a L<document|HTML::Object::Document>.
2067              
2068             This module inherits from L<HTML::Object::Node> and is extended by L<HTML::Object::XQuery>
2069              
2070             =head1 INHERITANCE
2071              
2072             +-----------------------+ +---------------------------+ +-------------------------+ +----------------------------+
2073             | HTML::Object::Element | --> | HTML::Object::EventTarget | --> | HTML::Object::DOM::Node | --> | HTML::Object::DOM::Element |
2074             +-----------------------+ +---------------------------+ +-------------------------+ +----------------------------+
2075              
2076             =head1 PROPERTIES
2077              
2078             All the following properties can be used as lvalue method as well as regular method. For example with L</baseURI>
2079              
2080             =head2 accessKey
2081              
2082             A string representing the access key assigned to the element.
2083              
2084             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/accessKey> for more information.
2085              
2086             =head2 accessKeyLabel
2087              
2088             my $label = $element->accessKeyLabel;
2089              
2090             my $btn = $document->getElementById("btn1");
2091             my $shortcutLabel = $btn->accessKeyLabel || $btn->accessKey;
2092             $btn->title .= " [" . uc( $shortcutLabel ) . "]";
2093              
2094             Read-only
2095              
2096             Returns a string containing the element's assigned access key.
2097              
2098             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/accessKeyLabel> for more information.
2099              
2100             =head2 attributeStyleMap
2101              
2102             Sets or gets the C<style> attribute.
2103              
2104             Normally, this is read-only, and represents a C<StylePropertyMap> representing the declarations of the element's style attribute.
2105              
2106             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attributeStyleMap> for more information.
2107              
2108             =head2 baseURI
2109              
2110             # Get the base uri, if any
2111             my $uri = $e->baseURI;
2112             $e->baseURI = 'https://example.org/some/where';
2113             # or
2114             $e->baseURI( 'https://example.org/some/where' );
2115              
2116             Read-only
2117              
2118             This returns an L<URI> object representing the base URL of the document containing the Node, if any.
2119              
2120             =head2 childElementCount
2121              
2122             Read-only
2123              
2124             Returns the number of child elements of this element.
2125              
2126             =head2 childNodes
2127              
2128             Read-only
2129              
2130             This returns an L<array object|Module::Generic::Array> containing all the children of this node (including elements, text and comments). This list being live means that if the children of the Node change, the L<list object|Module::Generic::Array> is automatically updated.
2131              
2132             =head2 classList
2133              
2134             The C<classList> property is a read-only property that returns a live L<HTML::Object::TokenList> collection of the class attributes of the element. This can then be used to manipulate the class list.
2135              
2136             Using classList is a convenient alternative to accessing an element's list of classes as a space-delimited string via L</className>
2137              
2138             It returns a L<HTML::Object::TokenList> object representing the contents of the element's class attribute. If the class attribute is not set or empty, it returns an empty L<HTML::Object::TokenList>, i.e. a L<HTML::Object::TokenList> with the L<length|HTML::Object::TokenList/length> property equal to 0.
2139              
2140             Although the classList property itself is read-only, you can modify its associated L<HTML::Object::TokenList> using the L<add()|HTML::Object::TokenList/add>, L<remove()|HTML::Object::TokenList/add>, L<replace()|HTML::Object::TokenList/add>, and L<toggle()|HTML::Object::TokenList/add> methods.
2141              
2142             For example:
2143              
2144             my $div = $doc->createElement('div');
2145             # use the classList API to remove and add classes
2146             $div->classList->remove("foo");
2147             $div->classList->add("anotherclass");
2148             say $div->outerHTML; # <div class="anotherclass"></div>
2149             # if visible is set remove it, otherwise add it
2150             $div->classList->toggle("visible");
2151             $div->classList->contains("foo");
2152             # add or remove multiple classes
2153             $div->classList->add("foo", "bar", "baz");
2154             $div->classList->remove("foo", "bar", "baz");
2155             # replace class "foo" with class "bar"
2156             $div->classList->replace("foo", "bar");
2157              
2158             See also L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/Element/classList>
2159              
2160             =head2 className
2161              
2162             Set or get the element class.
2163              
2164             Returns a string representing the class of the element.
2165              
2166             This method is an lvalue method, so you can assign value like this:
2167              
2168             $e->className = "my-class";
2169              
2170             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/className>
2171              
2172             =head2 clientHeight
2173              
2174             Read-only.
2175              
2176             This always return C<undef> since this has no meaning under perl.
2177              
2178             Normally, under JavaScript, this would return a number representing the inner height of the element.
2179              
2180             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/clientHeight>
2181              
2182             =head2 clientLeft
2183              
2184             This always return C<undef> since this has no meaning under perl.
2185              
2186             Normally, under JavaScript, this would return a number representing the width of the left border of the element.
2187              
2188             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/clientLeft>
2189              
2190             =head2 clientTop
2191              
2192             This always return C<undef> since this has no meaning under perl.
2193              
2194             Normally, under JavaScript, this would return a number representing the width of the top border of the element.
2195              
2196             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/clientTop>
2197              
2198             =head2 clientWidth
2199              
2200             This always return C<undef> since this has no meaning under perl.
2201              
2202             Normally, under JavaScript, this would return a number representing the inner width of the element.
2203              
2204             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/clientWidth>
2205              
2206             =head2 contentEditable
2207              
2208             Set or get the boolean value where true means the element is editable and a value of false means it is not. Defautls to true.
2209              
2210             $e->contentEditable = 0; # turn off content editability
2211              
2212             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/contentEditable> for more information.
2213              
2214             =head2 dataset
2215              
2216             <div id="user" data-id="1234567890" data-user="carinaanand" data-date-of-birth>
2217             Carina Anand
2218             </div>
2219              
2220             var $el = $document->getElementById('user');
2221              
2222             # $el->id eq 'user';
2223             # $el->dataset->id eq '1234567890';
2224             # $el->dataset->user eq 'carinaanand';
2225             # $el->dataset->dateOfBirth eq '';
2226              
2227             # set a data attribute
2228             $el->dataset->dateOfBirth = "1960-10-03";
2229             # <div id="user" data-id="1234567890" data-user="carinaanand" data-date-of-birth="1960-10-03">Carina Anand</div>
2230              
2231             Read-only
2232              
2233             Returns an L<HTML::Object::ElementDataMap> object with which script can read and write the element's custom data attributes (data-*).
2234              
2235             The attribute name begins with data-. It can contain only letters, numbers, dashes (-), periods (.), colons (:), and underscores (_). Any ASCII capital letters (A to Z) are converted to lowercase.
2236              
2237             Name conversion
2238              
2239             dash-style to camelCase conversion
2240              
2241             A custom data attribute name is transformed to a key for the DOMStringMap entry by the following:
2242              
2243             =over 4
2244              
2245             =item 1. Lowercase all ASCII capital letters (A to Z);
2246              
2247             =item 2. Remove the prefix data- (including the dash);
2248              
2249             =item 3. For any dash (U+002D) followed by an ASCII lowercase letter a to z, remove the dash and uppercase the letter;
2250              
2251             =item 4. Other characters (including other dashes) are left unchanged.
2252              
2253             =back
2254              
2255             camelCase to dash-style conversion
2256              
2257             The opposite transformation, which maps a key to an attribute name, uses the following:
2258              
2259             =over 4
2260              
2261             =item 1. Restriction: Before transformation, a dash must not be immediately followed by an ASCII lowercase letter a to z;
2262              
2263             =item 2. Add the data- prefix;
2264              
2265             =item 3. Add a dash before any ASCII uppercase letter A to Z, then lowercase the letter;
2266              
2267             =item 4. Other characters are left unchanged.
2268              
2269             =back
2270              
2271             For example, a data-abc-def attribute corresponds to C<<$dataset->abcDef>>.
2272              
2273             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset> for more information.
2274              
2275             =head2 dir
2276              
2277             my $parg = $document->getElementById("para1");
2278             $parg->dir = "rtl";
2279             # change the text direction on a paragraph identified as "para1"
2280              
2281             A string, reflecting the dir global attribute, representing the directionality of the element. Possible values are:
2282              
2283             =over 4
2284              
2285             =item * C<ltr>
2286              
2287             for left-to-right;
2288              
2289             =item * C<rtl>
2290              
2291             for right-to-left;
2292              
2293             =item * C<auto>
2294              
2295             for specifying that the direction of the element must be determined based on the contents of the element.
2296              
2297             =back
2298              
2299             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dir> for more information.
2300              
2301             =head2 draggable
2302              
2303             A boolean value indicating if the element can be dragged.
2304              
2305             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/draggable> for more information.
2306              
2307             =head2 enterKeyHint
2308              
2309             A string defining what action label (or icon) to present for the enter key on virtual keyboards.
2310              
2311             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/enterKeyHint> for more information.
2312              
2313             =head2 firstChild
2314              
2315             Read-only
2316              
2317             This returns an element representing the first direct child element of the element, or C<undef> if the element has no child.
2318              
2319             =head2 firstElementChild
2320              
2321             Read-only.
2322              
2323             It returns the first child element of this element.
2324              
2325             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/firstElementChild>
2326              
2327             =head2 hidden
2328              
2329             A string or boolean value reflecting the value of the element's hidden attribute.
2330              
2331             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/hidden> for more information.
2332              
2333             =head2 id
2334              
2335             Set or get an string representing the id of the element.
2336              
2337             # Set it as a regular method
2338             $e->id( 'hello' );
2339             # Set it as a lvalue method
2340             $e->id = 'hello';
2341             # Retrieve it
2342             my $id = $e->id;
2343              
2344             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/id>
2345              
2346             =head2 innerHTML
2347              
2348             Set or get the element's content. This returns a string representing the markup of the element's content.
2349              
2350             Se L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML>
2351              
2352             =head2 inert
2353              
2354             A boolean value indicating whether the user agent must act as though the given node is absent for the purposes of user interaction events, in-page text searches (C<find in page>), and text selection.
2355              
2356             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/inert> for more information.
2357              
2358             =head2 innerText
2359              
2360             Represents the rendered text content of a node and its descendants. As a getter, it approximates the text the user would get if they highlighted the contents of the element with the cursor and then copied it to the clipboard. This returns a L<string object|Module::Generic::Scalar>. As a setter, it replaces the content inside the selected element, with either a L<text object|HTML::Object::DOM::Text> or by a string converting any line breaks into C<<br />> elements.
2361              
2362             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/innerText> for more information.
2363              
2364             =head2 inputMode
2365              
2366             A string value reflecting the value of the element's inputmode attribute.
2367              
2368             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/inputMode> for more information.
2369              
2370             =head2 isConnected
2371              
2372             Returns a boolean indicating whether or not the element is connected (directly or indirectly) to the context object, i.e. the L<Document object|HTML::Object::Document> in the case of the normal DOM.
2373              
2374             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Node/isConnected>
2375              
2376             =head2 isContentEditable
2377              
2378             <p id="firstParagraph">Uneditable Paragraph</p>
2379             <p id="secondParagraph" contenteditable="true">Editable Paragraph</p>
2380              
2381             my $firstParagraph = $document->getElementById("firstParagraph");
2382             my $secondParagraph = $document->getElementById("secondParagraph");
2383              
2384             Read-only
2385              
2386             Returns a L<boolean value|HTML::Object::Boolean> indicating whether or not the content of the element can be edited. Use L<contentEditable> to change the value.
2387              
2388             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/isContentEditable> for more information.
2389              
2390             =head2 lang
2391              
2392             A string representing the language of an element's attributes, text, and element contents.
2393              
2394             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/lang> for more information.
2395              
2396             =head2 lastChild
2397              
2398             Read-only
2399              
2400             This returns an element representing the last direct child element of the element, or C<undef> if the element has no child.
2401              
2402             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Node/lastChild>
2403              
2404             =head2 lastElementChild
2405              
2406             Read-only
2407              
2408             Returns the last child element of this element, if any at all.
2409              
2410             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/lastElementChild>
2411              
2412             =head2 localName
2413              
2414             Read-only
2415              
2416             A string representing the local part of the qualified name of the element. This is basically the tag name. This has a special meaning only when using xml, which we do not. So this is just an alias to L</getName>
2417              
2418             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/localName>
2419              
2420             =head2 namespaceURI
2421              
2422             Read-only
2423              
2424             The namespace URI of the element, or C<undef> if it is no namespace.
2425              
2426             This always return C<undef>, because as HTML, we do not deal with namespace, which is used primarily under xml.
2427              
2428             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/namespaceURI>
2429              
2430             =head2 nextElementSibling
2431              
2432             Read-only
2433              
2434             Is an L<Element|HTML::Object::Element>, the element immediately following the given one in the tree, or C<undef> if there's no sibling node.
2435              
2436             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/nextElementSibling>
2437              
2438             =head2 nextSibling
2439              
2440             Read-only
2441              
2442             This returns an element representing the next element in the tree, or C<undef> if there is not such element.
2443              
2444             The next node could also be a whitespace or a text. If you want to get the next element and not just any node, use L<nextElementSibling|HTML::Object::DOM/nextElementSibling> instead.
2445              
2446             =head2 nodeName
2447              
2448             Read-only
2449              
2450             This returns a string containing the name of the element. The structure of the name will differ with the element type. E.g. An L<HTML Element|HTML::Object::Element> will contain the name of the corresponding tag, like 'audio' for an HTML audio element, a L<Text|HTML::Object::Text> element will have the '#text' string, or a L<Document|HTML::Object::Document> element will have the '#document' string.
2451              
2452             =head2 nodeType
2453              
2454             Read-only
2455              
2456             This returns an integer representing the type of the element. Possible values are:
2457              
2458             =over 4
2459              
2460             =item 1. element node
2461              
2462             =item 2. attribute node
2463              
2464             =item 3. text node
2465              
2466             =item 4. CDATA section node
2467              
2468             =item 5. unused
2469              
2470             =item 6. unsued
2471              
2472             =item 7. processing instruction node
2473              
2474             =item 8. comment node
2475              
2476             =item 9. document node
2477              
2478             =item 10. document type node
2479              
2480             =item 11. document fragment node
2481              
2482             =back
2483              
2484             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType>
2485              
2486             =head2 nodeValue
2487              
2488             This returns or sets the value of the current node.
2489              
2490             For document, element or collection, this returns C<undef> and for attribute, text or comment, this sets or returns the objct value.
2491              
2492             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeValue>
2493              
2494             =head2 noModule
2495              
2496             A boolean value indicating whether an import script can be executed in user agents that support module scripts.
2497              
2498             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/noModule> for more information.
2499              
2500             =head2 nonce
2501              
2502             This returns nothing.
2503              
2504             Normally, under JavaScript, this would return the cryptographic number used once that is used by Content Security Policy to determine whether a given fetch will be allowed to proceed.
2505              
2506             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/nonce> for more information.
2507              
2508             =head2 offsetHeight
2509              
2510             Sets or gets the property C<offsetheight>.
2511              
2512             Normally, under JavaScript, this would be read-only and return a double containing the height of an element, relative to the layout.
2513              
2514             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetHeight> for more information.
2515              
2516             =head2 offsetLeft
2517              
2518             Sets or gets the property C<offsetleft>.
2519              
2520             Normally, under JavaScript, this would be read-only and return a double, the distance from this element's left border to its offsetParent's left border.
2521              
2522             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetLeft> for more information.
2523              
2524             =head2 offsetParent
2525              
2526             Sets or gets the property C<offsetparent>.
2527              
2528             Normally, under JavaScript, this would be read-only and return Element that is the element from which all offset calculations are currently computed.
2529              
2530             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetParent> for more information.
2531              
2532             =head2 offsetTop
2533              
2534             Sets or gets the property C<offsettop>.
2535              
2536             Normally, under JavaScript, this would be read-only and return a double, the distance from this element's top border to its offsetParent's top border.
2537              
2538             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetTop> for more information.
2539              
2540             =head2 offsetWidth
2541              
2542             Sets or gets the property C<offsetwidth>.
2543              
2544             Normally, under JavaScript, this would be read-only and return a double containing the width of an element, relative to the layout.
2545              
2546             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetWidth> for more information.
2547              
2548             =head2 outerHTML
2549              
2550             Returns a string representing the markup of the element including its content.
2551             When used as a setter, replaces the element with nodes parsed from the given string.
2552              
2553             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/outerHTML>
2554              
2555             =head2 outerText
2556              
2557             Represents the rendered text content of a node and its descendants.
2558              
2559             As a getter, it is the same as L</innerText> (it represents the rendered text content of an element and its descendants).
2560              
2561             As a setter, it replaces the selected node and its contents with the given value, converting any line breaks into C<<br />> elements.
2562              
2563             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/outerText> for more information.
2564              
2565             =head2 ownerDocument
2566              
2567             Read-only
2568              
2569             This returns the L<Document|HTML::Object::Document> that this element belongs to. If the element is itself a document, returns C<undef>.
2570              
2571             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Node/ownerDocument>
2572              
2573             =head2 parentNode
2574              
2575             Read-only
2576              
2577             This returns an element that is the parent of this element. If there is no such element, like if this element is the top of the tree or if does not participate in a tree, this property returns C<undef>.
2578              
2579             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Node/parentNode>
2580              
2581             =head2 parentElement
2582              
2583             Read-only
2584              
2585             This returns an element that is the parent of this element. If the element has no parent, or if that parent is not an Element, this property returns C<undef>.
2586              
2587             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Node/parentElement>
2588              
2589             =head2 part
2590              
2591             This always returns C<undef>, because it has no meaning under perl.
2592              
2593             Normally, under JavaScript, this would be a part that represents the part identifier(s) of the element (i.e. set using the part attribute), returned as a DOMTokenList.
2594              
2595             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/outerHTML>
2596              
2597             =head2 popover
2598              
2599             Experimental
2600              
2601             Gets and sets an element's popover state via JavaScript (C<auto> or C<manual>), and can be used for feature detection. Reflects the value of the popover global HTML attribute.
2602              
2603             If no value are set, this will return the one of the HTML attribute.
2604              
2605             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/popover> for more information.
2606              
2607             =head2 prefix
2608              
2609             Read-only
2610              
2611             This always return C<undef>
2612              
2613             Normally, for xml documents, this would be a string representing the namespace prefix of the element, or C<undef> if no prefix is specified.
2614              
2615             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/prefix>
2616              
2617             =head2 previousElementSibling
2618              
2619             Read-only
2620              
2621             Returns an Element, the element immediately preceding the given one in the tree, or C<undef> if there is no sibling element.
2622              
2623             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/previousElementSibling>
2624              
2625             =head2 previousSibling
2626              
2627             Read-only
2628              
2629             This returns a element representing the previous element in the tree, or C<undef> if there is not such element.
2630              
2631             The previous node could also be a whitespace or a text. If you want to get the previous element and not just any node, use L<previousElementSibling|HTML::Object::DOM/previousElementSibling> instead.
2632              
2633             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Node/previousSibling>
2634              
2635             =head2 properties
2636              
2637             This does nothing, but return an empty L<array object|Module::Generic::Array>
2638              
2639             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/properties> for more information.
2640              
2641             =head2 scrollHeight
2642              
2643             Read-only
2644              
2645             This always return C<undef> as this is not applicable under perl.
2646              
2647             Normally, under JavaScript, this would return a number representing the scroll view height of an element.
2648              
2649             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight>
2650              
2651             =head2 scrollLeft
2652              
2653             This always return C<undef> as this is not applicable under perl.
2654              
2655             Normally, under JavaScript, this would set or return a number representing the left scroll offset of the element.
2656              
2657             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollLeft>
2658              
2659             =head2 scrollTop
2660              
2661             This always return C<undef> as this is not applicable under perl.
2662              
2663             Normally, under JavaScript, this would set or return a number representing number of pixels the top of the document is scrolled vertically.
2664              
2665             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollTop>
2666              
2667             =head2 scrollWidth
2668              
2669             Read-only
2670              
2671             This always return C<undef> as this is not applicable under perl.
2672              
2673             Normally, under JavaScript, this would return a number representing the scroll view width of the element.
2674              
2675             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollWidth>
2676              
2677             =head2 setHTML
2678              
2679             Parses and sanitizes a string of HTML and inserts into the DOM as a subtree of the element.
2680              
2681             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/setHTML>
2682              
2683             =head2 shadowRoot
2684              
2685             Read-only
2686              
2687             Always returns C<undef>
2688              
2689             Normally, under JavaScript, this would return the open shadow root that is hosted by the element, or null if no open shadow root is present.
2690              
2691             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/shadowRoot>
2692              
2693             =head2 spellcheck
2694              
2695             A boolean value that controls spell-checking. It is present on all HTML elements, though it doesn't have an effect on all of them.
2696              
2697             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/spellcheck> for more information.
2698              
2699             =head2 style
2700              
2701             This does nothing, but return a new empty L<hash object|Module::Generic::Hash>
2702              
2703             Normally, this would set or get A C<CSSStyleDeclaration> representing the declarations of the element's style attribute.
2704              
2705             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style> for more information.
2706              
2707             =head2 tabIndex
2708              
2709             The tabIndex property represents the tab order of the current element.
2710              
2711             Tab order is as follows:
2712              
2713             =over
2714              
2715             =item 1. Elements with a positive tabIndex. Elements that have identical tabIndex values should be navigated in the order they appear. Navigation proceeds from the lowest tabIndex to the highest tabIndex.
2716              
2717             =item 2. Elements that do not support the tabIndex attribute or support it and assign tabIndex to 0, in the order they appear.
2718              
2719             =back
2720              
2721             Elements that are disabled do not participate in the tabbing order.
2722              
2723             Values do not need to be sequential, nor must they begin with any particular value. They may even be negative, though each browser trims very large values.
2724              
2725             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/tabIndex>
2726              
2727             =head2 tagName
2728              
2729             Read-only. This is merely an alias for L</getName>
2730              
2731             This returns a string with the name of the tag for the given element.
2732              
2733             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/tagName>
2734              
2735             =head2 textContent
2736              
2737             This returns or sets the textual content of an element and all its descendants.
2738              
2739             Example:
2740              
2741             <div id="divA">This is <span>some</span> text!</div>
2742              
2743             my $text = $doc->getElementById('divA')->textContent;
2744             # The text variable is now: 'This is some text!'
2745            
2746             $doc->getElementById('divA')->textContent = 'This text is different!';
2747             # The HTML for divA is now:
2748             # <div id="divA">This text is different!</div>
2749              
2750             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent>
2751              
2752             =head2 title
2753              
2754             A string containing the text that appears in a popup box when mouse is over the element.
2755              
2756             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/title> for more information.
2757              
2758             =head2 translate
2759              
2760             A boolean value representing the translation.
2761              
2762             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/translate> for more information.
2763              
2764             =head1 METHODS
2765              
2766             =head2 addEventListener
2767              
2768             Registers an event handler to a specific event type on the element. This is inherited from L<HTML::Object::EventTarget>
2769              
2770             See L<HTML::Object::EventTarget/addEventListener> for more information.
2771              
2772             =head2 after
2773              
2774             Inserts a list of L<element|HTML::Object::Element> or HTML string in the L<children|/children> list of the L<element|HTML::Object::Element>'s parent, just after the L<element|HTML::Object::Element>.
2775              
2776             For example:
2777              
2778             Inserting an element:
2779              
2780             my $container = $doc->createElement("div");
2781             my $p = $doc->createElement("p");
2782             $container->appendChild( $p );
2783             my $span = $doc->createElement("span");
2784              
2785             $p->after( $span );
2786              
2787             say( $container->outerHTML );
2788             # "<div><p></p><span></span></div>"
2789              
2790             Inserting an element and text
2791              
2792             my $container = $doc->createElement("div");
2793             my $p = $doc->createElement("p");
2794             $container->appendChild( $p );
2795             my $span = $doc->createElement("span");
2796              
2797             $p->after( $span, "Text" );
2798              
2799             say( $container->outerHTML );
2800             # "<div><p></p><span></span>Text</div>"
2801              
2802             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/after>
2803              
2804             =head2 append
2805              
2806             Inserts a set of L<element|HTML::Object::Element> objects or HTML strings after the last child of the L<element|HTML::Object::Element>.
2807              
2808             It returns the objects thus inserted as an L<array object|Module::Generic::Array>.
2809              
2810             Differences from L</appendChild>:
2811              
2812             =over 4
2813              
2814             =item 1. L</append> allows you to also append HTML strings, whereas L</appendChild> only accepts L<element|HTML::Object::Element> objects.
2815              
2816             =item 2. L</append> returns the current L<element|HTML::Object::Element> object, whereas L</appendChild> returns the appended L<element|HTML::Object::Element> object.
2817              
2818             =item 3. L</append> can append several L<element|HTML::Object::Element> and strings, whereas L</appendChild> can only append one L<element|HTML::Object::Element>.
2819              
2820             =back
2821              
2822             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/append>
2823              
2824             =head2 appendChild
2825              
2826             Adds the specified child L<element|HTML::Object::Element> argument as the last child to the current L<element|HTML::Object::Element>. If the argument referenced an existing L<element|HTML::Object::Element> on the DOM tree, the element will be detached from its current position and attached at the new position.
2827              
2828             Returns the appended object.
2829              
2830             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild>
2831              
2832             =for assignedSlot
2833              
2834             =head2 attachInternals
2835              
2836             This does nothing.
2837              
2838             Normally, under JavaScript, this would set or return an C<ElementInternals> object, and enables a custom element to participate in HTML forms.
2839              
2840             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attachInternals> for more information.
2841              
2842             =head2 before
2843              
2844             Inserts a set of L<element|HTML::Object::Element> or HTML strings in the L<children|/children> list of the L<element|HTML::Object::Element>'s parent, just before the L<element|HTML::Object::Element>.
2845              
2846             For example:
2847              
2848             my $container = $doc->createElement("div");
2849             my $p = $doc->createElement("p");
2850             $container->appendChild( $p );
2851             my $span = $doc->createElement("span");
2852              
2853             $p->before(span);
2854              
2855             say( $container->outerHTML );
2856             # "<div><span></span><p></p></div>"
2857              
2858             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/before>
2859              
2860             =head2 blur
2861              
2862             This does nothing.
2863              
2864             Normally, under JavaScript, this would remove keyboard focus from the currently focused element.
2865              
2866             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/blur> for more information.
2867              
2868             =head2 click
2869              
2870             Sends a mouse click event to the element.
2871              
2872             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/click> for more information.
2873              
2874             =head2 cloneNode
2875              
2876             Clone an element, and optionally, all of its contents. By default, it clones the content of the element.
2877              
2878             Returns the element cloned.
2879              
2880             =head2 closest
2881              
2882             Returns the L<element|HTML::Object::Element> which is the closest ancestor of the current L<element|HTML::Object::Element> (or the current L<element|HTML::Object::Element> itself) which matches the selectors given in parameter.
2883              
2884             For example:
2885              
2886             <article>
2887             <div id="div-01">Here is div-01
2888             <div id="div-02">Here is div-02
2889             <div id="div-03">Here is div-03</div>
2890             </div>
2891             </div>
2892             </article>
2893              
2894             my $el = $doc->getElementById('div-03');
2895             my $r1 = $el->closest("#div-02");
2896             # returns the element with the id C<div-02>
2897              
2898             my $r2 = $el->closest("div div");
2899             # returns the closest ancestor which is a div in div, here it is the C<div-03> itself
2900              
2901             my $r3 = $el->closest("article > div");
2902             # returns the closest ancestor which is a div and has a parent article, here it is the C<div-01>
2903              
2904             my $r4 = $el->closest(":not(div)");
2905             # returns the closest ancestor which is not a div, here it is the outmost article
2906              
2907             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/closest>
2908              
2909             =for cmp
2910              
2911             =head2 compareDocumentPosition
2912              
2913             Compares the position of the current element against another element in any other document.
2914              
2915             my $head = $doc->head;
2916             my $body = $doc->body;
2917              
2918             if( $head->compareDocumentPosition( $body ) & HTML::Object::Element->Node.DOCUMENT_POSITION_FOLLOWING )
2919             {
2920             say( 'Well-formed document' );
2921             }
2922             else
2923             {
2924             say( '<head> is not before <body>' );
2925             }
2926              
2927             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition>
2928              
2929             =head2 contains
2930              
2931             Returns true or false value indicating whether or not an element is a descendant of the calling element.
2932              
2933             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Node/contains>
2934              
2935             =for css
2936              
2937             =for css_cache_check
2938              
2939             =for css_cache_store
2940              
2941             =for data
2942              
2943             =head2 dispatchEvent
2944              
2945             Dispatches an event to this element in the DOM and returns a boolean value that indicates whether no handler canceled the event.
2946              
2947             This is inherited from L<HTML::Object::EventTarget>
2948            
2949             See L<HTML::Object::EventTarget/dispatchEvent> for more information.
2950              
2951             =for each
2952              
2953             =for empty
2954              
2955             =for eq
2956              
2957             =for even
2958              
2959             =for exists
2960              
2961             =head2 focus
2962              
2963             This does nothing.
2964              
2965             Normally, under JavaScript, this would make the element the current keyboard focus.
2966              
2967             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus> for more information.
2968              
2969             =head2 getAttribute
2970              
2971             Retrieves the value of the named attribute from the current node and returns it as a string.
2972              
2973             Example:
2974              
2975             my $parser = HTML::Object::DOM->new;
2976             my $doc = $parser->parse_data( q{<div id="div1">Hi Champ!</div>} );
2977              
2978             # in a console
2979             my $div1 = $doc->getElementById('div1');
2980             # => <div id="div1">Hi Champ!</div>
2981              
2982             my $exampleAttr = $div1->getAttribute('id');
2983             # => "div1"
2984              
2985             my $align = $div1->getAttribute('align');
2986             # => null
2987              
2988             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute>
2989              
2990             =head2 getAttributeNames
2991              
2992             Returns an L<array object|Module::Generic::Array> of attribute names from the current element.
2993              
2994             Example:
2995              
2996             <div id="hello" class="opened" data-status="ok"></div>
2997              
2998             my $div = $doc->getElementById( 'hello' );
2999             my $arr = $div->getAttributeNames; # id class data-status
3000             $arr->foreach(sub
3001             {
3002             say $_;
3003             });
3004             # would print:
3005             # id
3006             # class
3007             # data-status
3008              
3009             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttributeNames>
3010              
3011             =head2 getAttributeNode
3012              
3013             Retrieves the node representation of the named attribute from the current node and returns it as an Attr.
3014              
3015             Example:
3016              
3017             # html: <div id="top" />
3018             my $t = $doc->getElementById("top");
3019             my $idAttr = $t->getAttributeNode("id");
3020             say( $idAttr->value eq "top" ); # 1
3021              
3022             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttributeNode>
3023              
3024             =head2 getAttributeNodeNS
3025              
3026             This always returns C<undef> since there is no support for namespace.
3027              
3028             Retrieves the node representation of the attribute with the specified name and namespace, from the current node and returns it as an Attr.
3029              
3030             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttributeNodeNS>
3031              
3032             =head2 getAttributeNS
3033              
3034             This always returns C<undef> since there is no support for namespace.
3035              
3036             Retrieves the value of the attribute with the specified namespace and name from the current node and returns it as a string.
3037              
3038             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttributeNS>
3039              
3040             =head2 getElementsByClassName
3041              
3042             Provided with a space-delimited list of classes, or a list of classes, and this returns an L<array object|Module::Generic::Array> that contains all descendants of the current element that possess the list of classes given in the parameter.
3043              
3044             Example:
3045              
3046             my $array = $element->getElementsByClassName('test');
3047              
3048             This example finds all elements that have a class of C<test>, which are also a descendant of the element that has the id of C<main>:
3049              
3050             my $array = $doc->getElementById('main')->getElementsByClassName('test');
3051              
3052             To find elements whose class lists include both the C<red> and C<test> classes:
3053              
3054             $element->getElementsByClassName('red test');
3055              
3056             or, equivalently:
3057              
3058             $element->getElementsByClassName('red', 'test');
3059              
3060             Inspecting the results:
3061              
3062             my $matches = $element->getElementsByClassName('colorbox');
3063              
3064             for( my $i=0; $i<$matches->length; $i++ )
3065             {
3066             $matches->[$i]->classList->remove('colorbox');
3067             $matches->get($i)->classList->add('hueframe');
3068             }
3069              
3070             or, somewhat more streamlined:
3071              
3072             $matches->foreach(sub
3073             {
3074             $_->classList->remove('colorbox');
3075             $_->classList->add('hueframe');
3076             });
3077              
3078             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/getElementsByClassName>
3079              
3080             =head2 getElementsByTagName
3081              
3082             Provided with a tag name, and this returns an L<array object|Module::Generic::Array> containing all descendant elements, of a particular tag name, from the current element.
3083              
3084             The special string C<*> represents all elements.
3085              
3086             Example:
3087              
3088             # Check the status of each data cell in a table
3089             my $cells = $doc->getElementById('forecast-table')->getElementsByTagName('td');
3090              
3091             $cells->foreach(sub
3092             {
3093             my $cell = shift( @_ ); $_ is available too
3094             my $status = $cell->getAttribute('data-status');
3095             if( $status === 'open' )
3096             {
3097             # Grab the data
3098             }
3099             });
3100              
3101             All descendants of the specified element are searched, but not the element itself.
3102              
3103             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/getElementsByTagName>
3104              
3105             =head2 getElementsByTagNameNS
3106              
3107             This always returns C<undef> since there is no support for namespace.
3108              
3109             Normally, under JavaScript, this would return a live HTMLCollection containing all descendant elements, of a particular tag name and namespace, from the current element.
3110              
3111             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/getElementsByTagNameNS>
3112              
3113             =head2 getElementsByTagNames
3114              
3115             Provided with a space-separated string of tag names, or an array reference of tag names or a list of tag names, and this will return an L<array object|Module::Generic::Array> of descendant elements matching those tag names.
3116              
3117             This is a non-standard method, courtesy of L<John Resig|https://johnresig.com/blog/comparing-document-position/#postcomment>
3118              
3119             =for getLocalName
3120              
3121             =head2 getNextSibling
3122              
3123             This non-standard method is an alias for the property L</nextSibling>
3124              
3125             =for getNodePath
3126              
3127             =head2 getPreviousSibling
3128              
3129             This non-standard method is an alias for the property L</previousSibling>
3130              
3131             =head2 getRootNode
3132              
3133             Returns the context object's root which optionally includes the shadow root if it is available.
3134              
3135             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Node/getRootNode>
3136              
3137             =for getValue
3138              
3139             =head2 hasAttribute
3140              
3141             Provided with an attribute name and this returns a boolean value indicating if the element has the specified attribute or not.
3142              
3143             Example:
3144              
3145             my $foo = $doc->getElementById("foo");
3146             if( $foo->hasAttribute("bar") )
3147             {
3148             # do something
3149             }
3150              
3151             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/hasAttribute>
3152              
3153             =head2 hasAttributeNS
3154              
3155             This always returns C<undef> since there is no support for namespace.
3156              
3157             Returns a boolean value indicating if the element has the specified attribute, in the specified namespace, or not.
3158              
3159             =head2 hasAttributes
3160              
3161             Returns a boolean value indicating if the element has one or more HTML attributes present.
3162              
3163             Example:
3164              
3165             my $foo = $doc->getElementById('foo');
3166             if( $foo->hasAttributes() )
3167             {
3168             # Do something with '$foo->attributes'
3169             }
3170              
3171             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/hasAttributes>
3172              
3173             =head2 hasChildNodes
3174              
3175             Normally, under JavaScript, this would return a boolean value indicating whether or not the element has any child elements.
3176              
3177             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Node/hasChildNodes>
3178              
3179             =for hasClass
3180              
3181             =for hide
3182              
3183             =head2 hidePopover
3184              
3185             Experimental
3186              
3187             This does nothing.
3188              
3189             Normally, under JavaScript, this would hide a popover element by removing it from the top layer and styling it with display: none.
3190              
3191             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/hidePopover> for more information.
3192              
3193             =for html
3194              
3195             =for index
3196              
3197             =head2 insertAdjacentElement
3198              
3199             Provided with a C<position> and an L<element|HTML::Object::DOM::Element> and this inserts a given L<element|HTML::Object::DOM::Element> node at a given C<position> relative to the L<element|HTML::Object::DOM::Element> it is invoked upon.
3200              
3201             It returns the element that was inserted, or C<undef>, if the insertion failed.
3202              
3203             It returns a C<HTML::Object::SyntaxError> error if the C<position> specified is not a recognised value.
3204              
3205             It returns a C<HTML::Object::TypeError> error if the C<element> specified is not a valid element.
3206              
3207             THe C<position> can be any one of (case insensitive)
3208              
3209             =over 4
3210              
3211             =item C<beforebegin>
3212              
3213             Before the targetElement itself.
3214              
3215             =item C<afterbegin>
3216              
3217             Just inside the targetElement, before its first child.
3218              
3219             =item C<beforeend>
3220              
3221             Just inside the targetElement, after its last child.
3222              
3223             =item C<afterend>
3224              
3225             After the targetElement itself.
3226              
3227             =back
3228              
3229             <!-- beforebegin -->
3230             <p>
3231             <!-- afterbegin -->
3232             foo
3233             <!-- beforeend -->
3234             </p>
3235             <!-- afterend -->
3236              
3237             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentElement>
3238              
3239             =head2 insertAdjacentHTML
3240              
3241             Provided with a C<position> and an L<element|HTML::Object::DOM::Element> and this parses the text as HTML and inserts the resulting nodes into the tree in the position given.
3242              
3243             This takes the same C<position> parameter as L</insertAdjacentElement>
3244              
3245             It returns the newly created objects from parsing the html data and that were inserted, as an L<array object|Module::Generic::Array>, or C<undef>, if the insertion failed.
3246              
3247             It returns a C<HTML::Object::SyntaxError> error if the C<position> specified is not a recognised value.
3248              
3249             It returns a C<HTML::Object::TypeError> error if the C<element> specified is not a valid element.
3250              
3251             Example:
3252              
3253             # <div id="one">one</div>
3254             my $d1 = $doc->getElementById('one');
3255             $d1->insertAdjacentHTML('afterend', q{<div id="two">two</div>});
3256              
3257             # At this point, the new structure is:
3258             # <div id="one">one</div><div id="two">two</div>
3259              
3260             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML>
3261              
3262             =head2 insertAdjacentText
3263              
3264             Provided with a C<position> and a C<text> string, or a list of C<text> string that will be concatenated, and this inserts the given C<text> at the given C<position> relative to the element it is invoked upon.
3265              
3266             This takes the same C<position> parameter as L</insertAdjacentElement>
3267              
3268             It returns the newly created text L<node|HTML::Object::DOM::Node> that was inserted, or C<undef>, if the insertion failed.
3269              
3270             It returns a C<HTML::Object::SyntaxError> error if the C<position> specified is not a recognised value.
3271              
3272             It returns a C<HTML::Object::TypeError> error if the C<element> specified is not a valid element.
3273              
3274             Example:
3275              
3276             $beforeBtn->addEventListener( click => sub
3277             {
3278             $para->insertAdjacentText('afterbegin',$textInput->value);
3279             });
3280              
3281             $afterBtn->addEventListener( click => sub
3282             {
3283             $para->insertAdjacentText('beforeend',$textInput->value);
3284             });
3285              
3286             Or
3287              
3288             $para->insertAdjacentText( beforesend => 'Some chunks', 'of', 'text to insert' );
3289              
3290             Or, more simply:
3291              
3292             $para->insertAdjacentText( beforesend => qw( Some chunks of text to insert ) );
3293              
3294             But, the following would fail since there is no data provided:
3295              
3296             $para->insertAdjacentText( beforesend => '' );
3297             $para->insertAdjacentText( beforesend => undef );
3298             $para->insertAdjacentText( beforesend => '', '', '' );
3299              
3300             So you have to make sure the data submitted is not zero length.
3301              
3302             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentText>
3303              
3304             =head2 insertBefore
3305              
3306             Inserts an element before the reference element as a child of a specified parent element.
3307              
3308             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Node/insertBefore>
3309              
3310             =for isa_collection
3311              
3312             =for isa_element
3313              
3314             =head2 isDefaultNamespace
3315              
3316             Accepts a namespace URI as an argument and returns a boolean value with a value of true if the namespace is the default namespace on the given element or false if not.
3317              
3318             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Node/isDefaultNamespace>
3319              
3320             =head2 isEqualNode
3321              
3322             Returns a boolean value which indicates whether or not two elements are of the same type and all their defining data points match.
3323              
3324             Two elements are equal when they have the same type, defining characteristics (this would be their ID, number of children, and so forth), its attributes match, and so on. The specific set of data points that must match varies depending on the types of the elements.
3325              
3326             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Node/isEqualNode>
3327              
3328             =head2 isSameNode
3329              
3330             Returns a boolean value indicating whether or not the two elements are the same (that is, they reference the same object).
3331              
3332             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Node/isSameNode>
3333              
3334             =for length
3335              
3336             =for load
3337              
3338             =head2 lookupNamespaceURI
3339              
3340             Accepts a prefix and returns the namespace URI associated with it on the given element if found (and C<undef> if not). Supplying C<undef> for the prefix will return the default namespace.
3341              
3342             This always return an empty string and C<http://www.w3.org/XML/1998/namespace> if the prefix is C<xml>
3343              
3344             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Node/lookupNamespaceURI>
3345              
3346             =head2 lookupPrefix
3347              
3348             This always returns C<undef>.
3349              
3350             Returns a string containing the prefix for a given namespace URI, if present, and C<undef> if not.
3351              
3352             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Node/lookupPrefix>
3353              
3354             =for map
3355              
3356             =head2 matches
3357              
3358             Provided with a CSS C<selector> string, and an optional hash or hash reference, and this returns a boolean value indicating whether or not the element would be selected by the specified C<selector> string.
3359              
3360             Example:
3361              
3362             <ul id="birds">
3363             <li>Orange-winged parrot</li>
3364             <li class="endangered">Philippine eagle</li>
3365             <li>Great white pelican</li>
3366             </ul>
3367              
3368             my $birds = $doc->getElementsByTagName('li');
3369              
3370             for( my $i = 0; $i < $birds->length; $i++ )
3371             {
3372             if( $birds->[$i]->matches('.endangered') )
3373             {
3374             say('The ' + $birds->[i]->textContent + ' is endangered!');
3375             }
3376             }
3377             # The Philippine eagle is endangered!
3378              
3379             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/matches>
3380              
3381             =for name
3382              
3383             =head2 new_attribute
3384              
3385             Returns a new L<HTML::Object::DOM::Attribute> object, passing it whatever arguments were provided and return the newly instantiated object.
3386              
3387             If an error occurred, this returns C<undef> and sets an L<error|Module::Generic/error>
3388              
3389             =head2 new_closing
3390              
3391             Returns a new L<HTML::Object::DOM::Closing> object, passing it whatever arguments were provided and return the newly instantiated object.
3392              
3393             If an error occurred, this returns C<undef> and sets an L<error|Module::Generic/error>
3394              
3395             =head2 new_collection
3396              
3397             Returns a new L<HTML::Object::DOM::Collection> object, passing it whatever arguments were provided and return the newly instantiated object.
3398              
3399             If an error occurred, this returns C<undef> and sets an L<error|Module::Generic/error>
3400              
3401             =head2 new_comment
3402              
3403             Returns a new L<HTML::Object::DOM::Comment> object, passing it whatever arguments were provided and return the newly instantiated object.
3404              
3405             If an error occurred, this returns C<undef> and sets an L<error|Module::Generic/error>
3406              
3407             =head2 new_document
3408              
3409             Returns a new L<HTML::Object::DOM::Document> object, passing it whatever arguments were provided and return the newly instantiated object.
3410              
3411             If an error occurred, this returns C<undef> and sets an L<error|Module::Generic/error>
3412              
3413             =head2 new_element
3414              
3415             Returns a new L<HTML::Object::DOM::Element> object, passing it whatever arguments were provided and return the newly instantiated object.
3416              
3417             If an error occurred, this returns C<undef> and sets an L<error|Module::Generic/error>
3418              
3419             =head2 new_nodelist
3420              
3421             Returns a new L<HTML::Object::DOM::NodeList> object, passing it whatever arguments were provided and return the newly instantiated object.
3422              
3423             If an error occurred, this returns C<undef> and sets an L<error|Module::Generic/error>
3424              
3425             =head2 new_parser
3426              
3427             Returns a new L<HTML::Object::DOM> object, passing it whatever arguments were provided and return the newly instantiated object.
3428              
3429             If an error occurred, this returns C<undef> and sets an L<error|Module::Generic/error>
3430              
3431             =for new_root
3432              
3433             =head2 new_space
3434              
3435             Returns a new L<HTML::Object::DOM::Space> object, passing it whatever arguments were provided and return the newly instantiated object.
3436              
3437             If an error occurred, this returns C<undef> and sets an L<error|Module::Generic/error>
3438              
3439             =head2 new_text
3440              
3441             Returns a new L<HTML::Object::DOM::Text> object, passing it whatever arguments were provided and return the newly instantiated object.
3442              
3443             If an error occurred, this returns C<undef> and sets an L<error|Module::Generic/error>
3444              
3445             =head2 normalize
3446              
3447             Clean up all the text elements under this element (merge adjacent, remove empty).
3448              
3449             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Node/normalize>
3450              
3451             =for odd
3452              
3453             =head2 prepend
3454              
3455             Provided with a list of L<node objects|HTML::Object::DOM::Node> (this includes L<element objects|HTML::Object::DOM::Element>) or C<text>, or a mixture of them and this inserts them before the first child of the L<element|HTML::Object::DOM::Element>.
3456              
3457             It returns the objects thus inserted as an L<array object|Module::Generic::Array>.
3458              
3459             It returns a C<HTML::Object::HierarchyRequestError> error if the objects cannot be inserted at the specified point into the hierarchy.
3460              
3461             It returns a C<HTML::Object::TypeError> error if any argument provided is neither a text string nor a L<node object|HTML::Object::DOM::Node>.
3462              
3463             It returns a C<HTML::Object::SyntaxError> error if no argument was provided.
3464              
3465             Upon success, it returns an L<array objects|Module::Generic::Array> of the nods thus prepended, and upon error, it returns undef and sets one of the errors aforementioned.
3466              
3467             Example:
3468              
3469             Prepending an element:
3470              
3471             my $div = $doc->createElement("div");
3472             my $p = $doc->createElement("p");
3473             my $span = $doc->createElement("span");
3474             $div->append($p);
3475             $div->prepend($span);
3476              
3477             # Array object containing <span>, <p>
3478             my $list = $div->childNodes;
3479              
3480             Prepending text
3481              
3482             my $div = $doc->createElement("div");
3483             $div->append("Some text");
3484             $div->prepend("Headline: ");
3485              
3486             # "Headline: Some text"
3487             say( $div->textContent );
3488              
3489             Prepending both an element and some text
3490              
3491             my $div = $doc->createElement("div");
3492             my $p = $doc->createElement("p");
3493             $div->prepend("Some text", $p);
3494              
3495             # Array object containing "Some text", <p>
3496             my $list = $div->childNodes;
3497              
3498             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/prepend>
3499              
3500             =for prependTo
3501              
3502             =for promise
3503              
3504             =for prop
3505              
3506             =head2 querySelector
3507              
3508             Provided with a list of CSS C<selector> strings and this returns the first L<element|HTML::Object::DOM::Element> that is a descendant of the L<element|HTML::Object::DOM::Element> on which it is invoked that matches the specified group of selectors.
3509              
3510             If returns a L<smart undef|Module::Generic/new_null> if nothing is found (to differentiate from an error and still treated as false), and C<undef> upon error and sets an L<error|Module::Generic/error>.
3511              
3512             It returns a C<HTML::Object::SyntaxError> error if any of the selector provided is not a valid selector.
3513              
3514             Example:
3515              
3516             <div>
3517             <h6>Page Title</h6>
3518             <div id="parent">
3519             <span>Love is Kind.</span>
3520             <span>
3521             <span>Love is Patient.</span>
3522             </span>
3523             <span>
3524             <span>Love is Selfless.</span>
3525             </span>
3526             </div>
3527             </div>
3528              
3529             my $parentElement = $doc->querySelector('#parent');
3530             # would need to check that $parentElement is not undef here through...
3531             my $allChildren = $parentElement->querySelectorAll(":scope > span");
3532             $allChildren->foreach(sub
3533             {
3534             my $item = shift( @_ );
3535             $item->classList->add("red");
3536             });
3537              
3538             <div>
3539             <h6>Page Title</h6>
3540             <div id="parent">
3541             <span class="red">Love is Kind.</span>
3542             <span class="red">
3543             <span>Love is Patient.</span>
3544             </span>
3545             <span class="red">
3546             <span>Love is Selfless.</span>
3547             </span>
3548             </div>
3549             </div>
3550              
3551             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelector>
3552              
3553             =head2 querySelectorAll
3554              
3555             Provided with a list of CSS C<selector> strings and this returns an L<array object|Module::Generic::Array> representing a list of L<elements|HTML::Object::DOM::Element> matching the specified group of C<selectors> which are descendants of the L<element|HTML::Object::DOM::Element> on which the method was called.
3556              
3557             It returns a C<HTML::Object::SyntaxError> error if any of the selector provided is not a valid selector.
3558              
3559             Example:
3560              
3561             my $matches = $myBox->querySelectorAll("p");
3562             my $matches = $myBox->querySelectorAll("div.note, div.alert");
3563              
3564             Get a list of the document's <p> elements whose immediate parent element is a C<div> with the class C<highlighted> and which are located inside a container whose ID is C<test>.
3565              
3566             my $container = $doc->querySelector("#test");
3567             my $matches = $container->querySelectorAll("div.highlighted > p");
3568              
3569             Here with an attribute C<selector>
3570              
3571             my $container = $doc->querySelector("#userlist");
3572             my $matches = $container->querySelectorAll("li[data-active='1']");
3573              
3574             To access the matched element, see L<Module::Generic::Array/foreach> for example:
3575              
3576             $matches->foreach(sub
3577             {
3578             my $elem = shift( @_ ); # $_ is available too
3579             # Do something with $elem
3580            
3581             # To satisfy array object's foreach and avoid ending abruptly the loop
3582             return(1);
3583             });
3584              
3585             or
3586              
3587             foreach my $elem ( @$matches )
3588             {
3589             # Do something with $elem
3590             }
3591              
3592             A word of caution on some edge case. While the JavaScript equivalent of C<querySelectorAll> takes a document global view at the CSS selector provided, here in perl, it only matches descendants.
3593              
3594             For example, the following would return 1 in JavaScript while it would return 0 in our implementation.
3595              
3596             <div class="outer">
3597             <div class="select">
3598             <div class="inner"></div>
3599             </div>
3600             </div>
3601              
3602             With JavaScript:
3603              
3604             var select = document.querySelector('.select');
3605             var inner = select.querySelectorAll('.outer .inner');
3606             inner.length; // 1, not 0 !
3607              
3608             With Perl:
3609              
3610             my $select = $doc->querySelector('.select');
3611             my $inner = $select->querySelectorAll('.outer .inner');
3612             $inner->length; // 0, not 1 !!
3613              
3614             Why is that? Because, when JavaScript does it search for the element whose class is C<inner> and who is inside another element whose class is C<outer>, it does not bother JavaScript that C<outer> is a parent of the elment on which C<querySelectorAll> is being called, because it retains only the last part of the selector, i.e. C<inner>.
3615              
3616             In perl, there is no such elaborate CSS engine that would allow us this level of granularity, and thus it would look for C<.outer .inner> below C<select> and since there are none, it would return C<0>
3617              
3618             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelectorAll>
3619              
3620             =for rank
3621              
3622             =head2 remove
3623              
3624             Removes the element from the children list of its parent.
3625              
3626             It returns true upon success, and upon error, it returns C<undef> and sets an L<error|Module::Generic/error>
3627              
3628             It returns an C<HTML::Object::HierarchyRequestError> if the L<element|HTML::Object::DOM::Element> does not have any parent, like so:
3629              
3630             my $div = $doc->createElement( 'div' );
3631             $div->remove; # Error returned !
3632              
3633             Example:
3634              
3635             <div id="div-01">Here is div-01</div>
3636             <div id="div-02">Here is div-02</div>
3637             <div id="div-03">Here is div-03</div>
3638              
3639             my $el = $doc->getElementById('div-02');
3640             $el->remove(); # Removes the div with the 'div-02' id
3641              
3642             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/remove>
3643              
3644             =for removeAttr
3645              
3646             =head2 removeAttribute
3647              
3648             Provided with an attribute C<name> and this removes the attribute with the specified C<name> from the L<element|HTML::Object::DOM::Element>.
3649              
3650             It returns true upon success or false otherwise.
3651              
3652             It returns an C<HTML::Object::SyntaxError> if no attribute name was provided.
3653              
3654             Example:
3655              
3656             # Given: <div id="div1" align="left" width="200px">
3657             $doc->getElementById("div1")->removeAttribute("align");
3658             # Now: <div id="div1" width="200px">
3659              
3660             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttribute>
3661              
3662             =head2 removeAttributeNode
3663              
3664             Provided with an L<attribute node|HTML::Object::DOM::Attribute> and this removes the node representation of the named attribute from the current node. This is similar to L</removeAttribute>, except that L</removeAttribute> takes a string as attribute name, while L</removeAttributeNode> takes an L<attribute object|HTML::Object::DOM::Attribute>.
3665              
3666             Upon error, it returns C<undef> and sets an L<error|Module::Generic/error>.
3667              
3668             It returns an C<HTML::Object::SyntaxError> if the value provided is not an L<attribute object|HTML::Object::DOM::Attribute>.
3669              
3670             Example:
3671              
3672             # Given: <div id="top" align="center" />
3673             my $d = $doc->getElementById("top");
3674             my $d_align = $d->getAttributeNode("align");
3675             $d->removeAttributeNode( $d_align ); # <-- passing the attribute object
3676             # align is now removed: <div id="top" />
3677              
3678             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttributeNode>
3679              
3680             =head2 removeAttributeNS
3681              
3682             This always return C<undef> since there is no support for namespace.
3683              
3684             Under JavaScript, this would remove the attribute with the specified name and namespace, from the current node.
3685              
3686             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttributeNS>
3687              
3688             =head2 removeChild
3689              
3690             Removes a child element from the current element, which must be a child of the current element.
3691              
3692             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Node/removeChild>
3693              
3694             =for removeClass
3695              
3696             =head2 removeEventListener
3697              
3698             Removes an event listener from the element. This is inherited from L<HTML::Object::EventTarget>
3699            
3700             See L<HTML::Object::EventTarget/removeEventListener> for more information.
3701              
3702             =head2 replaceChild
3703              
3704             Replaces one child element of the current one with the second one given in parameter.
3705              
3706             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Node/replaceChild>
3707              
3708             =head2 replaceChildren
3709              
3710             Replaces the existing children of a L<Node|HTML::Object::DOM::Node> with a specified new set of children.
3711             These can be HTML strings or L<node objects|HTML::Object::DOM::Node>.
3712              
3713             It returns an L<array object|Module::Generic::Array> of the replaced or removed children. Note that those replaced children will have their parent value set to C<undef>.
3714              
3715             You can call it on a node without any argument specified to remove all of its children:
3716              
3717             $myNode->replaceChildren();
3718              
3719             This method also enables you to easily transfer nodes between elements since each new nodes provided will be detached from their previous parent and re-attached under the current element.
3720              
3721             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/replaceChildren>
3722              
3723             =head2 replaceWith
3724              
3725             Replaces the element in the children list of its parent with a set of Node or DOMString objects.
3726              
3727             This method replaces the current L<element|HTML::Object::DOM::Element> in the children list of its parent with a set of L<node|HTML::Object::DOM::Node> or strings. Strings that look like HTML are parsed and added as L<HTML::Object::DOM::Element> and other text strings are inserted as equivalent L<Text nodes|HTML::Object::DOM::Text>.
3728              
3729             This returns a C<HTML::Object::HierarchyRequestError> when a L<node|HTML::Object::DOM::Node> provided cannot be inserted at the specified point in the hierarchy.
3730              
3731             It returns an L<array object|Module::Generic::Array> of the newly inserted nodes.
3732              
3733             Example:
3734              
3735             my $div = $doc->createElement("div");
3736             my $p = $doc->createElement("p");
3737             $div->appendChild( $p );
3738             # Now div is: <div><p></p></div>
3739             my $span = $doc->createElement("span");
3740              
3741             $p->replaceWith( $span );
3742              
3743             say( $div->outerHTML );
3744             # "<div><span></span></div>"
3745              
3746             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/replaceWith>
3747              
3748             =for set_namespace
3749              
3750             =head2 setAttribute
3751              
3752             Provided with a C<name> and a C<value> and this sets the value of an attribute on the specified L<element|HTML::Object::DOM::Element>. If the attribute already exists, the value is updated; otherwise a new attribute is added with the specified name and value.
3753              
3754             To get the current value of an attribute, use L</getAttribute>; to remove an attribute, use L</removeAttribute>.
3755              
3756             Contrary to the original JavaScript equivalent, providing a C<value> with C<undef> results in removing the attribute altogether.
3757              
3758             It returns the current element upon success and upon error, it returns C<undef> and sets an L<error|Module::Generic/error>
3759              
3760             It returns an C<HTML::Object::InvalidCharacterError> object when the attribute name provided contains illegal characters.
3761              
3762             It returns an C<HTML::Object::SyntaxError> object when no attribute name was provided.
3763              
3764             Example:
3765              
3766             Set attributes on a button
3767              
3768             <button>Hello World</button>
3769              
3770             my $b = $doc->querySelector("button");
3771              
3772             $b->setAttribute("name", "helloButton");
3773             $b->setAttribute("disabled", "");
3774             # <button name="helloButton" disabled="">Hello World</button>
3775              
3776             To remove an attribute (same as calling L</removeAttribute>)
3777              
3778             $b->setAttribute("disabled", undef);
3779              
3780             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute>
3781              
3782             =head2 setAttributeNode
3783              
3784             Provided with an L<attribute object|HTML::Object::DOM::Attribute> and this sets, or possibly replace, the node representation of the named attribute from the current node.
3785              
3786             If a previous L<attribute|HTML::Object::DOM::Attribute> existed, it will be replaced and returned.
3787              
3788             Example:
3789              
3790             <div id="one" align="left">one</div>
3791             <div id="two">two</div>
3792              
3793             my $d1 = $doc->getElementById('one');
3794             my $d2 = $doc->getElementById('two');
3795             my $a = $d1->getAttributeNode('align');
3796              
3797             $d2->setAttributeNode( $a.cloneNode($true) );
3798              
3799             # Returns: 'left'
3800             say( $d2->attributes->get( 'align' )->value );
3801             # or
3802             say( $d2->attributes->{align}->value );
3803              
3804             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttributeNode>
3805              
3806             =head2 setAttributeNodeNS
3807              
3808             This always return C<undef> since namespace is not supported.
3809              
3810             Under JavaScript, this would set the L<node|HTML::Object::DOM::Attribute> representation of the attribute with the specified C<name> and C<namespace>, from the current node.
3811              
3812             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttributeNS>
3813              
3814             =for show
3815              
3816             =head2 showPopover
3817              
3818             Experimental
3819              
3820             This does nothing.
3821              
3822             Normally, under JavaScript, this would show a popover element by adding it to the top layer and removing display: none; from its styles.
3823              
3824             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/showPopover> for more information.
3825              
3826             =for string_value
3827              
3828             =for tagname
3829              
3830             =head2 toggleAttribute
3831              
3832             Provided with an attribute name and an optiona C<force> value, and this toggles a boolean attribute, removing it if it is present and adding it if it is not present, on the specified element.
3833              
3834             C<force> is a boolean value to determine whether the attribute should be added or removed, no matter whether the attribute is present or not at the moment.
3835              
3836             It returns C<true> if attribute C<name> is eventually present, and C<false> otherwise.
3837              
3838             It returns an C<HTML::Object::InvalidCharacterError> error if the specified attribute C<name> contains one or more characters which are not valid in attribute names.
3839              
3840             Example:
3841              
3842             To toggle the C<disabled> attribute of an input field
3843              
3844             <input value="text">
3845             <button>toggleAttribute("disabled")</button>
3846              
3847             my $button = $doc->querySelector("button");
3848             my $input = $doc->querySelector("input");
3849              
3850             $button->addEventListener( click => sub
3851             {
3852             $input->toggleAttribute("disabled");
3853             });
3854              
3855             See L<for more information|https://developer.mozilla.org/en-US/docs/Web/API/Element/toggleAttribute>
3856              
3857             =for toggleClass
3858              
3859             =head2 togglePopover
3860              
3861             Experimental
3862              
3863             This does nothing.
3864              
3865             Normally, under JavaScript, this would toggle a popover element between the hidden and showing states.
3866              
3867             See L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/togglePopover> for more information.
3868              
3869             =head2 toString
3870              
3871             Returns a string representation for this element.
3872              
3873             =head2 to_number
3874              
3875             Returns a L<HTML::Object::DOM::Number> object representing the text value of this element.
3876              
3877             =for xq
3878              
3879             =head1 EVENTS
3880              
3881             Listen to these events using L<HTML::Object::EventTarget/addEventListener> or by assigning an event listener to the C<oneventname> property of this interface.
3882              
3883             Under perl, few events are actually "fired" by L<HTML::Object> and L<for the others|https://developer.mozilla.org/en-US/docs/Web/API/Element#events> (also L<here|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement#events>), nothing prevents you from L<triggering|HTML::Object::EventTarget/dispatchEvent> whatever events you want, even private non-standard ones, and set up listeners for them.
3884              
3885             Below are the ones actually "fired" by L<HTML::Object>.
3886              
3887             =head2 change
3888              
3889             This event is fired when there has been some change to the underlying element. This is also available via the onchange property.
3890              
3891             =head2 error
3892              
3893             This event is fired when an error occurs for this element. This is also available via the onerror property.
3894              
3895             Example:
3896              
3897             $video->onerror = sub
3898             {
3899             say( "Error " . $video->error->code . "; details: " . $video->error->message );
3900             }
3901              
3902             =head1 EXTENDED
3903              
3904             Inheriting from L<HTML::Object::EventTarget> and extended with L<HTML::Object::XQuery>
3905              
3906             See detailed documentation in L<HTML::Object::XQuery>
3907              
3908             =head1 AUTHOR
3909              
3910             Jacques Deguest E<lt>F<jack@deguest.jp>E<gt>
3911              
3912             =head1 SEE ALSO
3913              
3914             L<HTML::Object::DOM>, L<HTML::Object::DOM::Attribute>, L<HTML::Object::DOM::Boolean>, L<HTML::Object::DOM::Closing>, L<HTML::Object::Collection>, L<HTML::Object::DOM::Comment>, L<HTML::Object::DOM::Declaration>, L<HTML::Object::DOM::Document>, L<HTML::Object::DOM::Element>, L<HTML::Object::Exception>, L<HTML::Object::Literal>, L<HTML::Object::DOM::Number>, L<HTML::Object::DOM::Root>, L<HTML::Object::DOM::Space>, L<HTML::Object::DOM::Text>, L<HTML::Object::XQuery>
3915              
3916             L<Mozilla DOM documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement>, L<Mozilla Element documentation|https://developer.mozilla.org/en-US/docs/Web/API/Element>
3917              
3918             =head1 COPYRIGHT & LICENSE
3919              
3920             Copyright(c) 2021 DEGUEST Pte. Ltd.
3921              
3922             All rights reserved
3923              
3924             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
3925              
3926             =cut