001package org.jsoup.nodes; 002 003import org.jsoup.helper.Validate; 004import org.jsoup.internal.Normalizer; 005import org.jsoup.internal.QuietAppendable; 006import org.jsoup.helper.Regex; 007import org.jsoup.internal.StringUtil; 008import org.jsoup.parser.ParseSettings; 009import org.jsoup.parser.Parser; 010import org.jsoup.parser.Tag; 011import org.jsoup.parser.TokenQueue; 012import org.jsoup.select.Collector; 013import org.jsoup.select.Elements; 014import org.jsoup.select.Evaluator; 015import org.jsoup.select.NodeFilter; 016import org.jsoup.select.NodeVisitor; 017import org.jsoup.select.Nodes; 018import org.jsoup.select.Selector; 019import org.jspecify.annotations.Nullable; 020 021import java.lang.ref.WeakReference; 022import java.util.ArrayList; 023import java.util.Collection; 024import java.util.Collections; 025import java.util.Iterator; 026import java.util.LinkedHashSet; 027import java.util.List; 028import java.util.Map; 029import java.util.Set; 030import java.util.concurrent.atomic.AtomicBoolean; 031import java.util.function.Consumer; 032import java.util.regex.Pattern; 033import java.util.regex.PatternSyntaxException; 034import java.util.stream.Collectors; 035import java.util.stream.Stream; 036 037import static org.jsoup.internal.Normalizer.normalize; 038import static org.jsoup.nodes.Document.OutputSettings.Syntax.xml; 039import static org.jsoup.nodes.TextNode.lastCharIsWhitespace; 040import static org.jsoup.parser.Parser.NamespaceHtml; 041import static org.jsoup.parser.TokenQueue.escapeCssIdentifier; 042import static org.jsoup.select.Selector.evaluatorOf; 043 044/** 045 An HTML Element consists of a tag name, attributes, and child nodes (including text nodes and other elements). 046 <p> 047 From an Element, you can extract data, traverse the node graph, and manipulate the HTML. 048*/ 049public class Element extends Node implements Iterable<Element> { 050 private static final List<Element> EmptyChildren = Collections.emptyList(); 051 private static final NodeList EmptyNodeList = new NodeList(0); 052 static final String BaseUriKey = Attributes.internalKey("baseUri"); 053 Tag tag; 054 NodeList childNodes; 055 @Nullable Attributes attributes; // field is nullable but all methods for attributes are non-null 056 057 /** 058 * Create a new, standalone element, in the specified namespace. 059 * @param tag tag name 060 * @param namespace namespace for this element 061 */ 062 public Element(String tag, String namespace) { 063 this(Tag.valueOf(tag, namespace, ParseSettings.preserveCase), null); 064 } 065 066 /** 067 * Create a new, standalone element, in the HTML namespace. 068 * @param tag tag name 069 * @see #Element(String tag, String namespace) 070 */ 071 public Element(String tag) { 072 this(tag, Parser.NamespaceHtml); 073 } 074 075 /** 076 * Create a new, standalone Element. (Standalone in that it has no parent.) 077 * 078 * @param tag tag of this element 079 * @param baseUri the base URI (optional, may be null to inherit from parent, or "" to clear parent's) 080 * @param attributes initial attributes (optional, may be null) 081 * @see #appendChild(Node) 082 * @see #appendElement(String) 083 */ 084 public Element(Tag tag, @Nullable String baseUri, @Nullable Attributes attributes) { 085 Validate.notNull(tag); 086 childNodes = EmptyNodeList; 087 this.attributes = attributes; 088 this.tag = tag; 089 if (!StringUtil.isBlank(baseUri)) this.setBaseUri(baseUri); 090 } 091 092 /** 093 * Create a new Element from a Tag and a base URI. 094 * 095 * @param tag element tag 096 * @param baseUri the base URI of this element. Optional, and will inherit from its parent, if any. 097 * @see Tag#valueOf(String, ParseSettings) 098 */ 099 public Element(Tag tag, @Nullable String baseUri) { 100 this(tag, baseUri, null); 101 } 102 103 /** 104 Internal test to check if a nodelist object has been created. 105 */ 106 protected boolean hasChildNodes() { 107 return childNodes != EmptyNodeList; 108 } 109 110 @Override protected List<Node> ensureChildNodes() { 111 if (childNodes == EmptyNodeList) { 112 childNodes = new NodeList(4); 113 } 114 return childNodes; 115 } 116 117 @Override 118 protected boolean hasAttributes() { 119 return attributes != null; 120 } 121 122 @Override 123 public Attributes attributes() { 124 if (attributes == null) // not using hasAttributes, as doesn't clear warning 125 attributes = new Attributes(); 126 return attributes; 127 } 128 129 @Override 130 public String baseUri() { 131 String baseUri = searchUpForAttribute(this, BaseUriKey); 132 return baseUri != null ? baseUri : ""; 133 } 134 135 @Nullable 136 static String searchUpForAttribute(final Element start, final String key) { 137 Element el = start; 138 while (el != null) { 139 if (el.attributes != null && el.attributes.hasKey(key)) 140 return el.attributes.get(key); 141 el = el.parent(); 142 } 143 return null; 144 } 145 146 @Override 147 protected void doSetBaseUri(String baseUri) { 148 attributes().put(BaseUriKey, baseUri); 149 } 150 151 @Override 152 public int childNodeSize() { 153 return childNodes.size(); 154 } 155 156 @Override 157 public String nodeName() { 158 return tag.getName(); 159 } 160 161 /** 162 * Get the name of the tag for this element. E.g. {@code div}. If you are using {@link ParseSettings#preserveCase 163 * case preserving parsing}, this will return the source's original case. 164 * 165 * @return the tag name 166 */ 167 public String tagName() { 168 return tag.getName(); 169 } 170 171 /** 172 * Get the normalized name of this Element's tag. This will always be the lower-cased version of the tag, regardless 173 * of the tag case preserving setting of the parser. For e.g., {@code <DIV>} and {@code <div>} both have a 174 * normal name of {@code div}. 175 * @return normal name 176 */ 177 @Override 178 public String normalName() { 179 return tag.normalName(); 180 } 181 182 /** 183 Test if this Element has the specified normalized name, and is in the specified namespace. 184 * @param normalName a normalized element name (e.g. {@code div}). 185 * @param namespace the namespace 186 * @return true if the element's normal name matches exactly, and is in the specified namespace 187 * @since 1.17.2 188 */ 189 public boolean elementIs(String normalName, String namespace) { 190 return tag.normalName().equals(normalName) && tag.namespace().equals(namespace); 191 } 192 193 /** 194 * Change (rename) the tag of this element. For example, convert a {@code <span>} to a {@code <div>} with 195 * {@code el.tagName("div");}. 196 * 197 * @param tagName new tag name for this element 198 * @return this element, for chaining 199 * @see Elements#tagName(String) 200 */ 201 public Element tagName(String tagName) { 202 return tagName(tagName, tag.namespace()); 203 } 204 205 /** 206 * Change (rename) the tag of this element. For example, convert a {@code <span>} to a {@code <div>} with 207 * {@code el.tagName("div");}. 208 * 209 * @param tagName new tag name for this element 210 * @param namespace the new namespace for this element 211 * @return this element, for chaining 212 * @see Elements#tagName(String) 213 */ 214 public Element tagName(String tagName, String namespace) { 215 Validate.notEmptyParam(tagName, "tagName"); 216 Validate.notEmptyParam(namespace, "namespace"); 217 Parser parser = NodeUtils.parser(this); 218 tag = parser.tagSet().valueOf(tagName, namespace, parser.settings()); // maintains the case option of the original parse 219 return this; 220 } 221 222 /** 223 * Get the Tag for this element. 224 * 225 * @return the tag object 226 */ 227 public Tag tag() { 228 return tag; 229 } 230 231 /** 232 Change the Tag of this element. 233 @param tag the new tag 234 @return this element, for chaining 235 @since 1.20.1 236 */ 237 public Element tag(Tag tag) { 238 Validate.notNull(tag); 239 this.tag = tag; 240 return this; 241 } 242 243 /** 244 * Test if this element is a block-level element. (E.g. {@code <div> == true} or an inline element 245 * {@code <span> == false}). 246 * 247 * @return true if block, false if not (and thus inline) 248 */ 249 public boolean isBlock() { 250 return tag.isBlock(); 251 } 252 253 /** 254 * Get the {@code id} attribute of this element. 255 * 256 * @return The id attribute, if present, or an empty string if not. 257 */ 258 public String id() { 259 return attributes != null ? attributes.getIgnoreCase("id") :""; 260 } 261 262 /** 263 Set the {@code id} attribute of this element. 264 @param id the ID value to use 265 @return this Element, for chaining 266 */ 267 public Element id(String id) { 268 Validate.notNull(id); 269 attr("id", id); 270 return this; 271 } 272 273 /** 274 * Set an attribute value on this element. If this element already has an attribute with the 275 * key, its value is updated; otherwise, a new attribute is added. 276 * 277 * @return this element 278 */ 279 @Override public Element attr(String attributeKey, String attributeValue) { 280 super.attr(attributeKey, attributeValue); 281 return this; 282 } 283 284 /** 285 * Set a boolean attribute value on this element. Setting to <code>true</code> sets the attribute value to "" and 286 * marks the attribute as boolean so no value is written out. Setting to <code>false</code> removes the attribute 287 * with the same key if it exists. 288 * 289 * @param attributeKey the attribute key 290 * @param attributeValue the attribute value 291 * 292 * @return this element 293 */ 294 public Element attr(String attributeKey, boolean attributeValue) { 295 attributes().put(attributeKey, attributeValue); 296 return this; 297 } 298 299 /** 300 Get an Attribute by key. Changes made via {@link Attribute#setKey(String)}, {@link Attribute#setValue(String)} etc 301 will cascade back to this Element. 302 @param key the (case-sensitive) attribute key 303 @return the Attribute for this key, or null if not present. 304 @since 1.17.2 305 */ 306 @Nullable public Attribute attribute(String key) { 307 return hasAttributes() ? attributes().attribute(key) : null; 308 } 309 310 /** 311 * Get this element's HTML5 custom data attributes. Each attribute in the element that has a key 312 * starting with "data-" is included the dataset. 313 * <p> 314 * E.g., the element {@code <div data-package="jsoup" data-language="Java" class="group">...} has the dataset 315 * {@code package=jsoup, language=java}. 316 * <p> 317 * This map is a filtered view of the element's attribute map. Changes to one map (add, remove, update) are reflected 318 * in the other map. 319 * <p> 320 * You can find elements that have data attributes using the {@code [^data-]} attribute key prefix selector. 321 * @return a map of {@code key=value} custom data attributes. 322 */ 323 public Map<String, String> dataset() { 324 return attributes().dataset(); 325 } 326 327 @Override @Nullable 328 public final Element parent() { 329 return (Element) parentNode; 330 } 331 332 /** 333 * Get this element's parent and ancestors, up to the document root. 334 * @return this element's stack of parents, starting with the closest first. 335 */ 336 public Elements parents() { 337 Elements parents = new Elements(); 338 Element parent = this.parent(); 339 while (parent != null && !parent.nameIs("#root")) { 340 parents.add(parent); 341 parent = parent.parent(); 342 } 343 return parents; 344 } 345 346 /** 347 * Get a child element of this element, by its 0-based index number. 348 * <p> 349 * Note that an element can have both mixed Nodes and Elements as children. This method inspects 350 * a filtered list of children that are elements, and the index is based on that filtered list. 351 * </p> 352 * 353 * @param index the index number of the element to retrieve 354 * @return the child element, if it exists, otherwise throws an {@code IndexOutOfBoundsException} 355 * @see #childNode(int) 356 */ 357 public Element child(int index) { 358 Validate.isTrue(index >= 0, "Index must be >= 0"); 359 List<Element> cached = cachedChildren(); 360 if (cached != null) return cached.get(index); 361 // otherwise, iter on elementChild; saves creating list 362 int size = childNodes.size(); 363 for (int i = 0, e = 0; i < size; i++) { // direct iter is faster than chasing firstElSib, nextElSibd 364 Node node = childNodes.get(i); 365 if (node instanceof Element) { 366 if (e++ == index) return (Element) node; 367 } 368 } 369 throw new IndexOutOfBoundsException("No child at index: " + index); 370 } 371 372 /** 373 * Get the number of child nodes of this element that are elements. 374 * <p> 375 * This method works on the same filtered list like {@link #child(int)}. Use {@link #childNodes()} and {@link 376 * #childNodeSize()} to get the unfiltered Nodes (e.g. includes TextNodes etc.) 377 * </p> 378 * 379 * @return the number of child nodes that are elements 380 * @see #children() 381 * @see #child(int) 382 */ 383 public int childrenSize() { 384 if (childNodeSize() == 0) return 0; 385 return childElementsList().size(); // gets children into cache; faster subsequent child(i) if unmodified 386 } 387 388 /** 389 * Get this element's child elements. 390 * <p> 391 * This is effectively a filter on {@link #childNodes()} to get Element nodes. 392 * </p> 393 * @return child elements. If this element has no children, returns an empty list. 394 * @see #childNodes() 395 */ 396 public Elements children() { 397 return new Elements(childElementsList()); 398 } 399 400 /** 401 * Maintains a shadow copy of this element's child elements. If the nodelist is changed, this cache is invalidated. 402 * @return a list of child elements 403 */ 404 List<Element> childElementsList() { 405 if (childNodeSize() == 0) return EmptyChildren; // short circuit creating empty 406 // set atomically, so works in multi-thread. Calling methods look like reads, so should be thread-safe 407 synchronized (childNodes) { // sync vs re-entrant lock, to save another field 408 List<Element> children = cachedChildren(); 409 if (children == null) { 410 children = filterNodes(Element.class); 411 stashChildren(children); 412 } 413 return children; 414 } 415 } 416 417 private static final String childElsKey = "jsoup.childEls"; 418 private static final String childElsMod = "jsoup.childElsMod"; 419 420 /** returns the cached child els, if they exist, and the modcount of our childnodes matches the stashed modcount */ 421 @SuppressWarnings("unchecked") 422 @Nullable List<Element> cachedChildren() { 423 if (attributes == null || !attributes.hasUserData()) return null; // don't create empty userdata 424 Map<String, Object> userData = attributes.userData(); 425 WeakReference<List<Element>> ref = (WeakReference<List<Element>>) userData.get(childElsKey); 426 if (ref != null) { 427 List<Element> els = ref.get(); 428 if (els != null) { 429 Integer modCount = (Integer) userData.get(childElsMod); 430 if (modCount != null && modCount == childNodes.modCount()) 431 return els; 432 } 433 } 434 return null; 435 } 436 437 /** caches the child els into the Attribute user data. */ 438 private void stashChildren(List<Element> els) { 439 Map<String, Object> userData = attributes().userData(); 440 WeakReference<List<Element>> ref = new WeakReference<>(els); 441 userData.put(childElsKey, ref); 442 userData.put(childElsMod, childNodes.modCount()); 443 } 444 445 /** 446 Returns a Stream of this Element and all of its descendant Elements. The stream has document order. 447 @return a stream of this element and its descendants. 448 @see #nodeStream() 449 @since 1.17.1 450 */ 451 public Stream<Element> stream() { 452 return NodeUtils.stream(this, Element.class); 453 } 454 455 private <T> List<T> filterNodes(Class<T> clazz) { 456 return childNodes.stream() 457 .filter(clazz::isInstance) 458 .map(clazz::cast) 459 .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)); 460 } 461 462 /** 463 * Get this element's child text nodes. The list is unmodifiable but the text nodes may be manipulated. 464 * <p> 465 * This is effectively a filter on {@link #childNodes()} to get Text nodes. 466 * @return child text nodes. If this element has no text nodes, returns an 467 * empty list. 468 * </p> 469 * For example, with the input HTML: {@code <p>One <span>Two</span> Three <br> Four</p>} with the {@code p} element selected: 470 * <ul> 471 * <li>{@code p.text()} = {@code "One Two Three Four"}</li> 472 * <li>{@code p.ownText()} = {@code "One Three Four"}</li> 473 * <li>{@code p.children()} = {@code Elements[<span>, <br>]}</li> 474 * <li>{@code p.childNodes()} = {@code List<Node>["One ", <span>, " Three ", <br>, " Four"]}</li> 475 * <li>{@code p.textNodes()} = {@code List<TextNode>["One ", " Three ", " Four"]}</li> 476 * </ul> 477 */ 478 public List<TextNode> textNodes() { 479 return filterNodes(TextNode.class); 480 } 481 482 /** 483 * Get this element's child data nodes. The list is unmodifiable but the data nodes may be manipulated. 484 * <p> 485 * This is effectively a filter on {@link #childNodes()} to get Data nodes. 486 * </p> 487 * @return child data nodes. If this element has no data nodes, returns an 488 * empty list. 489 * @see #data() 490 */ 491 public List<DataNode> dataNodes() { 492 return filterNodes(DataNode.class); 493 } 494 495 /** 496 * Find elements that match the {@link Selector} CSS query, with this element as the starting context. Matched elements 497 * may include this element, or any of its descendents. 498 * <p>If the query starts with a combinator (e.g. {@code *} or {@code >}), that will combine to this element.</p> 499 * <p>This method is generally more powerful to use than the DOM-type {@code getElementBy*} methods, because 500 * multiple filters can be combined, e.g.:</p> 501 * <ul> 502 * <li>{@code el.select("a[href]")} - finds links ({@code a} tags with {@code href} attributes)</li> 503 * <li>{@code el.select("a[href*=example.com]")} - finds links pointing to example.com (loosely)</li> 504 * <li>{@code el.select("* div")} - finds all divs that descend from this element (and excludes this element)</li> 505 * <li>{@code el.select("> div")} - finds all divs that are direct children of this element (and excludes this element)</li> 506 * </ul> 507 * <p>See the query syntax documentation in {@link org.jsoup.select.Selector}.</p> 508 * <p>Also known as {@code querySelectorAll()} in the Web DOM.</p> 509 * 510 * @param cssQuery a {@link Selector} CSS-like query 511 * @return an {@link Elements} list containing elements that match the query (empty if none match) 512 * @see Selector selector query syntax 513 * @see #select(Evaluator) 514 * @throws Selector.SelectorParseException (unchecked) on an invalid CSS query. 515 */ 516 public Elements select(String cssQuery) { 517 return Selector.select(cssQuery, this); 518 } 519 520 /** 521 * Find elements that match the supplied Evaluator. This has the same functionality as {@link #select(String)}, but 522 * may be useful if you are running the same query many times (on many documents) and want to save the overhead of 523 * repeatedly parsing the CSS query. 524 * @param evaluator an element evaluator 525 * @return an {@link Elements} list containing elements that match the query (empty if none match) 526 * @see Selector#evaluatorOf(String css) 527 */ 528 public Elements select(Evaluator evaluator) { 529 return Selector.select(evaluator, this); 530 } 531 532 /** 533 Selects elements from the given root that match the specified {@link Selector} CSS query, with this element as the 534 starting context, and returns them as a lazy Stream. Matched elements may include this element, or any of its 535 children. 536 <p> 537 Unlike {@link #select(String query)}, which returns a complete list of all matching elements, this method returns a 538 {@link Stream} that processes elements lazily as they are needed. The stream operates in a "pull" model — elements 539 are fetched from the root as the stream is traversed. You can use standard {@code Stream} operations such as 540 {@code filter}, {@code map}, or {@code findFirst} to process elements on demand. 541 </p> 542 543 @param cssQuery a {@link Selector} CSS-like query 544 @return a {@link Stream} containing elements that match the query (empty if none match) 545 @throws Selector.SelectorParseException (unchecked) on an invalid CSS query. 546 @see Selector selector query syntax 547 @see #selectStream(Evaluator eval) 548 @since 1.19.1 549 */ 550 public Stream<Element> selectStream(String cssQuery) { 551 return Selector.selectStream(cssQuery, this); 552 } 553 554 /** 555 Find a Stream of elements that match the supplied Evaluator. 556 557 @param evaluator an element Evaluator 558 @return a {@link Stream} containing elements that match the query (empty if none match) 559 @see Selector#evaluatorOf(String css) 560 @since 1.19.1 561 */ 562 public Stream<Element> selectStream(Evaluator evaluator) { 563 return Selector.selectStream(evaluator, this); 564 } 565 566 /** 567 * Find the first Element that matches the {@link Selector} CSS query, with this element as the starting context. 568 * <p>This is effectively the same as calling {@code element.select(query).first()}, but is more efficient as query 569 * execution stops on the first hit.</p> 570 * <p>Also known as {@code querySelector()} in the Web DOM.</p> 571 * @param cssQuery cssQuery a {@link Selector} CSS-like query 572 * @return the first matching element, or <b>{@code null}</b> if there is no match. 573 * @see #expectFirst(String) 574 */ 575 public @Nullable Element selectFirst(String cssQuery) { 576 return Selector.selectFirst(cssQuery, this); 577 } 578 579 /** 580 * Finds the first Element that matches the supplied Evaluator, with this element as the starting context, or 581 * {@code null} if none match. 582 * 583 * @param evaluator an element evaluator 584 * @return the first matching element (walking down the tree, starting from this element), or {@code null} if none 585 * match. 586 */ 587 public @Nullable Element selectFirst(Evaluator evaluator) { 588 return Collector.findFirst(evaluator, this); 589 } 590 591 /** 592 Just like {@link #selectFirst(String)}, but if there is no match, throws an {@link IllegalArgumentException}. This 593 is useful if you want to simply abort processing on a failed match. 594 @param cssQuery a {@link Selector} CSS-like query 595 @return the first matching element 596 @throws IllegalArgumentException if no match is found 597 @since 1.15.2 598 */ 599 public Element expectFirst(String cssQuery) { 600 return Validate.expectNotNull( 601 Selector.selectFirst(cssQuery, this), 602 parent() != null ? 603 "No elements matched the query '%s' on element '%s'." : 604 "No elements matched the query '%s' in the document." 605 , cssQuery, this.tagName() 606 ); 607 } 608 609 /** 610 Find nodes that match the supplied {@link Evaluator}, with this element as the starting context. Matched 611 nodes may include this element, or any of its descendents. 612 613 @param evaluator an evaluator 614 @return a list of nodes that match the query (empty if none match) 615 @since 1.21.1 616 */ 617 public Nodes<Node> selectNodes(Evaluator evaluator) { 618 return selectNodes(evaluator, Node.class); 619 } 620 621 /** 622 Find nodes that match the supplied {@link Selector} CSS query, with this element as the starting context. Matched 623 nodes may include this element, or any of its descendents. 624 <p>To select leaf nodes, the query should specify the node type, e.g. {@code ::text}, 625 {@code ::comment}, {@code ::data}, {@code ::leafnode}.</p> 626 627 @param cssQuery a {@link Selector} CSS query 628 @return a list of nodes that match the query (empty if none match) 629 @since 1.21.1 630 */ 631 public Nodes<Node> selectNodes(String cssQuery) { 632 return selectNodes(cssQuery, Node.class); 633 } 634 635 /** 636 Find nodes that match the supplied Evaluator, with this element as the starting context. Matched 637 nodes may include this element, or any of its descendents. 638 639 @param evaluator an evaluator 640 @param type the type of node to collect (e.g. {@link Element}, {@link LeafNode}, {@link TextNode} etc) 641 @param <T> the type of node to collect 642 @return a list of nodes that match the query (empty if none match) 643 @since 1.21.1 644 */ 645 public <T extends Node> Nodes<T> selectNodes(Evaluator evaluator, Class<T> type) { 646 Validate.notNull(evaluator); 647 return Collector.collectNodes(evaluator, this, type); 648 } 649 650 /** 651 Find nodes that match the supplied {@link Selector} CSS query, with this element as the starting context. Matched 652 nodes may include this element, or any of its descendents. 653 <p>To select specific node types, use {@code ::text}, {@code ::comment}, {@code ::leafnode}, etc. For example, to 654 select all text nodes under {@code p} elements: </p> 655 <pre> Nodes<TextNode> textNodes = doc.selectNodes("p ::text", TextNode.class);</pre> 656 657 @param cssQuery a {@link Selector} CSS query 658 @param type the type of node to collect (e.g. {@link Element}, {@link LeafNode}, {@link TextNode} etc) 659 @param <T> the type of node to collect 660 @return a list of nodes that match the query (empty if none match) 661 @since 1.21.1 662 */ 663 public <T extends Node> Nodes<T> selectNodes(String cssQuery, Class<T> type) { 664 Validate.notEmpty(cssQuery); 665 return selectNodes(evaluatorOf(cssQuery), type); 666 } 667 668 /** 669 Find the first Node that matches the {@link Selector} CSS query, with this element as the starting context. 670 <p>This is effectively the same as calling {@code element.selectNodes(query).first()}, but is more efficient as 671 query 672 execution stops on the first hit.</p> 673 <p>Also known as {@code querySelector()} in the Web DOM.</p> 674 675 @param cssQuery cssQuery a {@link Selector} CSS-like query 676 @return the first matching node, or <b>{@code null}</b> if there is no match. 677 @since 1.21.1 678 @see #expectFirst(String) 679 */ 680 public @Nullable <T extends Node> T selectFirstNode(String cssQuery, Class<T> type) { 681 return selectFirstNode(evaluatorOf(cssQuery), type); 682 } 683 684 /** 685 Finds the first Node that matches the supplied Evaluator, with this element as the starting context, or 686 {@code null} if none match. 687 688 @param evaluator an element evaluator 689 @return the first matching node (walking down the tree, starting from this element), or {@code null} if none 690 match. 691 @since 1.21.1 692 */ 693 public @Nullable <T extends Node> T selectFirstNode(Evaluator evaluator, Class<T> type) { 694 return Collector.findFirstNode(evaluator, this, type); 695 } 696 697 /** 698 Just like {@link #selectFirstNode(String, Class)}, but if there is no match, throws an 699 {@link IllegalArgumentException}. This is useful if you want to simply abort processing on a failed match. 700 701 @param cssQuery a {@link Selector} CSS-like query 702 @return the first matching node 703 @throws IllegalArgumentException if no match is found 704 @since 1.21.1 705 */ 706 public <T extends Node> T expectFirstNode(String cssQuery, Class<T> type) { 707 return Validate.expectNotNull( 708 selectFirstNode(cssQuery, type), 709 parent() != null ? 710 "No nodes matched the query '%s' on element '%s'.": 711 "No nodes matched the query '%s' in the document." 712 , cssQuery, this.tagName() 713 ); 714 } 715 716 /** 717 * Checks if this element matches the given {@link Selector} CSS query. Also knows as {@code matches()} in the Web 718 * DOM. 719 * 720 * @param cssQuery a {@link Selector} CSS query 721 * @return if this element matches the query 722 */ 723 public boolean is(String cssQuery) { 724 return is(evaluatorOf(cssQuery)); 725 } 726 727 /** 728 * Check if this element matches the given evaluator. 729 * @param evaluator an element evaluator 730 * @return if this element matches 731 */ 732 public boolean is(Evaluator evaluator) { 733 return evaluator.matches(this.root(), this); 734 } 735 736 /** 737 * Find the closest element up the tree of parents that matches the specified CSS query. Will return itself, an 738 * ancestor, or {@code null} if there is no such matching element. 739 * @param cssQuery a {@link Selector} CSS query 740 * @return the closest ancestor element (possibly itself) that matches the provided evaluator. {@code null} if not 741 * found. 742 */ 743 public @Nullable Element closest(String cssQuery) { 744 return closest(evaluatorOf(cssQuery)); 745 } 746 747 /** 748 * Find the closest element up the tree of parents that matches the specified evaluator. Will return itself, an 749 * ancestor, or {@code null} if there is no such matching element. 750 * @param evaluator a query evaluator 751 * @return the closest ancestor element (possibly itself) that matches the provided evaluator. {@code null} if not 752 * found. 753 */ 754 public @Nullable Element closest(Evaluator evaluator) { 755 Validate.notNull(evaluator); 756 Element el = this; 757 final Element root = root(); 758 do { 759 if (evaluator.matches(root, el)) 760 return el; 761 el = el.parent(); 762 } while (el != null); 763 return null; 764 } 765 766 /** 767 Find Elements that match the supplied {@index XPath} expression. 768 <p>Note that for convenience of writing the Xpath expression, namespaces are disabled, and queries can be 769 expressed using the element's local name only.</p> 770 <p>By default, XPath 1.0 expressions are supported. If you would to use XPath 2.0 or higher, you can provide an 771 alternate XPathFactory implementation:</p> 772 <ol> 773 <li>Add the implementation to your classpath. E.g. to use <a href="https://www.saxonica.com/products/products.xml">Saxon-HE</a>, add <a href="https://mvnrepository.com/artifact/net.sf.saxon/Saxon-HE">net.sf.saxon:Saxon-HE</a> to your build.</li> 774 <li>Set the system property <code>javax.xml.xpath.XPathFactory:jsoup</code> to the implementing classname. E.g.:<br> 775 <code>System.setProperty(W3CDom.XPathFactoryProperty, "net.sf.saxon.xpath.XPathFactoryImpl");</code> 776 </li> 777 </ol> 778 779 @param xpath XPath expression 780 @return matching elements, or an empty list if none match. 781 @see #selectXpath(String, Class) 782 @since 1.14.3 783 */ 784 public Elements selectXpath(String xpath) { 785 return new Elements(NodeUtils.selectXpath(xpath, this, Element.class)); 786 } 787 788 /** 789 Find Nodes that match the supplied XPath expression. 790 <p>For example, to select TextNodes under {@code p} elements: </p> 791 <pre>List<TextNode> textNodes = doc.selectXpath("//body//p//text()", TextNode.class);</pre> 792 <p>Note that in the jsoup DOM, Attribute objects are not Nodes. To directly select attribute values, do something 793 like:</p> 794 <pre>List<String> hrefs = doc.selectXpath("//a").eachAttr("href");</pre> 795 @param xpath XPath expression 796 @param nodeType the jsoup node type to return 797 @see #selectXpath(String) 798 @return a list of matching nodes 799 @since 1.14.3 800 */ 801 public <T extends Node> List<T> selectXpath(String xpath, Class<T> nodeType) { 802 return NodeUtils.selectXpath(xpath, this, nodeType); 803 } 804 805 /** 806 * Insert a node to the end of this Element's children. The incoming node will be re-parented. 807 * 808 * @param child node to add. 809 * @return this Element, for chaining 810 * @see #prependChild(Node) 811 * @see #insertChildren(int, Collection) 812 */ 813 public Element appendChild(Node child) { 814 Validate.notNull(child); 815 816 // was - Node#addChildren(child). short-circuits an array create and a loop. 817 reparentChild(child); 818 ensureChildNodes(); 819 childNodes.add(child); 820 child.setSiblingIndex(childNodes.size() - 1); 821 return this; 822 } 823 824 /** 825 Insert the given nodes to the end of this Element's children. 826 827 @param children nodes to add 828 @return this Element, for chaining 829 @see #insertChildren(int, Collection) 830 */ 831 public Element appendChildren(Collection<? extends Node> children) { 832 insertChildren(-1, children); 833 return this; 834 } 835 836 /** 837 * Add this element to the supplied parent element, as its next child. 838 * 839 * @param parent element to which this element will be appended 840 * @return this element, so that you can continue modifying the element 841 */ 842 public Element appendTo(Element parent) { 843 Validate.notNull(parent); 844 parent.appendChild(this); 845 return this; 846 } 847 848 /** 849 * Add a node to the start of this element's children. 850 * 851 * @param child node to add. 852 * @return this element, so that you can add more child nodes or elements. 853 */ 854 public Element prependChild(Node child) { 855 Validate.notNull(child); 856 857 addChildren(0, child); 858 return this; 859 } 860 861 /** 862 Insert the given nodes to the start of this Element's children. 863 864 @param children nodes to add 865 @return this Element, for chaining 866 @see #insertChildren(int, Collection) 867 */ 868 public Element prependChildren(Collection<? extends Node> children) { 869 insertChildren(0, children); 870 return this; 871 } 872 873 874 /** 875 * Inserts the given child nodes into this element at the specified index. Current nodes will be shifted to the 876 * right. The inserted nodes will be moved from their current parent. To prevent moving, copy the nodes first. 877 * 878 * @param index 0-based index to insert children at. Specify {@code 0} to insert at the start, {@code -1} at the 879 * end 880 * @param children child nodes to insert 881 * @return this element, for chaining. 882 */ 883 public Element insertChildren(int index, Collection<? extends Node> children) { 884 Validate.notNull(children, "Children collection to be inserted must not be null."); 885 int currentSize = childNodeSize(); 886 if (index < 0) index += currentSize +1; // roll around 887 Validate.isTrue(index >= 0 && index <= currentSize, "Insert position out of bounds."); 888 addChildren(index, children.toArray(new Node[0])); 889 return this; 890 } 891 892 /** 893 * Inserts the given child nodes into this element at the specified index. Current nodes will be shifted to the 894 * right. The inserted nodes will be moved from their current parent. To prevent moving, copy the nodes first. 895 * 896 * @param index 0-based index to insert children at. Specify {@code 0} to insert at the start, {@code -1} at the 897 * end 898 * @param children child nodes to insert 899 * @return this element, for chaining. 900 */ 901 public Element insertChildren(int index, Node... children) { 902 Validate.notNull(children, "Children collection to be inserted must not be null."); 903 int currentSize = childNodeSize(); 904 if (index < 0) index += currentSize +1; // roll around 905 Validate.isTrue(index >= 0 && index <= currentSize, "Insert position out of bounds."); 906 907 addChildren(index, children); 908 return this; 909 } 910 911 /** 912 * Create a new element by tag name, and add it as this Element's last child. 913 * 914 * @param tagName the name of the tag (e.g. {@code div}). 915 * @return the new element, to allow you to add content to it, e.g.: 916 * {@code parent.appendElement("h1").attr("id", "header").text("Welcome");} 917 */ 918 public Element appendElement(String tagName) { 919 return appendElement(tagName, tag.namespace()); 920 } 921 922 /** 923 * Create a new element by tag name and namespace, add it as this Element's last child. 924 * 925 * @param tagName the name of the tag (e.g. {@code div}). 926 * @param namespace the namespace of the tag (e.g. {@link Parser#NamespaceHtml}) 927 * @return the new element, in the specified namespace 928 */ 929 public Element appendElement(String tagName, String namespace) { 930 Parser parser = NodeUtils.parser(this); 931 Element child = new Element(parser.tagSet().valueOf(tagName, namespace, parser.settings()), baseUri()); 932 appendChild(child); 933 return child; 934 } 935 936 /** 937 * Create a new element by tag name, and add it as this Element's first child. 938 * 939 * @param tagName the name of the tag (e.g. {@code div}). 940 * @return the new element, to allow you to add content to it, e.g.: 941 * {@code parent.prependElement("h1").attr("id", "header").text("Welcome");} 942 */ 943 public Element prependElement(String tagName) { 944 return prependElement(tagName, tag.namespace()); 945 } 946 947 /** 948 * Create a new element by tag name and namespace, and add it as this Element's first child. 949 * 950 * @param tagName the name of the tag (e.g. {@code div}). 951 * @param namespace the namespace of the tag (e.g. {@link Parser#NamespaceHtml}) 952 * @return the new element, in the specified namespace 953 */ 954 public Element prependElement(String tagName, String namespace) { 955 Parser parser = NodeUtils.parser(this); 956 Element child = new Element(parser.tagSet().valueOf(tagName, namespace, parser.settings()), baseUri()); 957 prependChild(child); 958 return child; 959 } 960 961 /** 962 * Create and append a new TextNode to this element. 963 * 964 * @param text the (un-encoded) text to add 965 * @return this element 966 */ 967 public Element appendText(String text) { 968 Validate.notNull(text); 969 TextNode node = new TextNode(text); 970 appendChild(node); 971 return this; 972 } 973 974 /** 975 * Create and prepend a new TextNode to this element. 976 * 977 * @param text the decoded text to add 978 * @return this element 979 */ 980 public Element prependText(String text) { 981 Validate.notNull(text); 982 TextNode node = new TextNode(text); 983 prependChild(node); 984 return this; 985 } 986 987 /** 988 * Add inner HTML to this element. The supplied HTML will be parsed, and each node appended to the end of the children. 989 * @param html HTML to add inside this element, after the existing HTML 990 * @return this element 991 * @see #html(String) 992 */ 993 public Element append(String html) { 994 Validate.notNull(html); 995 List<Node> nodes = NodeUtils.parser(this).parseFragmentInput(html, this, baseUri()); 996 addChildren(nodes.toArray(new Node[0])); 997 return this; 998 } 999 1000 /** 1001 * Add inner HTML into this element. The supplied HTML will be parsed, and each node prepended to the start of the element's children. 1002 * @param html HTML to add inside this element, before the existing HTML 1003 * @return this element 1004 * @see #html(String) 1005 */ 1006 public Element prepend(String html) { 1007 Validate.notNull(html); 1008 List<Node> nodes = NodeUtils.parser(this).parseFragmentInput(html, this, baseUri()); 1009 addChildren(0, nodes.toArray(new Node[0])); 1010 return this; 1011 } 1012 1013 /** 1014 * Insert the specified HTML into the DOM before this element (as a preceding sibling). 1015 * 1016 * @param html HTML to add before this element 1017 * @return this element, for chaining 1018 * @see #after(String) 1019 */ 1020 @Override 1021 public Element before(String html) { 1022 return (Element) super.before(html); 1023 } 1024 1025 /** 1026 * Insert the specified node into the DOM before this node (as a preceding sibling). 1027 * @param node to add before this element 1028 * @return this Element, for chaining 1029 * @see #after(Node) 1030 */ 1031 @Override 1032 public Element before(Node node) { 1033 return (Element) super.before(node); 1034 } 1035 1036 /** 1037 * Insert the specified HTML into the DOM after this element (as a following sibling). 1038 * 1039 * @param html HTML to add after this element 1040 * @return this element, for chaining 1041 * @see #before(String) 1042 */ 1043 @Override 1044 public Element after(String html) { 1045 return (Element) super.after(html); 1046 } 1047 1048 /** 1049 * Insert the specified node into the DOM after this node (as a following sibling). 1050 * @param node to add after this element 1051 * @return this element, for chaining 1052 * @see #before(Node) 1053 */ 1054 @Override 1055 public Element after(Node node) { 1056 return (Element) super.after(node); 1057 } 1058 1059 /** 1060 * Remove all the element's child nodes. Any attributes are left as-is. Each child node has its parent set to 1061 * {@code null}. 1062 * @return this element 1063 */ 1064 @Override 1065 public Element empty() { 1066 // Detach each of the children -> parent links: 1067 int size = childNodes.size(); 1068 for (int i = 0; i < size; i++) 1069 childNodes.get(i).parentNode = null; 1070 childNodes.clear(); 1071 return this; 1072 } 1073 1074 /** 1075 * Wrap the supplied HTML around this element. 1076 * 1077 * @param html HTML to wrap around this element, e.g. {@code <div class="head"></div>}. Can be arbitrarily deep. 1078 * @return this element, for chaining. 1079 */ 1080 @Override 1081 public Element wrap(String html) { 1082 return (Element) super.wrap(html); 1083 } 1084 1085 /** 1086 Gets an #id selector for this element, if it has a unique ID. Otherwise, returns an empty string. 1087 1088 @param ownerDoc the document that owns this element, if there is one 1089 */ 1090 private String uniqueIdSelector(@Nullable Document ownerDoc) { 1091 String id = id(); 1092 if (!id.isEmpty()) { // check if the ID is unique and matches this 1093 String idSel = "#" + escapeCssIdentifier(id); 1094 if (ownerDoc != null) { 1095 Elements els = ownerDoc.select(idSel); 1096 if (els.size() == 1 && els.get(0) == this) return idSel; 1097 } else { 1098 return idSel; 1099 } 1100 } 1101 return EmptyString; 1102 } 1103 1104 /** 1105 Get a CSS selector that will uniquely select this element. 1106 <p> 1107 If the element has an ID, returns #id; otherwise returns the parent (if any) CSS selector, followed by 1108 {@literal '>'}, followed by a unique selector for the element (tag.class.class:nth-child(n)). 1109 </p> 1110 1111 @return the CSS Path that can be used to retrieve the element in a selector. 1112 */ 1113 public String cssSelector() { 1114 Document ownerDoc = ownerDocument(); 1115 String idSel = uniqueIdSelector(ownerDoc); 1116 if (!idSel.isEmpty()) return idSel; 1117 1118 // No unique ID, work up the parent stack and find either a unique ID to hang from, or just a GP > Parent > Child chain 1119 StringBuilder selector = StringUtil.borrowBuilder(); 1120 Element el = this; 1121 while (el != null && !(el instanceof Document)) { 1122 idSel = el.uniqueIdSelector(ownerDoc); 1123 if (!idSel.isEmpty()) { 1124 selector.insert(0, idSel); 1125 break; // found a unique ID to use as ancestor; stop 1126 } 1127 selector.insert(0, el.cssSelectorComponent()); 1128 el = el.parent(); 1129 } 1130 return StringUtil.releaseBuilder(selector); 1131 } 1132 1133 private String cssSelectorComponent() { 1134 // Escape tagname, and translate HTML namespace ns:tag to CSS namespace syntax ns|tag 1135 String tagName = escapeCssIdentifier(tagName()).replace("\\:", "|"); 1136 StringBuilder selector = StringUtil.borrowBuilder().append(tagName); 1137 String classes = classNames().stream().map(TokenQueue::escapeCssIdentifier) 1138 .collect(StringUtil.joining(".")); 1139 if (!classes.isEmpty()) 1140 selector.append('.').append(classes); 1141 1142 if (parent() == null || parent() instanceof Document) // don't add Document to selector, as will always have a html node 1143 return StringUtil.releaseBuilder(selector); 1144 1145 selector.insert(0, " > "); 1146 if (parent().select(selector.toString()).size() > 1) 1147 selector.append(String.format( 1148 ":nth-child(%d)", elementSiblingIndex() + 1)); 1149 1150 return StringUtil.releaseBuilder(selector); 1151 } 1152 1153 /** 1154 * Get sibling elements. If the element has no sibling elements, returns an empty list. An element is not a sibling 1155 * of itself, so will not be included in the returned list. 1156 * @return sibling elements 1157 */ 1158 public Elements siblingElements() { 1159 if (parentNode == null) 1160 return new Elements(0); 1161 1162 List<Element> elements = parent().childElementsList(); 1163 Elements siblings = new Elements(elements.size() - 1); 1164 for (Element el: elements) 1165 if (el != this) 1166 siblings.add(el); 1167 return siblings; 1168 } 1169 1170 1171 1172 /** 1173 * Get each of the sibling elements that come after this element. 1174 * 1175 * @return each of the element siblings after this element, or an empty list if there are no next sibling elements 1176 */ 1177 public Elements nextElementSiblings() { 1178 return nextElementSiblings(true); 1179 } 1180 1181 /** 1182 * Get each of the element siblings before this element. 1183 * 1184 * @return the previous element siblings, or an empty list if there are none. 1185 */ 1186 public Elements previousElementSiblings() { 1187 return nextElementSiblings(false); 1188 } 1189 1190 private Elements nextElementSiblings(boolean next) { 1191 Elements els = new Elements(); 1192 if (parentNode == null) 1193 return els; 1194 els.add(this); 1195 return next ? els.nextAll() : els.prevAll(); 1196 } 1197 1198 /** 1199 * Gets the first Element sibling of this element. That may be this element. 1200 * @return the first sibling that is an element (aka the parent's first element child) 1201 */ 1202 public Element firstElementSibling() { 1203 if (parent() != null) { 1204 //noinspection DataFlowIssue (not nullable, would be this is no other sibs) 1205 return parent().firstElementChild(); 1206 } else 1207 return this; // orphan is its own first sibling 1208 } 1209 1210 /** 1211 * Get the list index of this element in its element sibling list. I.e. if this is the first element 1212 * sibling, returns 0. 1213 * @return position in element sibling list 1214 */ 1215 public int elementSiblingIndex() { 1216 if (parent() == null) return 0; 1217 return indexInList(this, parent().childElementsList()); 1218 } 1219 1220 /** 1221 * Gets the last element sibling of this element. That may be this element. 1222 * @return the last sibling that is an element (aka the parent's last element child) 1223 */ 1224 public Element lastElementSibling() { 1225 if (parent() != null) { 1226 //noinspection DataFlowIssue (not nullable, would be this if no other sibs) 1227 return parent().lastElementChild(); 1228 } else 1229 return this; 1230 } 1231 1232 private static <E extends Element> int indexInList(Element search, List<E> elements) { 1233 final int size = elements.size(); 1234 for (int i = 0; i < size; i++) { 1235 if (elements.get(i) == search) 1236 return i; 1237 } 1238 return 0; 1239 } 1240 1241 /** 1242 Gets the first child of this Element that is an Element, or {@code null} if there is none. 1243 @return the first Element child node, or null. 1244 @see #firstChild() 1245 @see #lastElementChild() 1246 @since 1.15.2 1247 */ 1248 public @Nullable Element firstElementChild() { 1249 int size = childNodes.size(); 1250 for (int i = 0; i < size; i++) { 1251 Node node = childNodes.get(i); 1252 if (node instanceof Element) return (Element) node; 1253 } 1254 return null; 1255 } 1256 1257 /** 1258 Gets the last child of this Element that is an Element, or @{code null} if there is none. 1259 @return the last Element child node, or null. 1260 @see #lastChild() 1261 @see #firstElementChild() 1262 @since 1.15.2 1263 */ 1264 public @Nullable Element lastElementChild() { 1265 for (int i = childNodes.size() - 1; i >= 0; i--) { 1266 Node node = childNodes.get(i); 1267 if (node instanceof Element) return (Element) node; 1268 } 1269 return null; 1270 } 1271 1272 // DOM type methods 1273 1274 /** 1275 * Finds elements, including and recursively under this element, with the specified tag name. 1276 * @param tagName The tag name to search for (case insensitively). 1277 * @return a matching unmodifiable list of elements. Will be empty if this element and none of its children match. 1278 */ 1279 public Elements getElementsByTag(String tagName) { 1280 Validate.notEmpty(tagName); 1281 tagName = normalize(tagName); 1282 1283 return Collector.collect(new Evaluator.Tag(tagName), this); 1284 } 1285 1286 /** 1287 * Find an element by ID, including or under this element. 1288 * <p> 1289 * Note that this finds the first matching ID, starting with this element. If you search down from a different 1290 * starting point, it is possible to find a different element by ID. For unique element by ID within a Document, 1291 * use {@link Document#getElementById(String)} 1292 * @param id The ID to search for. 1293 * @return The first matching element by ID, starting with this element, or null if none found. 1294 */ 1295 public @Nullable Element getElementById(String id) { 1296 Validate.notEmpty(id); 1297 return Collector.findFirst(new Evaluator.Id(id), this); 1298 } 1299 1300 /** 1301 * Find elements that have this class, including or under this element. Case-insensitive. 1302 * <p> 1303 * Elements can have multiple classes (e.g. {@code <div class="header round first">}). This method 1304 * checks each class, so you can find the above with {@code el.getElementsByClass("header");}. 1305 * 1306 * @param className the name of the class to search for. 1307 * @return elements with the supplied class name, empty if none 1308 * @see #hasClass(String) 1309 * @see #classNames() 1310 */ 1311 public Elements getElementsByClass(String className) { 1312 Validate.notEmpty(className); 1313 1314 return Collector.collect(new Evaluator.Class(className), this); 1315 } 1316 1317 /** 1318 * Find elements that have a named attribute set. Case-insensitive. 1319 * 1320 * @param key name of the attribute, e.g. {@code href} 1321 * @return elements that have this attribute, empty if none 1322 */ 1323 public Elements getElementsByAttribute(String key) { 1324 Validate.notEmpty(key); 1325 key = key.trim(); 1326 1327 return Collector.collect(new Evaluator.Attribute(key), this); 1328 } 1329 1330 /** 1331 * Find elements that have an attribute name starting with the supplied prefix. Use {@code data-} to find elements 1332 * that have HTML5 datasets. 1333 * @param keyPrefix name prefix of the attribute e.g. {@code data-} 1334 * @return elements that have attribute names that start with the prefix, empty if none. 1335 */ 1336 public Elements getElementsByAttributeStarting(String keyPrefix) { 1337 Validate.notEmpty(keyPrefix); 1338 keyPrefix = keyPrefix.trim(); 1339 1340 return Collector.collect(new Evaluator.AttributeStarting(keyPrefix), this); 1341 } 1342 1343 /** 1344 * Find elements that have an attribute with the specific value. Case-insensitive. 1345 * 1346 * @param key name of the attribute 1347 * @param value value of the attribute 1348 * @return elements that have this attribute with this value, empty if none 1349 */ 1350 public Elements getElementsByAttributeValue(String key, String value) { 1351 return Collector.collect(new Evaluator.AttributeWithValue(key, value), this); 1352 } 1353 1354 /** 1355 * Find elements that either do not have this attribute, or have it with a different value. Case-insensitive. 1356 * 1357 * @param key name of the attribute 1358 * @param value value of the attribute 1359 * @return elements that do not have a matching attribute 1360 */ 1361 public Elements getElementsByAttributeValueNot(String key, String value) { 1362 return Collector.collect(new Evaluator.AttributeWithValueNot(key, value), this); 1363 } 1364 1365 /** 1366 * Find elements that have attributes that start with the value prefix. Case-insensitive. 1367 * 1368 * @param key name of the attribute 1369 * @param valuePrefix start of attribute value 1370 * @return elements that have attributes that start with the value prefix 1371 */ 1372 public Elements getElementsByAttributeValueStarting(String key, String valuePrefix) { 1373 return Collector.collect(new Evaluator.AttributeWithValueStarting(key, valuePrefix), this); 1374 } 1375 1376 /** 1377 * Find elements that have attributes that end with the value suffix. Case-insensitive. 1378 * 1379 * @param key name of the attribute 1380 * @param valueSuffix end of the attribute value 1381 * @return elements that have attributes that end with the value suffix 1382 */ 1383 public Elements getElementsByAttributeValueEnding(String key, String valueSuffix) { 1384 return Collector.collect(new Evaluator.AttributeWithValueEnding(key, valueSuffix), this); 1385 } 1386 1387 /** 1388 * Find elements that have attributes whose value contains the match string. Case-insensitive. 1389 * 1390 * @param key name of the attribute 1391 * @param match substring of value to search for 1392 * @return elements that have attributes containing this text 1393 */ 1394 public Elements getElementsByAttributeValueContaining(String key, String match) { 1395 return Collector.collect(new Evaluator.AttributeWithValueContaining(key, match), this); 1396 } 1397 1398 /** 1399 * Find elements that have an attribute whose value matches the supplied regular expression. 1400 * @param key name of the attribute 1401 * @param pattern compiled regular expression to match against attribute values 1402 * @return elements that have attributes matching this regular expression 1403 */ 1404 public Elements getElementsByAttributeValueMatching(String key, Pattern pattern) { 1405 return Collector.collect(new Evaluator.AttributeWithValueMatching(key, pattern), this); 1406 } 1407 1408 /** 1409 * Find elements that have attributes whose values match the supplied regular expression. 1410 * @param key name of the attribute 1411 * @param regex regular expression to match against attribute values. You can use <a href="http://java.sun.com/docs/books/tutorial/essential/regex/pattern.html#embedded">embedded flags</a> (such as {@code (?i)} and {@code (?m)}) to control regex options. 1412 * @return elements that have attributes matching this regular expression 1413 */ 1414 public Elements getElementsByAttributeValueMatching(String key, String regex) { 1415 Regex pattern; 1416 try { 1417 pattern = Regex.compile(regex); 1418 } catch (PatternSyntaxException e) { 1419 throw new IllegalArgumentException("Pattern syntax error: " + regex, e); 1420 } 1421 return Collector.collect(new Evaluator.AttributeWithValueMatching(key, pattern), this); 1422 } 1423 1424 /** 1425 * Find elements whose sibling index is less than the supplied index. 1426 * @param index 0-based index 1427 * @return elements less than index 1428 */ 1429 public Elements getElementsByIndexLessThan(int index) { 1430 return Collector.collect(new Evaluator.IndexLessThan(index), this); 1431 } 1432 1433 /** 1434 * Find elements whose sibling index is greater than the supplied index. 1435 * @param index 0-based index 1436 * @return elements greater than index 1437 */ 1438 public Elements getElementsByIndexGreaterThan(int index) { 1439 return Collector.collect(new Evaluator.IndexGreaterThan(index), this); 1440 } 1441 1442 /** 1443 * Find elements whose sibling index is equal to the supplied index. 1444 * @param index 0-based index 1445 * @return elements equal to index 1446 */ 1447 public Elements getElementsByIndexEquals(int index) { 1448 return Collector.collect(new Evaluator.IndexEquals(index), this); 1449 } 1450 1451 /** 1452 * Find elements that contain the specified string. The search is case-insensitive. The text may appear directly 1453 * in the element, or in any of its descendants. 1454 * @param searchText to look for in the element's text 1455 * @return elements that contain the string, case-insensitive. 1456 * @see Element#text() 1457 */ 1458 public Elements getElementsContainingText(String searchText) { 1459 return Collector.collect(new Evaluator.ContainsText(searchText), this); 1460 } 1461 1462 /** 1463 * Find elements that directly contain the specified string. The search is case-insensitive. The text must appear directly 1464 * in the element, not in any of its descendants. 1465 * @param searchText to look for in the element's own text 1466 * @return elements that contain the string, case-insensitive. 1467 * @see Element#ownText() 1468 */ 1469 public Elements getElementsContainingOwnText(String searchText) { 1470 return Collector.collect(new Evaluator.ContainsOwnText(searchText), this); 1471 } 1472 1473 /** 1474 * Find elements whose text matches the supplied regular expression. 1475 * @param pattern regular expression to match text against 1476 * @return elements matching the supplied regular expression. 1477 * @see Element#text() 1478 */ 1479 public Elements getElementsMatchingText(Pattern pattern) { 1480 return Collector.collect(new Evaluator.Matches(pattern), this); 1481 } 1482 1483 /** 1484 * Find elements whose text matches the supplied regular expression. 1485 * @param regex regular expression to match text against. You can use <a href="http://java.sun.com/docs/books/tutorial/essential/regex/pattern.html#embedded">embedded flags</a> (such as {@code (?i)} and {@code (?m)}) to control regex options. 1486 * @return elements matching the supplied regular expression. 1487 * @see Element#text() 1488 */ 1489 public Elements getElementsMatchingText(String regex) { 1490 Regex pattern; 1491 try { 1492 pattern = Regex.compile(regex); 1493 } catch (PatternSyntaxException e) { 1494 throw new IllegalArgumentException("Pattern syntax error: " + regex, e); 1495 } 1496 return Collector.collect(new Evaluator.Matches(pattern), this); 1497 } 1498 1499 /** 1500 * Find elements whose own text matches the supplied regular expression. 1501 * @param pattern regular expression to match text against 1502 * @return elements matching the supplied regular expression. 1503 * @see Element#ownText() 1504 */ 1505 public Elements getElementsMatchingOwnText(Pattern pattern) { 1506 return Collector.collect(new Evaluator.MatchesOwn(pattern), this); 1507 } 1508 1509 /** 1510 * Find elements whose own text matches the supplied regular expression. 1511 * @param regex regular expression to match text against. You can use <a href="http://java.sun.com/docs/books/tutorial/essential/regex/pattern.html#embedded">embedded flags</a> (such as {@code (?i)} and {@code (?m)}) to control regex options. 1512 * @return elements matching the supplied regular expression. 1513 * @see Element#ownText() 1514 */ 1515 public Elements getElementsMatchingOwnText(String regex) { 1516 Regex pattern; 1517 try { 1518 pattern = Regex.compile(regex); 1519 } catch (PatternSyntaxException e) { 1520 throw new IllegalArgumentException("Pattern syntax error: " + regex, e); 1521 } 1522 return Collector.collect(new Evaluator.MatchesOwn(pattern), this); 1523 } 1524 1525 /** 1526 * Find all elements under this element (including self, and children of children). 1527 * 1528 * @return all elements 1529 */ 1530 public Elements getAllElements() { 1531 return Collector.collect(new Evaluator.AllElements(), this); 1532 } 1533 1534 /** 1535 Gets the <b>normalized, combined text</b> of this element and all its children. Whitespace is normalized and 1536 trimmed. 1537 <p>For example, given HTML {@code <p>Hello <b>there</b> now! </p>}, {@code p.text()} returns {@code "Hello there 1538 now!"} 1539 <p>If you do not want normalized text, use {@link #wholeText()}. If you want just the text of this node (and not 1540 children), use {@link #ownText()}. 1541 <p>This method returns normalized, readable plain text for downstream uses such as data extraction, 1542 indexing, and accessibility-oriented processing. The contents of data nodes (such as 1543 {@code <script>} tags) are not considered text. Use {@link #data()} or {@link #html()} to retrieve 1544 that content. 1545 1546 @return decoded, normalized text, or empty string if none. 1547 @see #wholeText() 1548 @see #ownText() 1549 @see #textNodes() 1550 */ 1551 public String text() { 1552 final StringBuilder accum = StringUtil.borrowBuilder(); 1553 new TextAccumulator(accum).traverse(this); 1554 return StringUtil.releaseBuilder(accum).trim(); 1555 } 1556 1557 private static class TextAccumulator implements NodeVisitor { 1558 private final StringBuilder accum; 1559 1560 public TextAccumulator(StringBuilder accum) { 1561 this.accum = accum; 1562 } 1563 1564 @Override public void head(Node node, int depth) { 1565 if (node instanceof TextNode) { 1566 TextNode textNode = (TextNode) node; 1567 appendNormalisedText(accum, textNode); 1568 } else if (node instanceof Element) { 1569 Element element = (Element) node; 1570 // add a synthetic space before leading blocks and readable boundaries when text would otherwise run together 1571 if (accum.length() > 0 && needsLeadingTextSeparator(element) && !lastCharIsWhitespace(accum)) 1572 accum.append(' '); 1573 } 1574 } 1575 1576 @Override public void tail(Node node, int depth) { 1577 // make sure there is a space between block or readable-boundary tags and immediately following text nodes or inline elements. 1578 if (node instanceof Element) { 1579 Element element = (Element) node; 1580 Node next = node.nextSibling(); 1581 if (needsTrailingTextSeparator(element) && 1582 (next instanceof TextNode || next instanceof Element && ((Element) next).tag.isInline()) && 1583 !lastCharIsWhitespace(accum)) 1584 accum.append(' '); 1585 } 1586 1587 } 1588 1589 /** check if an element should separate preceding text during text() */ 1590 private static boolean needsLeadingTextSeparator(Element element) { 1591 return element.isBlock() 1592 || element.nameIs("br") 1593 || element.tag.is(Tag.TextBoundary) && element.childNodeSize() > 0 && element.hasText(); 1594 } 1595 1596 /** check if an element should separate following text during text() */ 1597 private static boolean needsTrailingTextSeparator(Element element) { 1598 return element.tag.is(Tag.TextBoundary) 1599 || !element.tag.isInline() 1600 || hasBlockChild(element); 1601 } 1602 1603 /** check if an inline wrapper contains direct block children and should close with a separator */ 1604 private static boolean hasBlockChild(Element element) { 1605 for (int i = 0; i < element.childNodeSize(); i++) { 1606 Node child = element.childNode(i); 1607 if (child instanceof Element && ((Element) child).isBlock()) 1608 return true; 1609 } 1610 return false; 1611 } 1612 } 1613 1614 /** 1615 Get the decoded text of this element and its children, preserving source whitespace and newlines from text nodes. 1616 Unlike {@link #text()}, no separators are inferred around element boundaries; {@code <br>} elements are returned 1617 as newlines. 1618 @return decoded, non-normalized text 1619 @see #text() 1620 @see #wholeOwnText() 1621 */ 1622 public String wholeText() { 1623 return wholeTextOf(nodeStream()); 1624 } 1625 1626 /** 1627 An Element's nodeValue is its whole own text. 1628 */ 1629 @Override 1630 public String nodeValue() { 1631 return wholeOwnText(); 1632 } 1633 1634 private static String wholeTextOf(Stream<Node> stream) { 1635 return stream.map(node -> { 1636 if (node instanceof TextNode) return ((TextNode) node).getWholeText(); 1637 if (node.nameIs("br")) return "\n"; 1638 return ""; 1639 }).collect(StringUtil.joining("")); 1640 } 1641 1642 /** 1643 Get the non-normalized, decoded text of this element, <b>not including</b> any child elements, including any 1644 newlines and spaces present in the original source. 1645 @return decoded, non-normalized text that is a direct child of this Element 1646 @see #text() 1647 @see #wholeText() 1648 @see #ownText() 1649 @since 1.15.1 1650 */ 1651 public String wholeOwnText() { 1652 return wholeTextOf(childNodes.stream()); 1653 } 1654 1655 /** 1656 * Gets the (normalized) text owned by this element only; does not get the combined text of all children. 1657 * <p> 1658 * For example, given HTML {@code <p>Hello <b>there</b> now!</p>}, {@code p.ownText()} returns {@code "Hello now!"}, 1659 * whereas {@code p.text()} returns {@code "Hello there now!"}. 1660 * Note that the text within the {@code b} element is not returned, as it is not a direct child of the {@code p} element. 1661 * 1662 * @return decoded text, or empty string if none. 1663 * @see #text() 1664 * @see #textNodes() 1665 */ 1666 public String ownText() { 1667 StringBuilder sb = StringUtil.borrowBuilder(); 1668 ownText(sb); 1669 return StringUtil.releaseBuilder(sb).trim(); 1670 } 1671 1672 private void ownText(StringBuilder accum) { 1673 for (int i = 0; i < childNodeSize(); i++) { 1674 Node child = childNodes.get(i); 1675 if (child instanceof TextNode) { 1676 TextNode textNode = (TextNode) child; 1677 appendNormalisedText(accum, textNode); 1678 } else if (child.nameIs("br") && !lastCharIsWhitespace(accum)) { 1679 accum.append(" "); 1680 } 1681 } 1682 } 1683 1684 private static void appendNormalisedText(StringBuilder accum, TextNode textNode) { 1685 String text = textNode.getWholeText(); 1686 if (preserveWhitespace(textNode.parentNode) || textNode instanceof CDataNode) 1687 accum.append(text); 1688 else 1689 StringUtil.appendNormalisedWhitespace(accum, text, lastCharIsWhitespace(accum)); 1690 } 1691 1692 static boolean preserveWhitespace(@Nullable Node node) { 1693 // looks only at this element and five levels up, to prevent recursion & needless stack searches 1694 if (node instanceof Element) { 1695 Element el = (Element) node; 1696 int i = 0; 1697 do { 1698 if (el.tag.preserveWhitespace()) 1699 return true; 1700 el = el.parent(); 1701 i++; 1702 } while (i < 6 && el != null); 1703 } 1704 return false; 1705 } 1706 1707 /** 1708 * Set the text of this element. Any existing contents (text or elements) will be cleared. 1709 * <p>As a special case, for {@code <script>} and {@code <style>} tags, the input text will be treated as data, 1710 * not visible text.</p> 1711 * @param text decoded text 1712 * @return this element 1713 */ 1714 public Element text(String text) { 1715 Validate.notNull(text); 1716 empty(); 1717 // special case for script/style in HTML (or customs): should be data node 1718 if (tag().is(Tag.Data)) 1719 appendChild(new DataNode(text)); 1720 else 1721 appendChild(new TextNode(text)); 1722 1723 return this; 1724 } 1725 1726 /** 1727 Checks if the current element or any of its child elements contain non-whitespace text. 1728 @return {@code true} if the element has non-blank text content, {@code false} otherwise. 1729 */ 1730 public boolean hasText() { 1731 AtomicBoolean hasText = new AtomicBoolean(false); 1732 filter((node, depth) -> { 1733 if (node instanceof TextNode) { 1734 TextNode textNode = (TextNode) node; 1735 if (!textNode.isBlank()) { 1736 hasText.set(true); 1737 return NodeFilter.FilterResult.STOP; 1738 } 1739 } 1740 return NodeFilter.FilterResult.CONTINUE; 1741 }); 1742 return hasText.get(); 1743 } 1744 1745 /** 1746 Get the combined data of this element. Data is e.g. the inside of a {@code <script>} tag. Note that data is NOT the 1747 plain text of the element. Use {@link #text()} to get normalized, readable text for extraction, indexing, or 1748 accessibility-oriented processing, and {@code data()} for the contents of scripts, comments, CSS styles, etc. 1749 1750 @return the data, or empty string if none 1751 @see #dataNodes() 1752 */ 1753 public String data() { 1754 StringBuilder sb = StringUtil.borrowBuilder(); 1755 traverse((childNode, depth) -> { 1756 if (childNode instanceof DataNode) { 1757 DataNode data = (DataNode) childNode; 1758 sb.append(data.getWholeData()); 1759 } else if (childNode instanceof Comment) { 1760 Comment comment = (Comment) childNode; 1761 sb.append(comment.getData()); 1762 } else if (childNode instanceof CDataNode) { 1763 // this shouldn't really happen because the html parser won't see the cdata as anything special when parsing script. 1764 // but in case another type gets through. 1765 CDataNode cDataNode = (CDataNode) childNode; 1766 sb.append(cDataNode.getWholeText()); 1767 } 1768 }); 1769 return StringUtil.releaseBuilder(sb); 1770 } 1771 1772 /** 1773 * Gets the literal value of this element's "class" attribute, which may include multiple class names, space 1774 * separated. (E.g. on <code><div class="header gray"></code> returns, "<code>header gray</code>") 1775 * @return The literal class attribute, or <b>empty string</b> if no class attribute set. 1776 */ 1777 public String className() { 1778 return attr("class").trim(); 1779 } 1780 1781 /** 1782 Get each of the element's class names. E.g. on element {@code <div class="header gray">}, 1783 returns a set of two elements {@code "header", "gray"}. 1784 <p>Note that modifications to this set are not pushed to the backing {@code class} attribute; use 1785 {@link #classNames(Set)} to persist them.</p> 1786 <p>Use {@link #classList()} for a more efficient, read-only list that preserves duplicate class names.</p> 1787 1788 @return set of class names, empty if no class attribute 1789 @see #classNames(Set) 1790 @see #hasClass(String) 1791 @see #classList() 1792 */ 1793 public Set<String> classNames() { 1794 Set<String> classNames = new LinkedHashSet<>(4); 1795 if (attributes == null) return classNames; 1796 1797 String classAttr = attributes.getIgnoreCase("class"); 1798 int len = classAttr.length(); 1799 for (int i = 0; i < len; ) { 1800 int start = nextClassStart(classAttr, i, len); 1801 if (start == len) break; 1802 1803 int end = nextClassEnd(classAttr, start, len); 1804 classNames.add(classToken(classAttr, start, end)); 1805 i = end; 1806 } 1807 return classNames; 1808 } 1809 1810 /** 1811 Get each of the element's class names, in attribute order. E.g. on element 1812 {@code <div class="header gray">}, returns a list of two elements {@code "header", "gray"}. 1813 <p>This immutable snapshot preserves duplicate class names, and is more memory efficient than 1814 {@link #classNames()} when a read-only result is sufficient, particularly for elements without class names. 1815 Use {@link #classNames()} for a mutable set of unique class names.</p> 1816 1817 @return immutable list of class names, empty if no class attribute 1818 @see #classNames() 1819 @see #hasClass(String) 1820 @since 1.23.1 1821 */ 1822 public List<String> classList() { 1823 if (attributes == null) return Collections.emptyList(); 1824 1825 String attr = attributes.getIgnoreCase("class"); 1826 int len = attr.length(); 1827 int start = nextClassStart(attr, 0, len); 1828 if (start == len) return Collections.emptyList(); 1829 1830 int end = nextClassEnd(attr, start, len); 1831 String first = classToken(attr, start, end); 1832 start = nextClassStart(attr, end, len); 1833 if (start == len) return Collections.singletonList(first); 1834 1835 List<String> classes = new ArrayList<>(4); 1836 classes.add(first); 1837 do { 1838 end = nextClassEnd(attr, start, len); 1839 classes.add(classToken(attr, start, end)); 1840 start = nextClassStart(attr, end, len); 1841 } while (start < len); 1842 return Collections.unmodifiableList(classes); 1843 } 1844 1845 /** 1846 Find the next class token start. 1847 */ 1848 private static int nextClassStart(String classAttr, int offset, int len) { 1849 while (offset < len && StringUtil.isWhitespace(classAttr.charAt(offset))) offset++; 1850 return offset; 1851 } 1852 1853 /** 1854 Find the next class token end. 1855 */ 1856 private static int nextClassEnd(String classAttr, int offset, int len) { 1857 while (offset < len && !StringUtil.isWhitespace(classAttr.charAt(offset))) offset++; 1858 return offset; 1859 } 1860 1861 /** 1862 Returns the class token while preserving the original string for a single unpadded class. 1863 */ 1864 private static String classToken(String classAttr, int start, int end) { 1865 return start == 0 && end == classAttr.length() ? classAttr : classAttr.substring(start, end); 1866 } 1867 1868 /** 1869 Set the element's {@code class} attribute to the supplied class names. 1870 @param classNames set of classes 1871 @return this element, for chaining 1872 */ 1873 public Element classNames(Set<String> classNames) { 1874 Validate.notNull(classNames); 1875 if (classNames.isEmpty()) { 1876 attributes().remove("class"); 1877 } else { 1878 attributes().put("class", StringUtil.join(classNames, " ")); 1879 } 1880 return this; 1881 } 1882 1883 /** 1884 * Tests if this element has a class. Case-insensitive. 1885 * @param className name of class to check for 1886 * @return true if it does, false if not 1887 */ 1888 // performance sensitive 1889 public boolean hasClass(String className) { 1890 if (attributes == null) return false; 1891 1892 final String classAttr = attributes.getIgnoreCase("class"); 1893 final int len = classAttr.length(); 1894 final int wantLen = className.length(); 1895 1896 if (len == 0 || len < wantLen) return false; 1897 1898 // if both lengths are equal, only need to compare the className with the attribute 1899 if (len == wantLen) return className.equalsIgnoreCase(classAttr); 1900 1901 // otherwise, scan for whitespace and compare regions (with no string or list allocations) 1902 for (int i = 0; i < len; ) { 1903 int start = nextClassStart(classAttr, i, len); 1904 if (start == len) return false; 1905 1906 int end = nextClassEnd(classAttr, start, len); 1907 if (end - start == wantLen && classAttr.regionMatches(true, start, className, 0, wantLen)) return true; 1908 i = end; 1909 } 1910 1911 return false; 1912 } 1913 1914 /** 1915 Add a class name to this element's {@code class} attribute. 1916 @param className class name to add 1917 @return this element 1918 */ 1919 public Element addClass(String className) { 1920 Validate.notNull(className); 1921 1922 Set<String> classes = classNames(); 1923 classes.add(className); 1924 classNames(classes); 1925 1926 return this; 1927 } 1928 1929 /** 1930 Remove a class name from this element's {@code class} attribute. 1931 @param className class name to remove 1932 @return this element 1933 */ 1934 public Element removeClass(String className) { 1935 Validate.notNull(className); 1936 1937 Set<String> classes = classNames(); 1938 classes.remove(className); 1939 classNames(classes); 1940 1941 return this; 1942 } 1943 1944 /** 1945 Toggle a class name on this element's {@code class} attribute: if present, remove it; otherwise add it. 1946 @param className class name to toggle 1947 @return this element 1948 */ 1949 public Element toggleClass(String className) { 1950 Validate.notNull(className); 1951 1952 Set<String> classes = classNames(); 1953 if (classes.contains(className)) 1954 classes.remove(className); 1955 else 1956 classes.add(className); 1957 classNames(classes); 1958 1959 return this; 1960 } 1961 1962 /** 1963 * Get the value of a form element (input, textarea, etc). 1964 * @return the value of the form element, or empty string if not set. 1965 */ 1966 public String val() { 1967 if (elementIs("textarea", NamespaceHtml)) 1968 return text(); 1969 else 1970 return attr("value"); 1971 } 1972 1973 /** 1974 * Set the value of a form element (input, textarea, etc). 1975 * @param value value to set 1976 * @return this element (for chaining) 1977 */ 1978 public Element val(String value) { 1979 if (elementIs("textarea", NamespaceHtml)) 1980 text(value); 1981 else 1982 attr("value", value); 1983 return this; 1984 } 1985 1986 /** 1987 Get the source range (start and end positions) of the end (closing) tag for this Element. Position tracking must be 1988 enabled before parsing the content. 1989 @return the range of the closing tag for this element, or {@code untracked} if its range was not tracked. 1990 @see org.jsoup.parser.Parser#setTrackPosition(boolean) 1991 @see Node#sourceRange() 1992 @see Range#isImplicit() 1993 @since 1.15.2 1994 */ 1995 public Range endSourceRange() { 1996 return Range.ofEnd(this); 1997 } 1998 1999 @Override 2000 void outerHtmlHead(final QuietAppendable accum, Document.OutputSettings out) { 2001 String tagName = safeTagName(out.syntax()); 2002 accum.append('<').append(tagName); 2003 if (attributes != null) attributes.html(accum, out); 2004 2005 if (childNodes.isEmpty()) { 2006 boolean xmlMode = out.syntax() == xml || !tag.namespace().equals(NamespaceHtml); 2007 if (xmlMode && (tag.is(Tag.SeenSelfClose) || (tag.isKnownTag() && (tag.isEmpty() || tag.isSelfClosing())))) { 2008 accum.append(" />"); 2009 } else if (!xmlMode && tag.isEmpty()) { // html void element 2010 accum.append('>'); 2011 } else { 2012 accum.append("></").append(tagName).append('>'); 2013 } 2014 } else { 2015 accum.append('>'); 2016 } 2017 } 2018 2019 @Override 2020 void outerHtmlTail(QuietAppendable accum, Document.OutputSettings out) { 2021 if (!childNodes.isEmpty()) 2022 accum.append("</").append(safeTagName(out.syntax())).append('>'); 2023 // if empty, we have already closed in htmlHead 2024 } 2025 2026 /* If XML syntax, normalizes < to _ in tag name. */ 2027 @Nullable private String safeTagName(Document.OutputSettings.Syntax syntax) { 2028 return syntax == xml ? Normalizer.xmlSafeTagName(tagName()) : tagName(); 2029 } 2030 2031 /** 2032 Get the inner HTML of this element. For example, on a {@code <div>} with one empty {@code <p>}, this returns 2033 {@code <p></p>}, whereas {@link #outerHtml()} returns {@code <div><p></p></div>}. 2034 2035 @return the inner HTML of this element 2036 @see #html(Appendable) 2037 @see #outerHtml() 2038 */ 2039 public String html() { 2040 StringBuilder sb = StringUtil.borrowBuilder(); 2041 html(sb); 2042 String html = StringUtil.releaseBuilder(sb); 2043 return NodeUtils.outputSettings(this).prettyPrint() ? html.trim() : html; 2044 } 2045 2046 /** 2047 Append the inner HTML of this element to the supplied {@link Appendable}. 2048 2049 @param appendable the {@link Appendable} that will receive the HTML 2050 @return the supplied {@link Appendable}, for chaining 2051 @throws org.jsoup.SerializationException if the appendable throws an IOException 2052 @see #html() 2053 @see #outerHtml(Appendable) 2054 @see #outerHtml() 2055 */ 2056 @Override 2057 public <T extends Appendable> T html(T appendable) { 2058 html(QuietAppendable.wrap(appendable)); 2059 return appendable; 2060 } 2061 2062 /** Append the inner HTML of this element to the supplied {@link QuietAppendable}. */ 2063 void html(QuietAppendable accum) { 2064 Node child = firstChild(); 2065 if (child != null) { 2066 Printer printer = Printer.printerFor(child, accum); 2067 while (child != null) { 2068 printer.traverse(child); 2069 child = child.nextSibling(); 2070 } 2071 } 2072 } 2073 2074 /** 2075 * Set this element's inner HTML. Clears the existing HTML first. 2076 * @param html HTML to parse and set into this element 2077 * @return this element 2078 * @see #append(String) 2079 */ 2080 public Element html(String html) { 2081 empty(); 2082 append(html); 2083 return this; 2084 } 2085 2086 @Override 2087 public Element clone() { 2088 return (Element) super.clone(); 2089 } 2090 2091 @Override 2092 public Element shallowClone() { 2093 // simpler than implementing a clone version with no child copy 2094 String baseUri = baseUri(); 2095 if (baseUri.isEmpty()) baseUri = null; // saves setting a blank internal attribute 2096 return new Element(tag, baseUri, attributes == null ? null : attributes.clone()); 2097 } 2098 2099 @Override 2100 protected Element doClone(@Nullable Node parent) { 2101 Element clone = (Element) super.doClone(parent); 2102 clone.childNodes = new NodeList(childNodes.size()); 2103 clone.childNodes.addAll(childNodes); // the children then get iterated and cloned in Node.clone 2104 if (attributes != null) { 2105 clone.attributes = attributes.clone(); 2106 // clear any cached children 2107 clone.attributes.userData(childElsKey, null); 2108 } 2109 2110 return clone; 2111 } 2112 2113 // overrides of Node for call chaining 2114 @Override 2115 public Element clearAttributes() { 2116 if (attributes != null) { 2117 super.clearAttributes(); // keeps internal attributes via iterator 2118 if (attributes.size == 0) 2119 attributes = null; // only remove entirely if no internal attributes 2120 } 2121 2122 return this; 2123 } 2124 2125 @Override 2126 public Element removeAttr(String attributeKey) { 2127 return (Element) super.removeAttr(attributeKey); 2128 } 2129 2130 @Override 2131 public Element root() { 2132 return (Element) super.root(); // probably a document, but always at least an element 2133 } 2134 2135 @Override 2136 public Element traverse(NodeVisitor nodeVisitor) { 2137 return (Element) super.traverse(nodeVisitor); 2138 } 2139 2140 @Override 2141 public Element forEachNode(Consumer<? super Node> action) { 2142 return (Element) super.forEachNode(action); 2143 } 2144 2145 /** 2146 Perform the supplied action on this Element and each of its descendant Elements, during a depth-first traversal. 2147 Elements may be inspected, changed, added, replaced, or removed. 2148 @param action the function to perform on the element 2149 @see Node#forEachNode(Consumer) 2150 */ 2151 @Override 2152 public void forEach(Consumer<? super Element> action) { 2153 stream().forEach(action); 2154 } 2155 2156 /** 2157 Returns an Iterator that iterates this Element and each of its descendant Elements, in document order. 2158 @return an Iterator 2159 */ 2160 @Override 2161 public Iterator<Element> iterator() { 2162 return new NodeIterator<>(this, Element.class); 2163 } 2164 2165 @Override 2166 public Element filter(NodeFilter nodeFilter) { 2167 return (Element) super.filter(nodeFilter); 2168 } 2169 2170 static final class NodeList extends ArrayList<Node> { 2171 /** Tracks if the children have valid sibling indices. We only need to reindex on siblingIndex() demand. */ 2172 boolean validChildren = true; 2173 2174 public NodeList(int size) { 2175 super(size); 2176 } 2177 2178 /** The modCount is used to invalidate the cached element children. */ 2179 int modCount() { 2180 return this.modCount; 2181 } 2182 2183 void incrementMod() { 2184 this.modCount++; 2185 } 2186 } 2187 2188 void reindexChildren() { 2189 final int size = childNodes.size(); 2190 for (int i = 0; i < size; i++) { 2191 childNodes.get(i).setSiblingIndex(i); 2192 } 2193 childNodes.validChildren = true; 2194 } 2195 2196 void invalidateChildren() { 2197 childNodes.validChildren = false; 2198 } 2199 2200 boolean hasValidChildren() { 2201 return childNodes.validChildren; 2202 } 2203}