The Perl Toolchain Summit 2025 Needs You: You can help 🙏 Learn more

##----------------------------------------------------------------------------
## HTML Object - ~/lib/HTML/Object/DOM/Element/Table.pm
## Version v0.2.1
## Copyright(c) 2022 DEGUEST Pte. Ltd.
## Author: Jacques Deguest <jack@deguest.jp>
## Created 2021/12/23
## Modified 2022/09/20
## All rights reserved
##
##
## This program is free software; you can redistribute it and/or modify it
## under the same terms as Perl itself.
##----------------------------------------------------------------------------
BEGIN
{
use strict;
use warnings;
use vars qw( $VERSION );
use Want;
our $VERSION = 'v0.2.1';
};
use strict;
sub init
{
my $self = shift( @_ );
$self->{_init_strict_use_sub} = 1;
$self->SUPER::init( @_ ) || return( $self->pass_error );
$self->{tag} = 'table' if( !defined( $self->{tag} ) || !CORE::length( "$self->{tag}" ) );
$self->{_table_reset_caption} = 1;
$self->{_table_reset_tbody} = 1;
$self->{_table_reset_tfoot} = 1;
$self->{_table_reset_thead} = 1;
$self->{_table_reset_rows} = 1;
$self->{_reset_fields} = [qw( _table_reset_caption _table_reset_tbody _table_reset_tfoot _table_reset_thead _table_reset_rows )];
my $callback = sub
{
my $def = shift( @_ );
# my $info = [caller(5)];
# print( STDERR ref( $self ), "::children->callback(add): called from package ", $info->[0], " at line ", $info->[2], " for '", $def->{added}->[0], "'\n" );
# Our children were modified from outside our package.
# We need to check if it affects our rows and reset the cache accordingly
unless( $def->{caller}->[0] eq ref( $self ) )
{
# my $has_row = 0;
# for( @{$def->{data}} )
# {
# $has_row++, last if( $self->_is_a( $_ => 'HTML::Object::DOM::Element::TableRow' ) );
# }
# $self->_reset_table( 'rows' ) if( $has_row );
$self->_reset_table( 'all' );
}
return(1);
};
$self->children->callback( add => $callback );
$self->children->callback( remove => $callback );
return( $self );
}
# Note: deprecated property align is inherited
# Note: deprecated property bgColor
sub bgColor : lvalue { return( shift->_set_get_property( 'bgcolor', @_ ) ); }
# Note: deprecated property border
sub border : lvalue { return( shift->_set_get_property( 'border', @_ ) ); }
# Note: property caption
sub caption : lvalue { return( shift->_set_get_section( caption => 'HTML::Object::DOM::Element::TableCaption', @_ ) ); }
# Note: deprecated property cellPadding
sub cellPadding : lvalue { return( shift->_set_get_property( 'cellpadding', @_ ) ); }
# Note: deprecated property cellSpacing
sub cellSpacing : lvalue { return( shift->_set_get_property( 'cellspacing', @_ ) ); }
sub createCaption
{
my $self = shift( @_ );
if( $self->{_table_captions} && !$self->_is_table_reset( 'caption' ) && !$self->{_table_captions}->is_empty )
{
return( $self->{_table_captions}->first );
}
my $list = $self->children->grep(sub{ $self->_is_a( $_ => 'HTML::Object::DOM::Element::TableCaption' ) });
my $capt = $list->first;
if( !$capt )
{
$self->_load_class( 'HTML::Object::DOM::Element::TableCaption' ) ||
return( $self->pass_error );
$capt = HTML::Object::DOM::Element::TableCaption->new( @_ ) ||
return( $self->pass_error( HTML::Object::DOM::Element::TableCaption->error ) );
$capt->close;
$capt->parent( $self );
$self->children->unshift( $capt );
$list->unshift( $capt );
$self->reset(1);
$self->_load_class( 'HTML::Object::DOM::Collection' ) ||
return( $self->pass_error );
$self->{_table_captions} = HTML::Object::DOM::Collection->new( $list );
$self->_remove_table_reset( 'caption' );
}
return( $capt );
}
sub createTBody { return( shift->_create_tsection( tbody => @_ ) ); }
sub createTFoot { return( shift->_create_tsection( tfoot => @_ ) ); }
sub createTHead { return( shift->_create_tsection( thead => @_ ) ); }
sub deleteCaption { return( shift->_delete_first_element( caption => @_ ) ); }
sub deleteRow
{
my $self = shift( @_ );
my $pos = shift( @_ );
return( $self->error({
message => "Value provided (" . overload::StrVal( $pos // '' ) . ") is not an integer.",
class => 'HTML::Object::IndexSizeError',
}) ) if( !defined( $pos ) || !$self->_is_integer( $pos ) );
my $rows = $self->rows;
my $size = $rows->size;
return( $self->error({
message => "Value provided ($pos) is greater than the zero-based number of rows available (" . $rows->size . ").",
class => 'HTML::Object::IndexSizeError',
}) ) if( $pos > $size );
return( $self->error({
message => "Value provided ($pos) is lower than the zero-based number of rows available (" . $rows->size . "). If you want to specify a negative index, it must be between -1 and -${size}",
class => 'HTML::Object::IndexSizeError',
}) ) if( $pos < 0 && abs( $pos ) > $size );
$pos = ( $rows->length + $pos ) if( $pos < 0 );
my $elem = $rows->index( $pos );
my $parent = $elem->parent;
my $children = $parent->children;
my $kid_pos = $children->pos( $elem );
return( $self->error({
message => "Unable to find the row element for index $pos",
class => 'HTML::Object::HierarchyRequestError',
}) ) if( !defined( $kid_pos ) );
my $rv = $children->splice( $kid_pos, 1 );
$elem->parent( undef );
$parent->reset(1);
$self->_reset_table( 'rows' );
return( $elem );
}
sub deleteTFoot { return( shift->_delete_first_element( tfoot => @_ ) ); }
sub deleteTHead { return( shift->_delete_first_element( thead => @_ ) ); }
# Note: deprecated property frame
sub frame : lvalue { return( shift->_set_get_property( 'frame', @_ ) ); }
sub insertRow
{
my $self = shift( @_ );
my $pos = shift( @_ );
my $rows = $self->rows;
my $size = $rows->size;
if( defined( $pos ) )
{
return( $self->error({
message => "Value provided (" . overload::StrVal( $pos // '' ) . ") is not an integer.",
class => 'HTML::Object::IndexSizeError',
}) ) if( !$self->_is_integer( $pos ) );
return( $self->error({
message => "Value provided ($pos) is greater than the zero-based number of rows available (" . $rows->size . ").",
class => 'HTML::Object::IndexSizeError',
}) ) if( $pos > $size );
return( $self->error({
message => "Value provided ($pos) is lower than the zero-based number of rows available (" . $rows->size . "). If you want to specify a negative index, it must be between -1 and -${size}",
class => 'HTML::Object::IndexSizeError',
}) ) if( $pos < 0 && abs( $pos ) > $size );
$pos = ( $rows->length + $pos ) if( $pos < 0 );
}
# "If a table has multiple <tbody> elements, by default, the new row is inserted into the last <tbody>."
my $body = $self->tbodies->last;
# If there is no tbody and no rows yet, we create a tbody
if( !$rows->length && !$body )
{
$body = $self->createTBody();
}
$self->_load_class( 'HTML::Object::DOM::Element::TableRow' ) || return( $self->pass_error );
my $row;
# A position was provided
if( defined( $pos ) )
{
# ..., but there are no rows yet
if( $rows->is_empty )
{
# if we have a tbody, we add the new row there
if( $body )
{
$row = $body->insertRow();
}
# otherwise, we just add as the last child of the table.
# However, this should not happen, because if there are no rows and no tbody, oen is created and this condition is never reached
else
{
$self->children->push( $row );
$row->parent( $self );
$self->reset(1);
}
}
else
{
my $elem = $rows->index( $pos );
return( $self->error({
message => "No element could be found at row index $pos",
class => 'HTML::Object::HierarchyRequestError',
}) ) if( !defined( $elem ) );
my $parent = $elem->parent;
my $children = $parent->children;
return( $self->error({
message => "Element at row index $pos has no parent!",
class => 'HTML::Object::HierarchyRequestError',
}) ) if( !$parent );
my $real_pos = $children->pos( $elem );
return( $self->error({
message => "Unable to find the row element for index $pos",
class => 'HTML::Object::HierarchyRequestError',
}) ) if( !defined( $real_pos ) );
$row = HTML::Object::DOM::Element::TableRow->new( @_ ) ||
return( $self->pass_error( HTML::Object::DOM::Element::TableRow->error ) );
$row->close;
$row->parent( $parent );
$children->splice( $real_pos, 0, $row );
$parent->reset(1);
}
}
# If there is already a tbody, the new row will be added there
elsif( $body )
{
$row = $body->insertRow();
}
# otherwise, there are already other rows directly under <table> and the new row is just added at the end of the table, even if there is a <tfoot> element.
else
{
$row = HTML::Object::DOM::Element::TableRow->new( @_ ) ||
return( $self->pass_error( HTML::Object::DOM::Element::TableRow->error ) );
$row->close;
$self->children->push( $row );
$row->parent( $self );
$self->reset(1);
}
$self->_reset_table( 'rows' );
return( $row );
}
# Note: property rows read-only
sub rows
{
my $self = shift( @_ );
return( $self->{_table_rows} ) if( $self->{_table_rows} && !$self->_is_table_reset( qw( rows tbody tfoot thead ) ) );
my $results = $self->new_array;
my $children = $self->children;
$children->foreach(sub
{
my $tag = $_->tag;
if( $tag eq 'tr' )
{
$results->push( $_ );
}
elsif( $tag eq 'tbody' || $tag eq 'tfoot' || $tag eq 'thead' )
{
my $rows = $_->rows;
$results->push( $rows->list ) if( !$rows->is_empty );
}
return(1);
});
unless( $self->{_table_rows} )
{
$self->_load_class( 'HTML::Object::DOM::Collection' ) ||
return( $self->pass_error );
$self->{_table_rows} = HTML::Object::DOM::Collection->new ||
return( $self->pass_error( HTML::Object::DOM::Collection->error ) );
}
# Re-use the same object, so that we can update the object the user may have retrieved
# e.g.:
# my $rows = $table->rows
# then do some row changes, and
# say $rows->length; # shows updated number of rows. No need to redo $table->rows
$self->{_table_rows}->set( $results );
$self->_remove_table_reset( 'rows' );
return( $self->{_table_rows} );
}
# Note: deprecated property rules
sub rules : lvalue { return( shift->_set_get_property( 'rules', @_ ) ); }
# Note: deprecated property summary
sub summary : lvalue { return( shift->_set_get_property( 'summary', @_ ) ); }
# Note: property tBodies read-only
sub tBodies { return( shift->_get_tsection_collection( 'tbody' ) ); }
sub tbodies { return( shift->tBodies( @_ ) ); }
# Note: property tFoot
sub tFoot : lvalue { return( shift->_set_get_section( tfoot => 'HTML::Object::DOM::Element::TableSection', @_ ) ); }
sub tfoot : lvalue { return( shift->tFoot( @_ ) ); }
# Note: property tHead
sub tHead : lvalue { return( shift->_set_get_section( thead => 'HTML::Object::DOM::Element::TableSection', @_ ) ); }
sub thead : lvalue { return( shift->tHead( @_ ) ); }
# Note: deprecated property width is inherited
# Common for tbody, thead and tfoot
sub _create_tsection
{
my $self = shift( @_ );
my $tag = shift( @_ ) || return( $self->error( "No tag name was provided for this table section." ) );
my $opts = $self->_get_args_as_hash( @_ );
$self->_load_class( 'HTML::Object::DOM::Element::TableSection' ) ||
return( $self->pass_error );
$opts->{tag} = $tag;
my $children = $self->children;
my $elem = HTML::Object::DOM::Element::TableSection->new( %$opts ) ||
return( $self->pass_error( HTML::Object::DOM::Element::TableSection->error ) );
$elem->close;
$elem->parent( $self );
my $list = $children->grep(sub{ $self->_is_a( $_ => 'HTML::Object::DOM::Element::TableSection' ) && $_->tag eq $tag });
if( $tag eq 'tbody' )
{
my $last_elem = $list->last;
if( $last_elem )
{
$last_elem->after( $elem );
}
else
{
$children->push( $elem );
}
$self->reset(1);
$self->_reset_table( $tag );
}
elsif( $tag eq 'tfoot' )
{
if( $list->is_empty )
{
$children->push( $elem );
$self->reset(1);
$self->_reset_table( $tag );
}
else
{
return( $list->first );
}
}
elsif( $tag eq 'thead' )
{
if( $list->is_empty )
{
my $len = $children->length;
my $pos;
for( my $i = 0; $i < $len; $i++ )
{
my $e = $children->[$i];
next if( !$e->_is_a( 'HTML::Object::DOM::Node' ) );
my $tag = $e->tag;
if( $tag ne 'caption' && $tag ne 'colgroup' )
{
$pos = $i;
$children->splice( $i, 0, $elem );
$self->reset(1);
$self->_reset_table( $tag );
last;
}
}
if( !defined( $pos ) )
{
$children->push( $elem );
$self->reset(1);
$self->_reset_table( $tag );
}
}
else
{
return( $list->first );
}
}
return( $elem );
}
sub _delete_first_element
{
my $self = shift( @_ );
my $tag = shift( @_ ) || return( $self->error( "No tag was provided." ) );
$tag = lc( $tag );
my $children = $self->children;
my $len = $children->length;
for( my $i = 0; $i < $len; $i++ )
{
if( $self->_is_a( $children->[$i] => 'HTML::Object::Element' ) &&
$children->[$i]->tag eq $tag )
{
my $elem = $children->[$i];
$children->splice( $i, 1 );
$elem->parent( undef );
$self->reset(1);
$self->_reset_table( $tag );
return( $elem );
}
}
return;
}
sub _get_tsection_collection
{
my $self = shift( @_ );
my $tag = shift( @_ ) || return( $self->error( "No tag name was provided for this table section." ) );
my $cache_name = "_table_collection_${tag}";
return( $self->{ $cache_name } ) if( $self->{ $cache_name } && !$self->_is_table_reset( $tag ) );
my $results = $self->new_array;
my $children = $self->children;
$children->foreach(sub
{
# if( $_->tag eq $tag && !$self->_is_a( $_ => 'HTML::Object::DOM::Closing' ) )
if( $_->tag eq $tag )
{
$results->push( $_ );
}
});
$self->_load_class( 'HTML::Object::DOM::Collection' ) || return( $self->pass_error );
my $col = HTML::Object::DOM::Collection->new( $results ) ||
return( $self->pass_error( HTML::Object::DOM::Collection->error ) );
$self->{ $cache_name } = $col;
$self->_remove_table_reset( $tag );
return( $col );
}
sub _is_table_reset
{
my $self = shift( @_ );
my @types = @_;
my $type = shift( @_ );
if( scalar( @types ) )
{
foreach my $type ( @types )
{
if( defined( $type ) && CORE::length( $type ) )
{
return(1) if( CORE::length( $self->{ "_table_reset_${type}" } ) );
}
}
}
else
{
for( @{$self->{_reset_fields}} )
{
return(1) if( CORE::exists( $self->{ $_ } ) && CORE::length( $self->{ $_ } ) );
}
}
return(0);
}
sub _remove_table_reset
{
my $self = shift( @_ );
my @types = @_;
for( @types )
{
CORE::delete( $self->{ "_table_reset_${_}" } );
}
return( $self );
}
sub _reset_table
{
my $self = shift( @_ );
my $type = shift( @_ );
if( $type eq 'all' )
{
for( @{$self->{_reset_fields}} )
{
$self->{ $_ } = 1;
}
}
else
{
$self->{ "_table_reset_${type}" } = 1;
}
$self->rows if( $type eq 'rows' || $type eq 'all' );
$self->reset(1);
return( $self );
}
sub _set_get_section : lvalue
{
my $self = shift( @_ );
my $tag = shift( @_ );
my $class = shift( @_ );
return( $self->_set_get_callback({
get => sub
{
my $self = shift( @_ );
my $list = $self->_get_tsection_collection( $tag );
return( $list->first );
},
set => sub
{
my $self = shift( @_ );
my $new = shift( @_ );
if( !$self->_is_a( $new => $class ) || $new->tag ne $tag )
{
return( $self->error({ message => "New ${tag} object provided is not an ${class} object", class => 'HTML::Object::HierarchyRequestError' }) );
}
$new->detach;
my( $old, $pos );
my $children = $self->children;
my $len = $children->length;
for( my $i = 0; $i < $len; $i++ )
{
my $e = $children->[$i];
my $e_tag = $e->tag;
if( $e_tag eq $tag )
{
$old = $e;
$pos = $i;
last;
}
# Find out the default position we would insert the new element if there is no previous ($old) element
elsif( !defined( $pos ) &&
(
( $tag eq 'thead' && $e_tag ne 'caption' && $e_tag ne 'colgroup' ) ||
( $tag eq 'tfoot' && $e_tag ne 'caption' && $e_tag ne 'colgroup' && $e_tag ne 'thead' )
) )
{
$pos = $i;
}
}
if( !defined( $pos ) )
{
if( $tag eq 'caption' )
{
$pos = 0;
}
# For thead, or tfoot, put them as the last element
else
{
$pos = $len;
}
}
$self->reset(1);
$self->_reset_table( $tag );
$new->parent( $self );
if( defined( $old ) )
{
$children->splice( $pos, 1, $new );
return( $old );
}
else
{
$children->splice( $pos, 0, $new );
return;
}
},
}, @_ ) );
}
1;
# NOTE: POD
__END__
=encoding utf-8
=head1 NAME
HTML::Object::DOM::Element::Table - HTML Object DOM Table Class
=head1 SYNOPSIS
use HTML::Object::DOM::Element::Table;
my $table = HTML::Object::DOM::Element::Table->new ||
die( HTML::Object::DOM::Element::Table->error, "\n" );
=head1 VERSION
v0.2.1
=head1 DESCRIPTION
This interface provides special properties and methods (beyond the regular L<HTML::Object::DOM::Element> object interface it also has available to it by inheritance) for manipulating the layout and presentation of tables in an HTML document.
Tables can have, in this order:
=over 4
=item 1. an optional L<caption|HTML::Object::DOM::Element::TableCaption> element,
=item 2. zero or more L<colgroup|HTML::Object::DOM::Element::TableCol> elements,
=item 3. an optional L<thead|HTML::Object::DOM::Element::TableSection> element,
=item 4. either one of the following:
=over 8
=item * zero or more L<tbody|HTML::Object::DOM::Element::TableSection> elements
=item * one or more L<tr|HTML::Object::DOM::Element::TableRow> elements
=back
=item 5. an optional L<tfoot|HTML::Object::DOM::Element::TableSection> element
=back
The above is for reference only and is not enforced by this interface.
=head1 INHERITANCE
+-----------------------+ +---------------------------+ +-------------------------+ +----------------------------+ +-----------------------------------+
| HTML::Object::Element | --> | HTML::Object::EventTarget | --> | HTML::Object::DOM::Node | --> | HTML::Object::DOM::Element | --> | HTML::Object::DOM::Element::Table |
+-----------------------+ +---------------------------+ +-------------------------+ +----------------------------+ +-----------------------------------+
=head1 PROPERTIES
Inherits properties from its parent L<HTML::Object::DOM::Element>
=head2 caption
Is a L<table caption element|HTML::Object::DOM::Element::TableCaption> representing the first L<caption|HTML::Object::DOM::Element::TableCaption> that is a child of the element, or C<undef> if none is found.
When set, if the object does not represent a L<caption|HTML::Object::DOM::Element::TableCaption>, a C<HTML::Object::HierarchyRequestError> error is returned. If a correct object is given, it is inserted in the tree as the first child of this element and the first L<caption|HTML::Object::DOM::Element::TableCaption> that is a child of this element is removed from the tree, if any, and returned.
Example:
my $table = $doc->getElementsByTagName( 'table' )->first;
my $old_caption = $table->caption( $new_caption );
# or
$table->caption = $new_caption;
=head2 rows
Read-only.
Returns a L<HTML Collection|HTML::Object::DOM::Collection> containing all the rows of the element, that is all C<tr> that are a child of the element, or a child of one of its C<thead>, C<tbody> and C<tfoot> children. The rows members of a C<thead> appear first, in tree order, and those members of a C<tbody> last, also in tree order.
Note that for performance improvement, the collection is cached until changes are made that would affect the results.
Example:
<table id="myTable">
<tr></tr>
<tbody>
<tr></tr>
</tbody>
</table>
my $rows = $doc->getElementById( 'myTable' )->rows;
say $rows->length; # 2
<table id="myTable2">
<tr>
<td>
<table>
<tr></tr>
</table>
</td>
</tr>
<tbody>
<tr></tr>
</tbody>
</table>
my $rows = $doc->getElementById( 'myTable2' )->rows;
say $rows->length; # Still 2
=head2 tBodies
Read-only.
Returns a L<HTML Collection|HTML::Object::DOM::Collection> containing all the L<tbody|HTML::Object::DOM::Element::TableSection> of the element.
Note that for performance improvement, the collection is cached until changes are made that would affect the results.
=head2 tbodies
Alias for L</tBodies>
=head2 tFoot
Is a L<HTML::Object::DOM::Element::TableSection> representing the first L<tfoot|HTML::Object::DOM::Element::TableSection> that is a child of the element, or C<undef> if none is found.
When set, if the object does not represent a L<tfoot|HTML::Object::DOM::Element::TableSection>, a C<HTML::Object::HierarchyRequestError> error is returned.
If a correct object is given, it is inserted in the tree immediately before the first element that is neither a L<caption|HTML::Object::DOM::Element::TableCaption>, a L<colgroup|HTML::Object::DOM::Element::TableCol>, nor a L<thead|HTML::Object::DOM::Element::TableSection>, or as the last child if there is no such element, and the first L<tfoot|HTML::Object::DOM::Element::TableSection> that is a child of this element is removed from the tree, if any.
=head2 tHead
Is a L<HTML::Object::DOM::Element::TableSection> representing the first L<thead|HTML::Object::DOM::Element::TableSection> that is a child of the element, or C<undef> if none is found.
When set, if the object does not represent a L<thead|HTML::Object::DOM::Element::TableSection>, a C<HTML::Object::HierarchyRequestError> error is returned.
If a correct object is given, it is inserted in the tree immediately before the first element that is neither a L<caption|HTML::Object::DOM::Element::TableCaption>, nor a L<colgroup|HTML::Object::DOM::Element::TableCol>, or as the last child if there is no such element, and the first L<thead|HTML::Object::DOM::Element::TableSection> that is a child of this element is removed from the tree, if any.
=head1 METHODS
Inherits methods from its parent L<HTML::Object::DOM::Element>
=head2 createCaption
Returns an L<HTML::Object::DOM::Element> representing the first L<caption|HTML::Object::DOM::Element::TableCaption> that is a child of the element. If none is found, a new one is created and inserted in the tree as the first child of the C<table> element.
Example:
<table>
<tr><td>Cell 1.1</td><td>Cell 1.2</td><td>Cell 1.3</td></tr>
<tr><td>Cell 2.1</td><td>Cell 2.2</td><td>Cell 2.3</td></tr>
</table>
my $table = $doc->querySelector('table');
my $caption = $table->createCaption();
$caption->textContent = 'This caption was created by JavaScript!';
=head2 createTBody
Returns a L<HTML::Object::DOM::Element::TableSection> representing a new L<tbody|HTML::Object::DOM::Element::TableSection> that is a child of the element. It is inserted in the tree after the last element that is a L<tbody|HTML::Object::DOM::Element::TableSection>, or as the last child if there is no such element.
Example:
my $mybody = $mytable->createTBody();
# Now this should be true: $mybody == mytable->tBodies->item( $mytable->tBodies->length - 1 )
=head2 createTFoot
Returns an L<HTML::Object::DOM::Element::TableSection> representing the first L<tfoot|HTML::Object::DOM::Element::TableSection> that is a child of the element. If none is found, a new one is created and inserted in the tree as the last child.
Example:
my $myfoot = $mytable->createTFoot();
# Now this should be true: $myfoot == $mytable->tFoot
=head2 createTHead
Returns an L<HTML::Object::DOM::Element::TableSection> representing the first L<thead|HTML::Object::DOM::Element::TableSection> that is a child of the element. If none is found, a new one is created and inserted in the tree immediately before the first element that is neither a L<caption|HTML::Object::DOM::Element::TableCaption>, nor a L<colgroup|HTML::Object::DOM::Element::TableCol>, or as the last child if there is no such element.
Example:
my $myhead = mytable->createTHead();
# Now this should be true: $myhead == mytable->tHead
=head2 deleteCaption
Removes the first L<caption|HTML::Object::DOM::Element::TableCaption> that is a child of the element and returns the object of the caption element.
Example:
<table>
<caption>This caption will be deleted!</caption>
<tr><td>Cell 1.1</td><td>Cell 1.2</td></tr>
<tr><td>Cell 2.1</td><td>Cell 2.2</td></tr>
</table>
my $table = $doc->querySelector('table');
$table->deleteCaption();
=head2 deleteRow
Removes the row corresponding to the index given in parameter. If the index value is C<-1> the last row is removed; if it smaller than C<-1> or greater than the amount of rows in the collection, an C<HTML::Object::IndexSizeError> is returned.
Example:
<table>
<tr><td>Cell 1.1</td><td>Cell 1.2</td><td>Cell 1.3</td></tr>
<tr><td>Cell 2.1</td><td>Cell 2.2</td><td>Cell 2.3</td></tr>
<tr><td>Cell 3.1</td><td>Cell 3.2</td><td>Cell 3.3</td></tr>
</table>
my $table = $doc->querySelector('table');
# Delete second row
$table->deleteRow(1);
=head2 deleteTFoot
Removes the first L<tfoot|HTML::Object::DOM::Element::TableSection> that is a child of the element and returns the object of the caption element.
Example:
<table>
<thead><th>Name</th><th>Score</th></thead>
<tr><td>Bob</td><td>541</td></tr>
<tr><td>Jim</td><td>225</td></tr>
<tfoot><th>Average</th><td>383</td></tfoot>
</table>
my $table = $doc->querySelector('table');
$table->deleteTFoot();
=head2 deleteTHead
Removes the first L<thead|HTML::Object::DOM::Element::TableSection> that is a child of the element and returns the object of the caption element.
Example:
<table>
<thead><th>Name</th><th>Occupation</th></thead>
<tr><td>Bob</td><td>Plumber</td></tr>
<tr><td>Jim</td><td>Roofer</td></tr>
</table>
my $table = $doc->querySelector('table');
$table->deleteTHead();
=head2 insertRow
Returns an L<HTML::Object::DOM::Element::TableRow> representing a new row of the table. It inserts it in the rows collection immediately before the C<tr> element at the given index position, if any was provided. If there are no existing rows yet, a L<tbody|HTML::Object::DOM::Element::TableSection> is created and the new row inserted into it. If a table has multiple C<tbody> elements, by default, the new row is inserted into the last C<tbody>.
If the index is not given or is C<-1>, the new row is appended to the collection. If the index is smaller than C<-1>, it will start that far back from the end of the collection array. If index is greater than the number of rows in the collection, an C<HTML::Object::IndexSizeError> error is returned.
Example:
<table></table>
$doc->getElementsByTagName('table')->[0]->insertRow();
Table is now:
<table>
<tbody>
<tr></tr>
</tbody>
</table>
But if there are already existing rows and no tbody, the new row will merely be added as the las child of the table.
<table>
<tr></tr>
</table>
$doc->getElementsByTagName('table')->[0]->insertRow();
Table is now:
<table>
<tr></tr>
<tr></tr>
</table>
Even if there is a C<tfoot>, the new row will be added after:
<table>
<tr></tr>
<tfoot></tfoot>
</table>
$doc->getElementsByTagName('table')->[0]->insertRow();
Table is now:
<table>
<tr></tr>
<tfoot></tfoot>
<tr></tr>
</table>
If an index is negative, the new row will be added that far back from the end:
<table>
<tr id="one"></tr>
<tr id="two"></tr>
<tr id="three"></tr>
</table>
$doc->getElementsByTagName('table')->[0]->insertRow(-2);
Table is now:
<table>
<tr id="one"></tr>
<tr></tr>
<tr id="two"></tr>
<tr id="three"></tr>
</table>
=head2 DEPRECATED PROPERTIES
=head2 align
Is a string containing an enumerated value reflecting the align attribute. It indicates the alignment of the element's contents with respect to the surrounding context. The possible values are "left", "right", and "center".
=head2 bgColor
A string containing the background color of the table. It reflects the obsolete bgcolor attribute.
=head2 border
Is a string containing the width in pixels of the border of the table. It reflects the obsolete border attribute.
=head2 cellPadding
Is a string containing the width in pixels of the horizontal and vertical sapce between cell content and cell borders. It reflects the obsolete cellpadding attribute.
=head2 cellSpacing
Is a string containing the width in pixels of the horizontal and vertical separation between cells. It reflects the obsolete cellspacing attribute.
=head2 frame
Is a string containing the type of the external borders of the table. It reflects the obsolete frame attribute and can take one of the following values: C<void>, C<above>, C<below>, C<hsides>, C<vsides>, C<lhs>, C<rhs>, C<box>, or C<border>.
=head2 rules
Is a string containing the type of the internal borders of the table. It reflects the obsolete rules attribute and can take one of the following values: C<none>, C<groups>, C<rows>, C<cols>, or C<all>.
=head2 summary
Is a string containing a description of the purpose or the structure of the table. It reflects the obsolete summary attribute.
=head2 width
Is a string containing the length in pixels or in percentage of the desired width fo the entire table. It reflects the obsolete width attribute.
=head1 AUTHOR
Jacques Deguest E<lt>F<jack@deguest.jp>E<gt>
=head1 SEE ALSO
=head1 COPYRIGHT & LICENSE
Copyright(c) 2021 DEGUEST Pte. Ltd.
All rights reserved
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
=cut