my $dom = DOM::Tiny->new('<P ID="greeting">Hi!</P>');
say $dom->at('p[id]')->text;
-If XML processing instructions are found, the parser will automatically switch
-into XML mode and everything becomes case-sensitive.
+If an XML declaration is found, the parser will automatically switch into XML
+mode and everything becomes case-sensitive.
# XML semantics
my $dom = DOM::Tiny->new('<?xml version="1.0"?><P ID="greeting">Hi!</P>');
my $dom = DOM::Tiny->new->xml(0)->parse('<P ID="greeting">Hi!</P>');
say $dom->at('p[id]')->text;
+=head1 SELECTORS
+
+L<DOM::Tiny> uses a CSS selector engine based on L<Mojo::DOM::CSS>. All CSS
+selectors that make sense for a standalone parser are supported.
+
+=over
+
+=item Z<>*
+
+Any element.
+
+ my $all = $dom->find('*');
+
+=item E
+
+An element of type C<E>.
+
+ my $title = $dom->at('title');
+
+=item E[foo]
+
+An C<E> element with a C<foo> attribute.
+
+ my $links = $dom->find('a[href]');
+
+=item E[foo="bar"]
+
+An C<E> element whose C<foo> attribute value is exactly equal to C<bar>.
+
+ my $case_sensitive = $dom->find('input[type="hidden"]');
+ my $case_sensitive = $dom->find('input[type=hidden]');
+
+=item E[foo="bar" i]
+
+An C<E> element whose C<foo> attribute value is exactly equal to any
+(ASCII-range) case-permutation of C<bar>. Note that this selector is
+EXPERIMENTAL and might change without warning!
+
+ my $case_insensitive = $dom->find('input[type="hidden" i]');
+ my $case_insensitive = $dom->find('input[type=hidden i]');
+ my $case_insensitive = $dom->find('input[class~="foo" i]');
+
+This selector is part of
+L<Selectors Level 4|http://dev.w3.org/csswg/selectors-4>, which is still a work
+in progress.
+
+=item E[foo~="bar"]
+
+An C<E> element whose C<foo> attribute value is a list of whitespace-separated
+values, one of which is exactly equal to C<bar>.
+
+ my $foo = $dom->find('input[class~="foo"]');
+ my $foo = $dom->find('input[class~=foo]');
+
+=item E[foo^="bar"]
+
+An C<E> element whose C<foo> attribute value begins exactly with the string
+C<bar>.
+
+ my $begins_with = $dom->find('input[name^="f"]');
+ my $begins_with = $dom->find('input[name^=f]');
+
+=item E[foo$="bar"]
+
+An C<E> element whose C<foo> attribute value ends exactly with the string
+C<bar>.
+
+ my $ends_with = $dom->find('input[name$="o"]');
+ my $ends_with = $dom->find('input[name$=o]');
+
+=item E[foo*="bar"]
+
+An C<E> element whose C<foo> attribute value contains the substring C<bar>.
+
+ my $contains = $dom->find('input[name*="fo"]');
+ my $contains = $dom->find('input[name*=fo]');
+
+=item E:root
+
+An C<E> element, root of the document.
+
+ my $root = $dom->at(':root');
+
+=item E:nth-child(n)
+
+An C<E> element, the C<n-th> child of its parent.
+
+ my $third = $dom->find('div:nth-child(3)');
+ my $odd = $dom->find('div:nth-child(odd)');
+ my $even = $dom->find('div:nth-child(even)');
+ my $top3 = $dom->find('div:nth-child(-n+3)');
+
+=item E:nth-last-child(n)
+
+An C<E> element, the C<n-th> child of its parent, counting from the last one.
+
+ my $third = $dom->find('div:nth-last-child(3)');
+ my $odd = $dom->find('div:nth-last-child(odd)');
+ my $even = $dom->find('div:nth-last-child(even)');
+ my $bottom3 = $dom->find('div:nth-last-child(-n+3)');
+
+=item E:nth-of-type(n)
+
+An C<E> element, the C<n-th> sibling of its type.
+
+ my $third = $dom->find('div:nth-of-type(3)');
+ my $odd = $dom->find('div:nth-of-type(odd)');
+ my $even = $dom->find('div:nth-of-type(even)');
+ my $top3 = $dom->find('div:nth-of-type(-n+3)');
+
+=item E:nth-last-of-type(n)
+
+An C<E> element, the C<n-th> sibling of its type, counting from the last one.
+
+ my $third = $dom->find('div:nth-last-of-type(3)');
+ my $odd = $dom->find('div:nth-last-of-type(odd)');
+ my $even = $dom->find('div:nth-last-of-type(even)');
+ my $bottom3 = $dom->find('div:nth-last-of-type(-n+3)');
+
+=item E:first-child
+
+An C<E> element, first child of its parent.
+
+ my $first = $dom->find('div p:first-child');
+
+=item E:last-child
+
+An C<E> element, last child of its parent.
+
+ my $last = $dom->find('div p:last-child');
+
+=item E:first-of-type
+
+An C<E> element, first sibling of its type.
+
+ my $first = $dom->find('div p:first-of-type');
+
+=item E:last-of-type
+
+An C<E> element, last sibling of its type.
+
+ my $last = $dom->find('div p:last-of-type');
+
+=item E:only-child
+
+An C<E> element, only child of its parent.
+
+ my $lonely = $dom->find('div p:only-child');
+
+=item E:only-of-type
+
+An C<E> element, only sibling of its type.
+
+ my $lonely = $dom->find('div p:only-of-type');
+
+=item E:empty
+
+An C<E> element that has no children (including text nodes).
+
+ my $empty = $dom->find(':empty');
+
+=item E:checked
+
+A user interface element C<E> which is checked (for instance a radio-button or
+checkbox).
+
+ my $input = $dom->find(':checked');
+
+=item E.warning
+
+An C<E> element whose class is "warning".
+
+ my $warning = $dom->find('div.warning');
+
+=item E#myid
+
+An C<E> element with C<ID> equal to "myid".
+
+ my $foo = $dom->at('div#foo');
+
+=item E:not(s)
+
+An C<E> element that does not match simple selector C<s>.
+
+ my $others = $dom->find('div p:not(:first-child)');
+
+=item E F
+
+An C<F> element descendant of an C<E> element.
+
+ my $headlines = $dom->find('div h1');
+
+=item E E<gt> F
+
+An C<F> element child of an C<E> element.
+
+ my $headlines = $dom->find('html > body > div > h1');
+
+=item E + F
+
+An C<F> element immediately preceded by an C<E> element.
+
+ my $second = $dom->find('h1 + h2');
+
+=item E ~ F
+
+An C<F> element preceded by an C<E> element.
+
+ my $second = $dom->find('h1 ~ h2');
+
+=item E, F, G
+
+Elements of type C<E>, C<F> and C<G>.
+
+ my $headlines = $dom->find('h1, h2, h3');
+
+=item E[foo=bar][bar=baz]
+
+An C<E> element whose attributes match all following attribute selectors.
+
+ my $links = $dom->find('a[foo^=b][foo$=ar]');
+
+=back
+
=head1 OPERATORS
L<DOM::Tiny> overloads the following operators.
$dom = $dom->append('<p>I ♥ DOM::Tiny!</p>');
-Append HTML/XML fragment to this node.
+Append HTML/XML fragment to this node (for all node types other than C<root>).
# "<div><h1>Test</h1><h2>123</h2></div>"
$dom->parse('<div><h1>Test</h1></div>')
my $result = $dom->at('div ~ p');
Find first descendant element of this element matching the CSS selector and
-return it as a L<DOM::Tiny> object or return C<undef> if none could be found.
-All selectors listed in L</"SELECTORS"> are supported.
+return it as a L<DOM::Tiny> object, or C<undef> if none could be found. All
+selectors listed in L</"SELECTORS"> are supported.
# Find first element with "svg" namespace definition
my $namespace = $dom->at('[xmlns\:svg]')->{'xmlns:svg'};
my $namespace = $dom->namespace;
-Find this element's namespace or return C<undef> if none could be found.
+Find this element's namespace, or return C<undef> if none could be found.
# Find namespace for an element with namespace prefix
my $namespace = $dom->at('svg > svg\:circle')->namespace;
my $sibling = $dom->next;
-Return L<DOM::Tiny> object for next sibling element or C<undef> if there are no
-more siblings.
+Return L<DOM::Tiny> object for next sibling element, or C<undef> if there are
+no more siblings.
# "<h2>123</h2>"
$dom->parse('<div><h1>Test</h1><h2>123</h2></div>')->at('h1')->next;
my $sibling = $dom->next_node;
-Return L<DOM::Tiny> object for next sibling node or C<undef> if there are no
+Return L<DOM::Tiny> object for next sibling node, or C<undef> if there are no
more siblings.
# "456"
my $parent = $dom->parent;
-Return L<DOM::Tiny> object for parent of this node or C<undef> if this node has
-no parent.
+Return L<DOM::Tiny> object for parent of this node, or C<undef> if this node
+has no parent.
+
+ # "<b><i>Test</i></b>"
+ $dom->parse('<p><b><i>Test</i></b></p>')->at('i')->parent;
=head2 parse
Parse HTML/XML fragment.
# Parse XML
- my $dom = DOM::Tiny->new->xml(1)->parse($xml);
+ my $dom = DOM::Tiny->new->xml(1)->parse('<foo>I ♥ DOM::Tiny!</foo>');
=head2 preceding
$dom = $dom->prepend('<p>I ♥ DOM::Tiny!</p>');
-Prepend HTML/XML fragment to this node.
+Prepend HTML/XML fragment to this node (for all node types other than C<root>).
# "<div><h1>Test</h1><h2>123</h2></div>"
$dom->parse('<div><h2>123</h2></div>')
my $sibling = $dom->previous;
-Return L<DOM::Tiny> object for previous sibling element or C<undef> if there
+Return L<DOM::Tiny> object for previous sibling element, or C<undef> if there
are no more siblings.
# "<h1>Test</h1>"
my $sibling = $dom->previous_node;
-Return L<DOM::Tiny> object for previous sibling node or C<undef> if there are
+Return L<DOM::Tiny> object for previous sibling node, or C<undef> if there are
no more siblings.
# "123"
my $value = $dom->val;
Extract value from form element (such as C<button>, C<input>, C<option>,
-C<select> and C<textarea>) or return C<undef> if this element has no value. In
+C<select> and C<textarea>), or return C<undef> if this element has no value. In
the case of C<select> with C<multiple> attribute, find C<option> elements with
-C<selected> attribute and return an array reference with all values or C<undef>
-if none could be found.
+C<selected> attribute and return an array reference with all values, or
+C<undef> if none could be found.
# "a"
- $dom->parse('<input name="test" value="a">')->at('input')->val;
+ $dom->parse('<input name=test value=a>')->at('input')->val;
# "b"
$dom->parse('<textarea>b</textarea>')->at('textarea')->val;
$dom->parse('<select multiple><option selected>e</option></select>')
->at('select')->val->[0];
+ # "on"
+ $dom->parse('<input name=test type=checkbox>')->at('input')->val;
+
=head2 wrap
$dom = $dom->wrap('<div></div>');
-Wrap HTML/XML fragment around this node, placing it as the last child of the
-first innermost element.
+Wrap HTML/XML fragment around this node (for all node types other than C<root>),
+placing it as the last child of the first innermost element.
# "<p>123<b>Test</b></p>"
$dom->parse('<b>Test</b>')->at('b')->wrap('<p>123</p>')->root;
$dom = $dom->wrap_content('<div></div>');
-Wrap HTML/XML fragment around this node's content, placing it as the last
-children of the first innermost element.
+Wrap HTML/XML fragment around this node's content (for C<root> and C<tag>
+nodes), placing it as the last children of the first innermost element.
# "<p><b>123Test</b></p>"
$dom->parse('<p>Test<p>')->at('p')->wrap_content('<b>123</b>')->root;
$dom = $dom->xml($bool);
Disable HTML semantics in parser and activate case-sensitivity, defaults to
-auto detection based on processing instructions.
+auto detection based on XML declarations.
=head1 COLLECTION METHODS
# Longer version
my $first = $collection->first(sub { $_->$method(@args) });
- # Find first value that contains the word "dom"
- my $interesting = $collection->first(qr/dom/i);
+ # Find first value that contains the word "tiny"
+ my $interesting = $collection->first(qr/tiny/i);
# Find first value that is greater than 5
my $greater = $collection->first(sub { $_ > 5 });
# Longer version
my $new = $collection->grep(sub { $_->$method(@args) });
- # Find all values that contain the word "dom"
- my $interesting = $collection->grep(qr/dom/i);
+ # Find all values that contain the word "tiny"
+ my $interesting = $collection->grep(qr/tiny/i);
# Find all values that are greater than 5
my $greater = $collection->grep(sub { $_ > 5 });
# Longer version
my $new = $collection->map(sub { $_->$method(@args) });
- # Append the word "dom" to all values
- my $domified = $collection->map(sub { $_ . 'dom' });
+ # Append the word "tiny" to all values
+ my $domified = $collection->map(sub { $_ . 'tiny' });
=head2 reduce
# $collection contains ([1, 2], [2, 1], [3, 2])
$collection->uniq(sub{ $_->[1] })->to_array; # "[[1, 2], [2, 1]]"
-=head1 SELECTORS
-
-L<DOM::Tiny> uses a CSS selector engine based on L<Mojo::DOM::CSS>. All CSS
-selectors that make sense for a standalone parser are supported.
-
-=head2 *
-
-Any element.
-
- my $all = $dom->find('*');
-
-=head2 E
-
-An element of type C<E>.
-
- my $title = $dom->at('title');
-
-=head2 E[foo]
-
-An C<E> element with a C<foo> attribute.
-
- my $links = $dom->find('a[href]');
-
-=head2 E[foo="bar"]
-
-An C<E> element whose C<foo> attribute value is exactly equal to C<bar>.
-
- my $case_sensitive = $dom->find('input[type="hidden"]');
- my $case_sensitive = $dom->find('input[type=hidden]');
-
-=head2 E[foo="bar" i]
-
-An C<E> element whose C<foo> attribute value is exactly equal to any
-(ASCII-range) case-permutation of C<bar>. Note that this selector is
-EXPERIMENTAL and might change without warning!
-
- my $case_insensitive = $dom->find('input[type="hidden" i]');
- my $case_insensitive = $dom->find('input[type=hidden i]');
- my $case_insensitive = $dom->find('input[class~="foo" i]');
-
-This selector is part of
-L<Selectors Level 4|http://dev.w3.org/csswg/selectors-4>, which is still a work
-in progress.
-
-=head2 E[foo~="bar"]
-
-An C<E> element whose C<foo> attribute value is a list of whitespace-separated
-values, one of which is exactly equal to C<bar>.
-
- my $foo = $dom->find('input[class~="foo"]');
- my $foo = $dom->find('input[class~=foo]');
-
-=head2 E[foo^="bar"]
-
-An C<E> element whose C<foo> attribute value begins exactly with the string
-C<bar>.
-
- my $begins_with = $dom->find('input[name^="f"]');
- my $begins_with = $dom->find('input[name^=f]');
-
-=head2 E[foo$="bar"]
-
-An C<E> element whose C<foo> attribute value ends exactly with the string
-C<bar>.
-
- my $ends_with = $dom->find('input[name$="o"]');
- my $ends_with = $dom->find('input[name$=o]');
-
-=head2 E[foo*="bar"]
-
-An C<E> element whose C<foo> attribute value contains the substring C<bar>.
-
- my $contains = $dom->find('input[name*="fo"]');
- my $contains = $dom->find('input[name*=fo]');
-
-=head2 E:root
-
-An C<E> element, root of the document.
-
- my $root = $dom->at(':root');
-
-=head2 E:nth-child(n)
-
-An C<E> element, the C<n-th> child of its parent.
-
- my $third = $dom->find('div:nth-child(3)');
- my $odd = $dom->find('div:nth-child(odd)');
- my $even = $dom->find('div:nth-child(even)');
- my $top3 = $dom->find('div:nth-child(-n+3)');
-
-=head2 E:nth-last-child(n)
-
-An C<E> element, the C<n-th> child of its parent, counting from the last one.
-
- my $third = $dom->find('div:nth-last-child(3)');
- my $odd = $dom->find('div:nth-last-child(odd)');
- my $even = $dom->find('div:nth-last-child(even)');
- my $bottom3 = $dom->find('div:nth-last-child(-n+3)');
-
-=head2 E:nth-of-type(n)
-
-An C<E> element, the C<n-th> sibling of its type.
-
- my $third = $dom->find('div:nth-of-type(3)');
- my $odd = $dom->find('div:nth-of-type(odd)');
- my $even = $dom->find('div:nth-of-type(even)');
- my $top3 = $dom->find('div:nth-of-type(-n+3)');
-
-=head2 E:nth-last-of-type(n)
-
-An C<E> element, the C<n-th> sibling of its type, counting from the last one.
-
- my $third = $dom->find('div:nth-last-of-type(3)');
- my $odd = $dom->find('div:nth-last-of-type(odd)');
- my $even = $dom->find('div:nth-last-of-type(even)');
- my $bottom3 = $dom->find('div:nth-last-of-type(-n+3)');
-
-=head2 E:first-child
-
-An C<E> element, first child of its parent.
-
- my $first = $dom->find('div p:first-child');
-
-=head2 E:last-child
-
-An C<E> element, last child of its parent.
-
- my $last = $dom->find('div p:last-child');
-
-=head2 E:first-of-type
-
-An C<E> element, first sibling of its type.
-
- my $first = $dom->find('div p:first-of-type');
-
-=head2 E:last-of-type
-
-An C<E> element, last sibling of its type.
-
- my $last = $dom->find('div p:last-of-type');
-
-=head2 E:only-child
-
-An C<E> element, only child of its parent.
-
- my $lonely = $dom->find('div p:only-child');
-
-=head2 E:only-of-type
-
-An C<E> element, only sibling of its type.
-
- my $lonely = $dom->find('div p:only-of-type');
-
-=head2 E:empty
-
-An C<E> element that has no children (including text nodes).
-
- my $empty = $dom->find(':empty');
-
-=head2 E:checked
-
-A user interface element C<E> which is checked (for instance a radio-button or
-checkbox).
-
- my $input = $dom->find(':checked');
-
-=head2 E.warning
-
-An C<E> element whose class is "warning".
-
- my $warning = $dom->find('div.warning');
-
-=head2 E#myid
-
-An C<E> element with C<ID> equal to "myid".
-
- my $foo = $dom->at('div#foo');
-
-=head2 E:not(s)
-
-An C<E> element that does not match simple selector C<s>.
-
- my $others = $dom->find('div p:not(:first-child)');
-
-=head2 E F
-
-An C<F> element descendant of an C<E> element.
-
- my $headlines = $dom->find('div h1');
-
-=head2 E E<gt> F
-
-An C<F> element child of an C<E> element.
-
- my $headlines = $dom->find('html > body > div > h1');
-
-=head2 E + F
-
-An C<F> element immediately preceded by an C<E> element.
-
- my $second = $dom->find('h1 + h2');
-
-=head2 E ~ F
-
-An C<F> element preceded by an C<E> element.
-
- my $second = $dom->find('h1 ~ h2');
-
-=head2 E, F, G
-
-Elements of type C<E>, C<F> and C<G>.
+=head1 BUGS
- my $headlines = $dom->find('h1, h2, h3');
+Report any issues on the public bugtracker.
-=head2 E[foo=bar][bar=baz]
+=head1 AUTHOR
-An C<E> element whose attributes match all following attribute selectors.
+Dan Book <dbook@cpan.org>
- my $links = $dom->find('a[foo^=b][foo$=ar]');
+Code and tests adapted from L<Mojo::DOM>, a lightweight DOM parser by the L<Mojolicious> team.
-=head1 BUGS
+=head1 CONTRIBUTORS
-Report any issues on the public bugtracker.
+=over
-=head1 AUTHOR
+=item Matt S Trout (mst)
-Dan Book <dbook@cpan.org>
+=back
=head1 COPYRIGHT AND LICENSE
-This software is Copyright (c) 2015 by Dan Book.
+Copyright (c) 2008-2015 Sebastian Riedel.
+
+Copyright (c) 2015 L</"AUTHOR"> and L</"CONTRIBUTORS"> for adaptation to standalone format.
This is free software, licensed under:
=head1 SEE ALSO
-L<Mojo::DOM>, L<XML::LibXML>, L<XML::Twig>, L<HTML::TreeBuilder>, L<XML::Smart>
+L<Mojo::DOM>, L<HTML::TreeBuilder>, L<XML::LibXML>, L<XML::Twig>, L<XML::Smart>
=for Pod::Coverage TO_JSON