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