.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35)
.\"
.\" Standard preamble:
.\" ========================================================================
.de Sp \" Vertical space (when we can't use .PP)
.if t .sp .5v
.if n .sp
..
.de Vb \" Begin verbatim text
.ft CW
.nf
.ne \\$1
..
.de Ve \" End verbatim text
.ft R
.fi
..
.\" Set up some character translations and predefined strings. \*(-- will
.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
.\" double quote, and \*(R" will give a right double quote. \*(C+ will
.\" give a nicer C++. Capital omega is used to do unbreakable dashes and
.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
.\" nothing in troff, for use with C<>.
.tr \(*W-
.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
.ie n \{\
. ds -- \(*W-
. ds PI pi
. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
. ds L" ""
. ds R" ""
. ds C` ""
. ds C' ""
'br\}
.el\{\
. ds -- \|\(em\|
. ds PI \(*p
. ds L" ``
. ds R" ''
. ds C`
. ds C'
'br\}
.\"
.\" Escape single quotes in literal strings from groff's Unicode transform.
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\"
.\" If the F register is >0, we'll generate index entries on stderr for
.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
.\" entries marked with X<> in POD. Of course, you'll have to process the
.\" output yourself in some meaningful fashion.
.\"
.\" Avoid warning from groff about undefined register 'F'.
.de IX
..
.nr rF 0
.if \n(.g .if rF .nr rF 1
.if (\n(rF:(\n(.g==0)) \{\
. if \nF \{\
. de IX
. tm Index:\\$1\t\\n%\t"\\$2"
..
. if !\nF==2 \{\
. nr % 0
. nr F 2
. \}
. \}
.\}
.rr rF
.\" ========================================================================
.\"
.IX Title "Mojo::DOM 3"
.TH Mojo::DOM 3 "2023-03-08" "perl v5.26.3" "User Contributed Perl Documentation"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
.nh
.SH "NAME"
Mojo::DOM \- Minimalistic HTML/XML DOM parser with CSS selectors
.SH "SYNOPSIS"
.IX Header "SYNOPSIS"
.Vb 1
\& use Mojo::DOM;
\&
\& # Parse
\& my $dom = Mojo::DOM\->new(\*(Aq<div><p id="a">Test</p><p id="b">123</p></div>\*(Aq);
\&
\& # Find
\& say $dom\->at(\*(Aq#b\*(Aq)\->text;
\& say $dom\->find(\*(Aqp\*(Aq)\->map(\*(Aqtext\*(Aq)\->join("\en");
\& say $dom\->find(\*(Aq[id]\*(Aq)\->map(attr => \*(Aqid\*(Aq)\->join("\en");
\&
\& # Iterate
\& $dom\->find(\*(Aqp[id]\*(Aq)\->reverse\->each(sub { say $_\->{id} });
\&
\& # Loop
\& for my $e ($dom\->find(\*(Aqp[id]\*(Aq)\->each) {
\& say $e\->{id}, \*(Aq:\*(Aq, $e\->text;
\& }
\&
\& # Modify
\& $dom\->find(\*(Aqdiv p\*(Aq)\->last\->append(\*(Aq<p id="c">456</p>\*(Aq);
\& $dom\->at(\*(Aq#c\*(Aq)\->prepend($dom\->new_tag(\*(Aqp\*(Aq, id => \*(Aqd\*(Aq, \*(Aq789\*(Aq));
\& $dom\->find(\*(Aq:not(p)\*(Aq)\->map(\*(Aqstrip\*(Aq);
\&
\& # Render
\& say "$dom";
.Ve
.SH "DESCRIPTION"
.IX Header "DESCRIPTION"
Mojo::DOM is a minimalistic and relaxed \s-1HTML/XML DOM\s0 parser with \s-1CSS\s0 selector support. It will even try to interpret
broken \s-1HTML\s0 and \s-1XML,\s0 so you should not use it for validation.
.SH "NODES AND ELEMENTS"
.IX Header "NODES AND ELEMENTS"
When we parse an \s-1HTML/XML\s0 fragment, it gets turned into a tree of nodes.
.PP
.Vb 5
\& <!DOCTYPE html>
\& <html>
\& <head><title>Hello</title></head>
\& <body>World!</body>
\& </html>
.Ve
.PP
There are currently eight different kinds of nodes, \f(CW\*(C`cdata\*(C'\fR, \f(CW\*(C`comment\*(C'\fR, \f(CW\*(C`doctype\*(C'\fR, \f(CW\*(C`pi\*(C'\fR, \f(CW\*(C`raw\*(C'\fR, \f(CW\*(C`root\*(C'\fR, \f(CW\*(C`tag\*(C'\fR
and \f(CW\*(C`text\*(C'\fR. Elements are nodes of the type \f(CW\*(C`tag\*(C'\fR.
.PP
.Vb 8
\& root
\& |\- doctype (html)
\& +\- tag (html)
\& |\- tag (head)
\& | +\- tag (title)
\& | +\- raw (Hello)
\& +\- tag (body)
\& +\- text (World!)
.Ve
.PP
While all node types are represented as Mojo::DOM objects, some methods like \*(L"attr\*(R" and \*(L"namespace\*(R" only
apply to elements.
.SH "HTML AND XML"
.IX Header "HTML AND XML"
Mojo::DOM defaults to \s-1HTML\s0 semantics, that means all tags and attribute names are lowercased and selectors need to
be lowercase as well.
.PP
.Vb 3
\& # HTML semantics
\& my $dom = Mojo::DOM\->new(\*(Aq<P ID="greeting">Hi!</P>\*(Aq);
\& say $dom\->at(\*(Aqp[id]\*(Aq)\->text;
.Ve
.PP
If an \s-1XML\s0 declaration is found, the parser will automatically switch into \s-1XML\s0 mode and everything becomes
case-sensitive.
.PP
.Vb 3
\& # XML semantics
\& my $dom = Mojo::DOM\->new(\*(Aq<?xml version="1.0"?><P ID="greeting">Hi!</P>\*(Aq);
\& say $dom\->at(\*(AqP[ID]\*(Aq)\->text;
.Ve
.PP
\&\s-1HTML\s0 or \s-1XML\s0 semantics can also be forced with the \*(L"xml\*(R" method.
.PP
.Vb 3
\& # Force HTML semantics
\& my $dom = Mojo::DOM\->new\->xml(0)\->parse(\*(Aq<P ID="greeting">Hi!</P>\*(Aq);
\& say $dom\->at(\*(Aqp[id]\*(Aq)\->text;
\&
\& # Force XML semantics
\& my $dom = Mojo::DOM\->new\->xml(1)\->parse(\*(Aq<P ID="greeting">Hi!</P>\*(Aq);
\& say $dom\->at(\*(AqP[ID]\*(Aq)\->text;
.Ve
.SH "METHODS"
.IX Header "METHODS"
Mojo::DOM implements the following methods.
.SS "all_text"
.IX Subsection "all_text"
.Vb 1
\& my $text = $dom\->all_text;
.Ve
.PP
Extract text content from all descendant nodes of this element. For \s-1HTML\s0 documents \f(CW\*(C`script\*(C'\fR and \f(CW\*(C`style\*(C'\fR elements are
excluded.
.PP
.Vb 2
\& # "foo\enbarbaz\en"
\& $dom\->parse("<div>foo\en<p>bar</p>baz\en</div>")\->at(\*(Aqdiv\*(Aq)\->all_text;
.Ve
.SS "ancestors"
.IX Subsection "ancestors"
.Vb 2
\& my $collection = $dom\->ancestors;
\& my $collection = $dom\->ancestors(\*(Aqdiv ~ p\*(Aq);
.Ve
.PP
Find all ancestor elements of this node matching the \s-1CSS\s0 selector and return a Mojo::Collection object containing
these elements as Mojo::DOM objects. All selectors from \*(L"\s-1SELECTORS\*(R"\s0 in Mojo::DOM::CSS are supported.
.PP
.Vb 2
\& # List tag names of ancestor elements
\& say $dom\->ancestors\->map(\*(Aqtag\*(Aq)\->join("\en");
.Ve
.SS "append"
.IX Subsection "append"
.Vb 2
\& $dom = $dom\->append(\*(Aq<p>I ♥ Mojolicious!</p>\*(Aq);
\& $dom = $dom\->append(Mojo::DOM\->new);
.Ve
.PP
Append \s-1HTML/XML\s0 fragment to this node (for all node types other than \f(CW\*(C`root\*(C'\fR).
.PP
.Vb 3
\& # "<div><h1>Test</h1><h2>123</h2></div>"
\& $dom\->parse(\*(Aq<div><h1>Test</h1></div>\*(Aq)
\& \->at(\*(Aqh1\*(Aq)\->append(\*(Aq<h2>123</h2>\*(Aq)\->root;
\&
\& # "<p>Test 123</p>"
\& $dom\->parse(\*(Aq<p>Test</p>\*(Aq)\->at(\*(Aqp\*(Aq)
\& \->child_nodes\->first\->append(\*(Aq 123\*(Aq)\->root;
.Ve
.SS "append_content"
.IX Subsection "append_content"
.Vb 2
\& $dom = $dom\->append_content(\*(Aq<p>I ♥ Mojolicious!</p>\*(Aq);
\& $dom = $dom\->append_content(Mojo::DOM\->new);
.Ve
.PP
Append \s-1HTML/XML\s0 fragment (for \f(CW\*(C`root\*(C'\fR and \f(CW\*(C`tag\*(C'\fR nodes) or raw content to this node's content.
.PP
.Vb 3
\& # "<div><h1>Test123</h1></div>"
\& $dom\->parse(\*(Aq<div><h1>Test</h1></div>\*(Aq)
\& \->at(\*(Aqh1\*(Aq)\->append_content(\*(Aq123\*(Aq)\->root;
\&
\& # "<!\-\- Test 123 \-\-><br>"
\& $dom\->parse(\*(Aq<!\-\- Test \-\-><br>\*(Aq)
\& \->child_nodes\->first\->append_content(\*(Aq123 \*(Aq)\->root;
\&
\& # "<p>Test<i>123</i></p>"
\& $dom\->parse(\*(Aq<p>Test</p>\*(Aq)\->at(\*(Aqp\*(Aq)\->append_content(\*(Aq<i>123</i>\*(Aq)\->root;
.Ve
.SS "at"
.IX Subsection "at"
.Vb 2
\& my $result = $dom\->at(\*(Aqdiv ~ p\*(Aq);
\& my $result = $dom\->at(\*(Aqsvg|line\*(Aq, svg => \*(Aqhttp://www.w3.org/2000/svg\*(Aq);
.Ve
.PP
Find first descendant element of this element matching the \s-1CSS\s0 selector and return it as a Mojo::DOM object, or
\&\f(CW\*(C`undef\*(C'\fR if none could be found. All selectors from \*(L"\s-1SELECTORS\*(R"\s0 in Mojo::DOM::CSS are supported.
.PP
.Vb 2
\& # Find first element with "svg" namespace definition
\& my $namespace = $dom\->at(\*(Aq[xmlns\e:svg]\*(Aq)\->{\*(Aqxmlns:svg\*(Aq};
.Ve
.PP
Trailing key/value pairs can be used to declare xml namespace aliases.
.PP
.Vb 3
\& # "<rect />"
\& $dom\->parse(\*(Aq<svg xmlns="http://www.w3.org/2000/svg"><rect /></svg>\*(Aq)
\& \->at(\*(Aqsvg|rect\*(Aq, svg => \*(Aqhttp://www.w3.org/2000/svg\*(Aq);
.Ve
.SS "attr"
.IX Subsection "attr"
.Vb 4
\& my $hash = $dom\->attr;
\& my $foo = $dom\->attr(\*(Aqfoo\*(Aq);
\& $dom = $dom\->attr({foo => \*(Aqbar\*(Aq});
\& $dom = $dom\->attr(foo => \*(Aqbar\*(Aq);
.Ve
.PP
This element's attributes.
.PP
.Vb 2
\& # Remove an attribute
\& delete $dom\->attr\->{id};
\&
\& # Attribute without value
\& $dom\->attr(selected => undef);
\&
\& # List id attributes
\& say $dom\->find(\*(Aq*\*(Aq)\->map(attr => \*(Aqid\*(Aq)\->compact\->join("\en");
.Ve
.SS "child_nodes"
.IX Subsection "child_nodes"
.Vb 1
\& my $collection = $dom\->child_nodes;
.Ve
.PP
Return a Mojo::Collection object containing all child nodes of this element as Mojo::DOM objects.
.PP
.Vb 2
\& # "<p><b>123</b></p>"
\& $dom\->parse(\*(Aq<p>Test<b>123</b></p>\*(Aq)\->at(\*(Aqp\*(Aq)\->child_nodes\->first\->remove;
\&
\& # "<!DOCTYPE html>"
\& $dom\->parse(\*(Aq<!DOCTYPE html><b>123</b>\*(Aq)\->child_nodes\->first;
\&
\& # " Test "
\& $dom\->parse(\*(Aq<b>123</b><!\-\- Test \-\->\*(Aq)\->child_nodes\->last\->content;
.Ve
.SS "children"
.IX Subsection "children"
.Vb 2
\& my $collection = $dom\->children;
\& my $collection = $dom\->children(\*(Aqdiv ~ p\*(Aq);
.Ve
.PP
Find all child elements of this element matching the \s-1CSS\s0 selector and return a Mojo::Collection object containing
these elements as Mojo::DOM objects. All selectors from \*(L"\s-1SELECTORS\*(R"\s0 in Mojo::DOM::CSS are supported.
.PP
.Vb 2
\& # Show tag name of random child element
\& say $dom\->children\->shuffle\->first\->tag;
.Ve
.SS "content"
.IX Subsection "content"
.Vb 3
\& my $str = $dom\->content;
\& $dom = $dom\->content(\*(Aq<p>I ♥ Mojolicious!</p>\*(Aq);
\& $dom = $dom\->content(Mojo::DOM\->new);
.Ve
.PP
Return this node's content or replace it with \s-1HTML/XML\s0 fragment (for \f(CW\*(C`root\*(C'\fR and \f(CW\*(C`tag\*(C'\fR nodes) or raw content.
.PP
.Vb 2
\& # "<b>Test</b>"
\& $dom\->parse(\*(Aq<div><b>Test</b></div>\*(Aq)\->at(\*(Aqdiv\*(Aq)\->content;
\&
\& # "<div><h1>123</h1></div>"
\& $dom\->parse(\*(Aq<div><h1>Test</h1></div>\*(Aq)\->at(\*(Aqh1\*(Aq)\->content(\*(Aq123\*(Aq)\->root;
\&
\& # "<p><i>123</i></p>"
\& $dom\->parse(\*(Aq<p>Test</p>\*(Aq)\->at(\*(Aqp\*(Aq)\->content(\*(Aq<i>123</i>\*(Aq)\->root;
\&
\& # "<div><h1></h1></div>"
\& $dom\->parse(\*(Aq<div><h1>Test</h1></div>\*(Aq)\->at(\*(Aqh1\*(Aq)\->content(\*(Aq\*(Aq)\->root;
\&
\& # " Test "
\& $dom\->parse(\*(Aq<!\-\- Test \-\-><br>\*(Aq)\->child_nodes\->first\->content;
\&
\& # "<div><!\-\- 123 \-\->456</div>"
\& $dom\->parse(\*(Aq<div><!\-\- Test \-\->456</div>\*(Aq)
\& \->at(\*(Aqdiv\*(Aq)\->child_nodes\->first\->content(\*(Aq 123 \*(Aq)\->root;
.Ve
.SS "descendant_nodes"
.IX Subsection "descendant_nodes"
.Vb 1
\& my $collection = $dom\->descendant_nodes;
.Ve
.PP
Return a Mojo::Collection object containing all descendant nodes of this element as Mojo::DOM objects.
.PP
.Vb 4
\& # "<p><b>123</b></p>"
\& $dom\->parse(\*(Aq<p><!\-\- Test \-\-><b>123<!\-\- 456 \-\-></b></p>\*(Aq)
\& \->descendant_nodes\->grep(sub { $_\->type eq \*(Aqcomment\*(Aq })
\& \->map(\*(Aqremove\*(Aq)\->first;
\&
\& # "<p><b>test</b>test</p>"
\& $dom\->parse(\*(Aq<p><b>123</b>456</p>\*(Aq)
\& \->at(\*(Aqp\*(Aq)\->descendant_nodes\->grep(sub { $_\->type eq \*(Aqtext\*(Aq })
\& \->map(content => \*(Aqtest\*(Aq)\->first\->root;
.Ve
.SS "find"
.IX Subsection "find"
.Vb 2
\& my $collection = $dom\->find(\*(Aqdiv ~ p\*(Aq);
\& my $collection = $dom\->find(\*(Aqsvg|line\*(Aq, svg => \*(Aqhttp://www.w3.org/2000/svg\*(Aq);
.Ve
.PP
Find all descendant elements of this element matching the \s-1CSS\s0 selector and return a Mojo::Collection object
containing these elements as Mojo::DOM objects. All selectors from \*(L"\s-1SELECTORS\*(R"\s0 in Mojo::DOM::CSS are supported.
.PP
.Vb 2
\& # Find a specific element and extract information
\& my $id = $dom\->find(\*(Aqdiv\*(Aq)\->[23]{id};
\&
\& # Extract information from multiple elements
\& my @headers = $dom\->find(\*(Aqh1, h2, h3\*(Aq)\->map(\*(Aqtext\*(Aq)\->each;
\&
\& # Count all the different tags
\& my $hash = $dom\->find(\*(Aq*\*(Aq)\->reduce(sub { $a\->{$b\->tag}++; $a }, {});
\&
\& # Find elements with a class that contains dots
\& my @divs = $dom\->find(\*(Aqdiv.foo\e.bar\*(Aq)\->each;
.Ve
.PP
Trailing key/value pairs can be used to declare xml namespace aliases.
.PP
.Vb 3
\& # "<rect />"
\& $dom\->parse(\*(Aq<svg xmlns="http://www.w3.org/2000/svg"><rect /></svg>\*(Aq)
\& \->find(\*(Aqsvg|rect\*(Aq, svg => \*(Aqhttp://www.w3.org/2000/svg\*(Aq)\->first;
.Ve
.SS "following"
.IX Subsection "following"
.Vb 2
\& my $collection = $dom\->following;
\& my $collection = $dom\->following(\*(Aqdiv ~ p\*(Aq);
.Ve
.PP
Find all sibling elements after this node matching the \s-1CSS\s0 selector and return a Mojo::Collection object containing
these elements as Mojo::DOM objects. All selectors from \*(L"\s-1SELECTORS\*(R"\s0 in Mojo::DOM::CSS are supported.
.PP
.Vb 2
\& # List tags of sibling elements after this node
\& say $dom\->following\->map(\*(Aqtag\*(Aq)\->join("\en");
.Ve
.SS "following_nodes"
.IX Subsection "following_nodes"
.Vb 1
\& my $collection = $dom\->following_nodes;
.Ve
.PP
Return a Mojo::Collection object containing all sibling nodes after this node as Mojo::DOM objects.
.PP
.Vb 2
\& # "C"
\& $dom\->parse(\*(Aq<p>A</p><!\-\- B \-\->C\*(Aq)\->at(\*(Aqp\*(Aq)\->following_nodes\->last\->content;
.Ve
.SS "matches"
.IX Subsection "matches"
.Vb 2
\& my $bool = $dom\->matches(\*(Aqdiv ~ p\*(Aq);
\& my $bool = $dom\->matches(\*(Aqsvg|line\*(Aq, svg => \*(Aqhttp://www.w3.org/2000/svg\*(Aq);
.Ve
.PP
Check if this element matches the \s-1CSS\s0 selector. All selectors from \*(L"\s-1SELECTORS\*(R"\s0 in Mojo::DOM::CSS are supported.
.PP
.Vb 3
\& # True
\& $dom\->parse(\*(Aq<p class="a">A</p>\*(Aq)\->at(\*(Aqp\*(Aq)\->matches(\*(Aq.a\*(Aq);
\& $dom\->parse(\*(Aq<p class="a">A</p>\*(Aq)\->at(\*(Aqp\*(Aq)\->matches(\*(Aqp[class]\*(Aq);
\&
\& # False
\& $dom\->parse(\*(Aq<p class="a">A</p>\*(Aq)\->at(\*(Aqp\*(Aq)\->matches(\*(Aq.b\*(Aq);
\& $dom\->parse(\*(Aq<p class="a">A</p>\*(Aq)\->at(\*(Aqp\*(Aq)\->matches(\*(Aqp[id]\*(Aq);
.Ve
.PP
Trailing key/value pairs can be used to declare xml namespace aliases.
.PP
.Vb 3
\& # True
\& $dom\->parse(\*(Aq<svg xmlns="http://www.w3.org/2000/svg"><rect /></svg>\*(Aq)
\& \->matches(\*(Aqsvg|rect\*(Aq, svg => \*(Aqhttp://www.w3.org/2000/svg\*(Aq);
.Ve
.SS "namespace"
.IX Subsection "namespace"
.Vb 1
\& my $namespace = $dom\->namespace;
.Ve
.PP
Find this element's namespace, or return \f(CW\*(C`undef\*(C'\fR if none could be found.
.PP
.Vb 2
\& # "http://www.w3.org/2000/svg"
\& Mojo::DOM\->new(\*(Aq<svg xmlns:svg="http://www.w3.org/2000/svg"><svg:circle>3.14</svg:circle></svg>\*(Aq)\->at(\*(Aqsvg\e:circle\*(Aq)\->namespace;
\&
\& # Find namespace for an element with namespace prefix
\& my $namespace = $dom\->at(\*(Aqsvg > svg\e:circle\*(Aq)\->namespace;
\&
\& # Find namespace for an element that may or may not have a namespace prefix
\& my $namespace = $dom\->at(\*(Aqsvg > circle\*(Aq)\->namespace;
.Ve
.SS "new"
.IX Subsection "new"
.Vb 2
\& my $dom = Mojo::DOM\->new;
\& my $dom = Mojo::DOM\->new(\*(Aq<foo bar="baz">I ♥ Mojolicious!</foo>\*(Aq);
.Ve
.PP
Construct a new scalar-based Mojo::DOM object and \*(L"parse\*(R" \s-1HTML/XML\s0 fragment if necessary.
.SS "new_tag"
.IX Subsection "new_tag"
.Vb 7
\& my $tag = Mojo::DOM\->new_tag(\*(Aqdiv\*(Aq);
\& my $tag = $dom\->new_tag(\*(Aqdiv\*(Aq);
\& my $tag = $dom\->new_tag(\*(Aqdiv\*(Aq, id => \*(Aqfoo\*(Aq, hidden => undef);
\& my $tag = $dom\->new_tag(\*(Aqdiv\*(Aq, \*(Aqsafe content\*(Aq);
\& my $tag = $dom\->new_tag(\*(Aqdiv\*(Aq, id => \*(Aqfoo\*(Aq, \*(Aqsafe content\*(Aq);
\& my $tag = $dom\->new_tag(\*(Aqdiv\*(Aq, data => {mojo => \*(Aqrocks\*(Aq}, \*(Aqsafe content\*(Aq);
\& my $tag = $dom\->new_tag(\*(Aqdiv\*(Aq, id => \*(Aqfoo\*(Aq, sub { \*(Aqunsafe content\*(Aq });
.Ve
.PP
Construct a new Mojo::DOM object for an \s-1HTML/XML\s0 tag with or without attributes and content. The \f(CW\*(C`data\*(C'\fR attribute
may contain a hash reference with key/value pairs to generate attributes from.
.PP
.Vb 2
\& # "<br>"
\& $dom\->new_tag(\*(Aqbr\*(Aq);
\&
\& # "<div></div>"
\& $dom\->new_tag(\*(Aqdiv\*(Aq);
\&
\& # "<div id="foo" hidden></div>"
\& $dom\->new_tag(\*(Aqdiv\*(Aq, id => \*(Aqfoo\*(Aq, hidden => undef);
\&
\& # "<div>test & 123</div>"
\& $dom\->new_tag(\*(Aqdiv\*(Aq, \*(Aqtest & 123\*(Aq);
\&
\& # "<div id="foo">test & 123</div>"
\& $dom\->new_tag(\*(Aqdiv\*(Aq, id => \*(Aqfoo\*(Aq, \*(Aqtest & 123\*(Aq);
\&
\& # "<div data\-foo="1" data\-bar="test">test & 123</div>""
\& $dom\->new_tag(\*(Aqdiv\*(Aq, data => {foo => 1, Bar => \*(Aqtest\*(Aq}, \*(Aqtest & 123\*(Aq);
\&
\& # "<div id="foo">test & 123</div>"
\& $dom\->new_tag(\*(Aqdiv\*(Aq, id => \*(Aqfoo\*(Aq, sub { \*(Aqtest & 123\*(Aq });
\&
\& # "<div>Hello<b>Mojo!</b></div>"
\& $dom\->parse(\*(Aq<div>Hello</div>\*(Aq)\->at(\*(Aqdiv\*(Aq)
\& \->append_content($dom\->new_tag(\*(Aqb\*(Aq, \*(AqMojo!\*(Aq))\->root;
.Ve
.SS "next"
.IX Subsection "next"
.Vb 1
\& my $sibling = $dom\->next;
.Ve
.PP
Return Mojo::DOM object for next sibling element, or \f(CW\*(C`undef\*(C'\fR if there are no more siblings.
.PP
.Vb 2
\& # "<h2>123</h2>"
\& $dom\->parse(\*(Aq<div><h1>Test</h1><h2>123</h2></div>\*(Aq)\->at(\*(Aqh1\*(Aq)\->next;
.Ve
.SS "next_node"
.IX Subsection "next_node"
.Vb 1
\& my $sibling = $dom\->next_node;
.Ve
.PP
Return Mojo::DOM object for next sibling node, or \f(CW\*(C`undef\*(C'\fR if there are no more siblings.
.PP
.Vb 3
\& # "456"
\& $dom\->parse(\*(Aq<p><b>123</b><!\-\- Test \-\->456</p>\*(Aq)
\& \->at(\*(Aqb\*(Aq)\->next_node\->next_node;
\&
\& # " Test "
\& $dom\->parse(\*(Aq<p><b>123</b><!\-\- Test \-\->456</p>\*(Aq)
\& \->at(\*(Aqb\*(Aq)\->next_node\->content;
.Ve
.SS "parent"
.IX Subsection "parent"
.Vb 1
\& my $parent = $dom\->parent;
.Ve
.PP
Return Mojo::DOM object for parent of this node, or \f(CW\*(C`undef\*(C'\fR if this node has no parent.
.PP
.Vb 2
\& # "<b><i>Test</i></b>"
\& $dom\->parse(\*(Aq<p><b><i>Test</i></b></p>\*(Aq)\->at(\*(Aqi\*(Aq)\->parent;
.Ve
.SS "parse"
.IX Subsection "parse"
.Vb 1
\& $dom = $dom\->parse(\*(Aq<foo bar="baz">I ♥ Mojolicious!</foo>\*(Aq);
.Ve
.PP
Parse \s-1HTML/XML\s0 fragment with Mojo::DOM::HTML.
.PP
.Vb 2
\& # Parse XML
\& my $dom = Mojo::DOM\->new\->xml(1)\->parse(\*(Aq<foo>I ♥ Mojolicious!</foo>\*(Aq);
.Ve
.SS "preceding"
.IX Subsection "preceding"
.Vb 2
\& my $collection = $dom\->preceding;
\& my $collection = $dom\->preceding(\*(Aqdiv ~ p\*(Aq);
.Ve
.PP
Find all sibling elements before this node matching the \s-1CSS\s0 selector and return a Mojo::Collection object containing
these elements as Mojo::DOM objects. All selectors from \*(L"\s-1SELECTORS\*(R"\s0 in Mojo::DOM::CSS are supported.
.PP
.Vb 2
\& # List tags of sibling elements before this node
\& say $dom\->preceding\->map(\*(Aqtag\*(Aq)\->join("\en");
.Ve
.SS "preceding_nodes"
.IX Subsection "preceding_nodes"
.Vb 1
\& my $collection = $dom\->preceding_nodes;
.Ve
.PP
Return a Mojo::Collection object containing all sibling nodes before this node as Mojo::DOM objects.
.PP
.Vb 2
\& # "A"
\& $dom\->parse(\*(AqA<!\-\- B \-\-><p>C</p>\*(Aq)\->at(\*(Aqp\*(Aq)\->preceding_nodes\->first\->content;
.Ve
.SS "prepend"
.IX Subsection "prepend"
.Vb 2
\& $dom = $dom\->prepend(\*(Aq<p>I ♥ Mojolicious!</p>\*(Aq);
\& $dom = $dom\->prepend(Mojo::DOM\->new);
.Ve
.PP
Prepend \s-1HTML/XML\s0 fragment to this node (for all node types other than \f(CW\*(C`root\*(C'\fR).
.PP
.Vb 3
\& # "<div><h1>Test</h1><h2>123</h2></div>"
\& $dom\->parse(\*(Aq<div><h2>123</h2></div>\*(Aq)
\& \->at(\*(Aqh2\*(Aq)\->prepend(\*(Aq<h1>Test</h1>\*(Aq)\->root;
\&
\& # "<p>Test 123</p>"
\& $dom\->parse(\*(Aq<p>123</p>\*(Aq)
\& \->at(\*(Aqp\*(Aq)\->child_nodes\->first\->prepend(\*(AqTest \*(Aq)\->root;
.Ve
.SS "prepend_content"
.IX Subsection "prepend_content"
.Vb 2
\& $dom = $dom\->prepend_content(\*(Aq<p>I ♥ Mojolicious!</p>\*(Aq);
\& $dom = $dom\->prepend_content(Mojo::DOM\->new);
.Ve
.PP
Prepend \s-1HTML/XML\s0 fragment (for \f(CW\*(C`root\*(C'\fR and \f(CW\*(C`tag\*(C'\fR nodes) or raw content to this node's content.
.PP
.Vb 3
\& # "<div><h2>Test123</h2></div>"
\& $dom\->parse(\*(Aq<div><h2>123</h2></div>\*(Aq)
\& \->at(\*(Aqh2\*(Aq)\->prepend_content(\*(AqTest\*(Aq)\->root;
\&
\& # "<!\-\- Test 123 \-\-><br>"
\& $dom\->parse(\*(Aq<!\-\- 123 \-\-><br>\*(Aq)
\& \->child_nodes\->first\->prepend_content(\*(Aq Test\*(Aq)\->root;
\&
\& # "<p><i>123</i>Test</p>"
\& $dom\->parse(\*(Aq<p>Test</p>\*(Aq)\->at(\*(Aqp\*(Aq)\->prepend_content(\*(Aq<i>123</i>\*(Aq)\->root;
.Ve
.SS "previous"
.IX Subsection "previous"
.Vb 1
\& my $sibling = $dom\->previous;
.Ve
.PP
Return Mojo::DOM object for previous sibling element, or \f(CW\*(C`undef\*(C'\fR if there are no more siblings.
.PP
.Vb 2
\& # "<h1>Test</h1>"
\& $dom\->parse(\*(Aq<div><h1>Test</h1><h2>123</h2></div>\*(Aq)\->at(\*(Aqh2\*(Aq)\->previous;
.Ve
.SS "previous_node"
.IX Subsection "previous_node"
.Vb 1
\& my $sibling = $dom\->previous_node;
.Ve
.PP
Return Mojo::DOM object for previous sibling node, or \f(CW\*(C`undef\*(C'\fR if there are no more siblings.
.PP
.Vb 3
\& # "123"
\& $dom\->parse(\*(Aq<p>123<!\-\- Test \-\-><b>456</b></p>\*(Aq)
\& \->at(\*(Aqb\*(Aq)\->previous_node\->previous_node;
\&
\& # " Test "
\& $dom\->parse(\*(Aq<p>123<!\-\- Test \-\-><b>456</b></p>\*(Aq)
\& \->at(\*(Aqb\*(Aq)\->previous_node\->content;
.Ve
.SS "remove"
.IX Subsection "remove"
.Vb 1
\& my $parent = $dom\->remove;
.Ve
.PP
Remove this node and return \*(L"root\*(R" (for \f(CW\*(C`root\*(C'\fR nodes) or \*(L"parent\*(R".
.PP
.Vb 2
\& # "<div></div>"
\& $dom\->parse(\*(Aq<div><h1>Test</h1></div>\*(Aq)\->at(\*(Aqh1\*(Aq)\->remove;
\&
\& # "<p><b>456</b></p>"
\& $dom\->parse(\*(Aq<p>123<b>456</b></p>\*(Aq)
\& \->at(\*(Aqp\*(Aq)\->child_nodes\->first\->remove\->root;
.Ve
.SS "replace"
.IX Subsection "replace"
.Vb 2
\& my $parent = $dom\->replace(\*(Aq<div>I ♥ Mojolicious!</div>\*(Aq);
\& my $parent = $dom\->replace(Mojo::DOM\->new);
.Ve
.PP
Replace this node with \s-1HTML/XML\s0 fragment and return \*(L"root\*(R" (for \f(CW\*(C`root\*(C'\fR nodes) or \*(L"parent\*(R".
.PP
.Vb 2
\& # "<div><h2>123</h2></div>"
\& $dom\->parse(\*(Aq<div><h1>Test</h1></div>\*(Aq)\->at(\*(Aqh1\*(Aq)\->replace(\*(Aq<h2>123</h2>\*(Aq);
\&
\& # "<p><b>123</b></p>"
\& $dom\->parse(\*(Aq<p>Test</p>\*(Aq)
\& \->at(\*(Aqp\*(Aq)\->child_nodes\->[0]\->replace(\*(Aq<b>123</b>\*(Aq)\->root;
.Ve
.SS "root"
.IX Subsection "root"
.Vb 1
\& my $root = $dom\->root;
.Ve
.PP
Return Mojo::DOM object for \f(CW\*(C`root\*(C'\fR node.
.SS "selector"
.IX Subsection "selector"
.Vb 1
\& my $selector = $dom\->selector;
.Ve
.PP
Get a unique \s-1CSS\s0 selector for this element.
.PP
.Vb 2
\& # "ul:nth\-child(1) > li:nth\-child(2)"
\& $dom\->parse(\*(Aq<ul><li>Test</li><li>123</li></ul>\*(Aq)\->find(\*(Aqli\*(Aq)\->last\->selector;
\&
\& # "p:nth\-child(1) > b:nth\-child(1) > i:nth\-child(1)"
\& $dom\->parse(\*(Aq<p><b><i>Test</i></b></p>\*(Aq)\->at(\*(Aqi\*(Aq)\->selector;
.Ve
.SS "strip"
.IX Subsection "strip"
.Vb 1
\& my $parent = $dom\->strip;
.Ve
.PP
Remove this element while preserving its content and return \*(L"parent\*(R".
.PP
.Vb 2
\& # "<div>Test</div>"
\& $dom\->parse(\*(Aq<div><h1>Test</h1></div>\*(Aq)\->at(\*(Aqh1\*(Aq)\->strip;
.Ve
.SS "tag"
.IX Subsection "tag"
.Vb 2
\& my $tag = $dom\->tag;
\& $dom = $dom\->tag(\*(Aqdiv\*(Aq);
.Ve
.PP
This element's tag name.
.PP
.Vb 2
\& # List tag names of child elements
\& say $dom\->children\->map(\*(Aqtag\*(Aq)\->join("\en");
.Ve
.SS "tap"
.IX Subsection "tap"
.Vb 1
\& $dom = $dom\->tap(sub {...});
.Ve
.PP
Alias for \*(L"tap\*(R" in Mojo::Base.
.SS "text"
.IX Subsection "text"
.Vb 1
\& my $text = $dom\->text;
.Ve
.PP
Extract text content from this element only (not including child elements).
.PP
.Vb 2
\& # "bar"
\& $dom\->parse("<div>foo<p>bar</p>baz</div>")\->at(\*(Aqp\*(Aq)\->text;
\&
\& # "foo\enbaz\en"
\& $dom\->parse("<div>foo\en<p>bar</p>baz\en</div>")\->at(\*(Aqdiv\*(Aq)\->text;
.Ve
.PP
To extract text content from all descendant nodes see \*(L"all_text\*(R".
.SS "to_string"
.IX Subsection "to_string"
.Vb 1
\& my $str = $dom\->to_string;
.Ve
.PP
Render this node and its content to \s-1HTML/XML.\s0
.PP
.Vb 2
\& # "<b>Test</b>"
\& $dom\->parse(\*(Aq<div><b>Test</b></div>\*(Aq)\->at(\*(Aqdiv b\*(Aq)\->to_string;
.Ve
.SS "tree"
.IX Subsection "tree"
.Vb 2
\& my $tree = $dom\->tree;
\& $dom = $dom\->tree([\*(Aqroot\*(Aq]);
.Ve
.PP
Document Object Model. Note that this structure should only be used very carefully since it is very dynamic.
.SS "type"
.IX Subsection "type"
.Vb 1
\& my $type = $dom\->type;
.Ve
.PP
This node's type, usually \f(CW\*(C`cdata\*(C'\fR, \f(CW\*(C`comment\*(C'\fR, \f(CW\*(C`doctype\*(C'\fR, \f(CW\*(C`pi\*(C'\fR, \f(CW\*(C`raw\*(C'\fR, \f(CW\*(C`root\*(C'\fR, \f(CW\*(C`tag\*(C'\fR or \f(CW\*(C`text\*(C'\fR.
.PP
.Vb 2
\& # "cdata"
\& $dom\->parse(\*(Aq<![CDATA[Test]]>\*(Aq)\->child_nodes\->first\->type;
\&
\& # "comment"
\& $dom\->parse(\*(Aq<!\-\- Test \-\->\*(Aq)\->child_nodes\->first\->type;
\&
\& # "doctype"
\& $dom\->parse(\*(Aq<!DOCTYPE html>\*(Aq)\->child_nodes\->first\->type;
\&
\& # "pi"
\& $dom\->parse(\*(Aq<?xml version="1.0"?>\*(Aq)\->child_nodes\->first\->type;
\&
\& # "raw"
\& $dom\->parse(\*(Aq<title>Test</title>\*(Aq)\->at(\*(Aqtitle\*(Aq)\->child_nodes\->first\->type;
\&
\& # "root"
\& $dom\->parse(\*(Aq<p>Test</p>\*(Aq)\->type;
\&
\& # "tag"
\& $dom\->parse(\*(Aq<p>Test</p>\*(Aq)\->at(\*(Aqp\*(Aq)\->type;
\&
\& # "text"
\& $dom\->parse(\*(Aq<p>Test</p>\*(Aq)\->at(\*(Aqp\*(Aq)\->child_nodes\->first\->type;
.Ve
.SS "val"
.IX Subsection "val"
.Vb 1
\& my $value = $dom\->val;
.Ve
.PP
Extract value from form element (such as \f(CW\*(C`button\*(C'\fR, \f(CW\*(C`input\*(C'\fR, \f(CW\*(C`option\*(C'\fR, \f(CW\*(C`select\*(C'\fR and \f(CW\*(C`textarea\*(C'\fR), or return \f(CW\*(C`undef\*(C'\fR
if this element has no value. In the case of \f(CW\*(C`select\*(C'\fR with \f(CW\*(C`multiple\*(C'\fR attribute, find \f(CW\*(C`option\*(C'\fR elements with
\&\f(CW\*(C`selected\*(C'\fR attribute and return an array reference with all values, or \f(CW\*(C`undef\*(C'\fR if none could be found.
.PP
.Vb 2
\& # "a"
\& $dom\->parse(\*(Aq<input name=test value=a>\*(Aq)\->at(\*(Aqinput\*(Aq)\->val;
\&
\& # "b"
\& $dom\->parse(\*(Aq<textarea>b</textarea>\*(Aq)\->at(\*(Aqtextarea\*(Aq)\->val;
\&
\& # "c"
\& $dom\->parse(\*(Aq<option value="c">Test</option>\*(Aq)\->at(\*(Aqoption\*(Aq)\->val;
\&
\& # "d"
\& $dom\->parse(\*(Aq<select><option selected>d</option></select>\*(Aq)
\& \->at(\*(Aqselect\*(Aq)\->val;
\&
\& # "e"
\& $dom\->parse(\*(Aq<select multiple><option selected>e</option></select>\*(Aq)
\& \->at(\*(Aqselect\*(Aq)\->val\->[0];
\&
\& # "on"
\& $dom\->parse(\*(Aq<input name=test type=checkbox>\*(Aq)\->at(\*(Aqinput\*(Aq)\->val;
.Ve
.SS "with_roles"
.IX Subsection "with_roles"
.Vb 3
\& my $new_class = Mojo::DOM\->with_roles(\*(AqMojo::DOM::Role::One\*(Aq);
\& my $new_class = Mojo::DOM\->with_roles(\*(Aq+One\*(Aq, \*(Aq+Two\*(Aq);
\& $dom = $dom\->with_roles(\*(Aq+One\*(Aq, \*(Aq+Two\*(Aq);
.Ve
.PP
Alias for \*(L"with_roles\*(R" in Mojo::Base.
.SS "wrap"
.IX Subsection "wrap"
.Vb 2
\& $dom = $dom\->wrap(\*(Aq<div></div>\*(Aq);
\& $dom = $dom\->wrap(Mojo::DOM\->new);
.Ve
.PP
Wrap \s-1HTML/XML\s0 fragment around this node (for all node types other than \f(CW\*(C`root\*(C'\fR), placing it as the last child of the
first innermost element.
.PP
.Vb 2
\& # "<p>123<b>Test</b></p>"
\& $dom\->parse(\*(Aq<b>Test</b>\*(Aq)\->at(\*(Aqb\*(Aq)\->wrap(\*(Aq<p>123</p>\*(Aq)\->root;
\&
\& # "<div><p><b>Test</b></p>123</div>"
\& $dom\->parse(\*(Aq<b>Test</b>\*(Aq)\->at(\*(Aqb\*(Aq)\->wrap(\*(Aq<div><p></p>123</div>\*(Aq)\->root;
\&
\& # "<p><b>Test</b></p><p>123</p>"
\& $dom\->parse(\*(Aq<b>Test</b>\*(Aq)\->at(\*(Aqb\*(Aq)\->wrap(\*(Aq<p></p><p>123</p>\*(Aq)\->root;
\&
\& # "<p><b>Test</b></p>"
\& $dom\->parse(\*(Aq<p>Test</p>\*(Aq)\->at(\*(Aqp\*(Aq)\->child_nodes\->first\->wrap(\*(Aq<b>\*(Aq)\->root;
.Ve
.SS "wrap_content"
.IX Subsection "wrap_content"
.Vb 2
\& $dom = $dom\->wrap_content(\*(Aq<div></div>\*(Aq);
\& $dom = $dom\->wrap_content(Mojo::DOM\->new);
.Ve
.PP
Wrap \s-1HTML/XML\s0 fragment around this node's content (for \f(CW\*(C`root\*(C'\fR and \f(CW\*(C`tag\*(C'\fR nodes), placing it as the last children of
the first innermost element.
.PP
.Vb 2
\& # "<p><b>123Test</b></p>"
\& $dom\->parse(\*(Aq<p>Test<p>\*(Aq)\->at(\*(Aqp\*(Aq)\->wrap_content(\*(Aq<b>123</b>\*(Aq)\->root;
\&
\& # "<p><b>Test</b></p><p>123</p>"
\& $dom\->parse(\*(Aq<b>Test</b>\*(Aq)\->wrap_content(\*(Aq<p></p><p>123</p>\*(Aq);
.Ve
.SS "xml"
.IX Subsection "xml"
.Vb 2
\& my $bool = $dom\->xml;
\& $dom = $dom\->xml($bool);
.Ve
.PP
Disable \s-1HTML\s0 semantics in parser and activate case-sensitivity, defaults to auto-detection based on \s-1XML\s0 declarations.
.SH "OPERATORS"
.IX Header "OPERATORS"
Mojo::DOM overloads the following operators.
.SS "array"
.IX Subsection "array"
.Vb 1
\& my @nodes = @$dom;
.Ve
.PP
Alias for \*(L"child_nodes\*(R".
.PP
.Vb 2
\& # "<!\-\- Test \-\->"
\& $dom\->parse(\*(Aq<!\-\- Test \-\-><b>123</b>\*(Aq)\->[0];
.Ve
.SS "bool"
.IX Subsection "bool"
.Vb 1
\& my $bool = !!$dom;
.Ve
.PP
Always true.
.SS "hash"
.IX Subsection "hash"
.Vb 1
\& my %attrs = %$dom;
.Ve
.PP
Alias for \*(L"attr\*(R".
.PP
.Vb 2
\& # "test"
\& $dom\->parse(\*(Aq<div id="test">Test</div>\*(Aq)\->at(\*(Aqdiv\*(Aq)\->{id};
.Ve
.SS "stringify"
.IX Subsection "stringify"
.Vb 1
\& my $str = "$dom";
.Ve
.PP
Alias for \*(L"to_string\*(R".
.SH "SEE ALSO"
.IX Header "SEE ALSO"
Mojolicious, Mojolicious::Guides, <https://mojolicious.org>.