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