001package org.jsoup.select;
002
003import org.jsoup.helper.Validate;
004import org.jsoup.internal.StringUtil;
005import org.jsoup.nodes.Comment;
006import org.jsoup.nodes.DataNode;
007import org.jsoup.nodes.Element;
008import org.jsoup.nodes.FormElement;
009import org.jsoup.nodes.Node;
010import org.jsoup.nodes.TextNode;
011import org.jspecify.annotations.Nullable;
012
013import java.util.ArrayList;
014import java.util.Arrays;
015import java.util.Collection;
016import java.util.HashSet;
017import java.util.Iterator;
018import java.util.LinkedHashSet;
019import java.util.List;
020import java.util.function.Predicate;
021import java.util.function.UnaryOperator;
022
023/**
024 A list of {@link Element}s, with methods that act on every element in the list.
025 <p>To get an {@code Elements} object, use the {@link Element#select(String)} method.</p>
026 <p>Methods that {@link #set(int, Element) set}, {@link #remove(int) remove}, or {@link #replaceAll(UnaryOperator)
027 replace} Elements in the list will also act on the underlying {@link org.jsoup.nodes.Document DOM}.</p>
028
029 @author Jonathan Hedley, jonathan@hedley.net */
030public class Elements extends ArrayList<Element> {
031    public Elements() {
032    }
033
034    public Elements(int initialCapacity) {
035        super(initialCapacity);
036    }
037
038    public Elements(Collection<Element> elements) {
039        super(elements);
040    }
041    
042    public Elements(List<Element> elements) {
043        super(elements);
044    }
045    
046    public Elements(Element... elements) {
047        super(Arrays.asList(elements));
048    }
049
050    /**
051     * Creates a deep copy of these elements.
052     * @return a deep copy
053     */
054    @Override
055    public Elements clone() {
056        Elements clone = new Elements(size());
057        for (Element e : this)
058            clone.add(e.clone());
059        return clone;
060    }
061
062    /**
063     Convenience method to get the Elements as a plain ArrayList. This allows modification to the list of elements
064     without modifying the source Document. I.e. whereas calling {@code elements.remove(0)} will remove the element from
065     both the Elements and the DOM, {@code elements.asList().remove(0)} will remove the element from the list only.
066     <p>Each Element is still the same DOM connected Element.</p>
067
068     @return a new ArrayList containing the elements in this list
069     @since 1.19.2
070     @see #Elements(List)
071     */
072    public ArrayList<Element> asList() {
073        return new ArrayList<>(this);
074    }
075
076    // attribute methods
077    /**
078     Get an attribute value from the first matched element that has the attribute.
079     @param attributeKey The attribute key.
080     @return The attribute value from the first matched element that has the attribute. If no elements were matched (isEmpty() == true),
081     or if the no elements have the attribute, returns empty string.
082     @see #hasAttr(String)
083     */
084    public String attr(String attributeKey) {
085        for (Element element : this) {
086            if (element.hasAttr(attributeKey))
087                return element.attr(attributeKey);
088        }
089        return "";
090    }
091
092    /**
093     Checks if any of the matched elements have this attribute defined.
094     @param attributeKey attribute key
095     @return true if any of the elements have the attribute; false if none do.
096     */
097    public boolean hasAttr(String attributeKey) {
098        for (Element element : this) {
099            if (element.hasAttr(attributeKey))
100                return true;
101        }
102        return false;
103    }
104
105    /**
106     * Get the attribute value for each of the matched elements. If an element does not have this attribute, no value is
107     * included in the result set for that element.
108     * @param attributeKey the attribute name to return values for. You can add the {@code abs:} prefix to the key to
109     * get absolute URLs from relative URLs, e.g.: {@code doc.select("a").eachAttr("abs:href")} .
110     * @return a list of each element's attribute value for the attribute
111     */
112    public List<String> eachAttr(String attributeKey) {
113        List<String> attrs = new ArrayList<>(size());
114        for (Element element : this) {
115            if (element.hasAttr(attributeKey))
116                attrs.add(element.attr(attributeKey));
117        }
118        return attrs;
119    }
120
121    /**
122     * Set an attribute on all matched elements.
123     * @param attributeKey attribute key
124     * @param attributeValue attribute value
125     * @return this
126     */
127    public Elements attr(String attributeKey, String attributeValue) {
128        for (Element element : this) {
129            element.attr(attributeKey, attributeValue);
130        }
131        return this;
132    }
133
134    /**
135     * Remove an attribute from every matched element.
136     * @param attributeKey The attribute to remove.
137     * @return this (for chaining)
138     */
139    public Elements removeAttr(String attributeKey) {
140        for (Element element : this) {
141            element.removeAttr(attributeKey);
142        }
143        return this;
144    }
145
146    /**
147     Add the class name to every matched element's {@code class} attribute.
148     @param className class name to add
149     @return this
150     */
151    public Elements addClass(String className) {
152        for (Element element : this) {
153            element.addClass(className);
154        }
155        return this;
156    }
157
158    /**
159     Remove the class name from every matched element's {@code class} attribute, if present.
160     @param className class name to remove
161     @return this
162     */
163    public Elements removeClass(String className) {
164        for (Element element : this) {
165            element.removeClass(className);
166        }
167        return this;
168    }
169
170    /**
171     Toggle the class name on every matched element's {@code class} attribute.
172     @param className class name to add if missing, or remove if present, from every element.
173     @return this
174     */
175    public Elements toggleClass(String className) {
176        for (Element element : this) {
177            element.toggleClass(className);
178        }
179        return this;
180    }
181
182    /**
183     Determine if any of the matched elements have this class name set in their {@code class} attribute.
184     @param className class name to check for
185     @return true if any do, false if none do
186     */
187    public boolean hasClass(String className) {
188        for (Element element : this) {
189            if (element.hasClass(className))
190                return true;
191        }
192        return false;
193    }
194    
195    /**
196     * Get the form element's value of the first matched element.
197     * @return The form element's value, or empty if not set.
198     * @see Element#val()
199     */
200    public String val() {
201        if (size() > 0)
202            //noinspection ConstantConditions
203            return first().val(); // first() != null as size() > 0
204        else
205            return "";
206    }
207    
208    /**
209     * Set the form element's value in each of the matched elements.
210     * @param value The value to set into each matched element
211     * @return this (for chaining)
212     */
213    public Elements val(String value) {
214        for (Element element : this)
215            element.val(value);
216        return this;
217    }
218    
219    /**
220     * Get the combined text of all the matched elements.
221     * <p>
222     * Note that it is possible to get repeats if the matched elements contain both parent elements and their own
223     * children, as the Element.text() method returns the combined text of a parent and all its children.
224     * @return string of all text: unescaped and no HTML.
225     * @see Element#text()
226     * @see #eachText()
227     */
228    public String text() {
229        return stream()
230            .map(Element::text)
231            .collect(StringUtil.joining(" "));
232    }
233
234    /**
235     Test if any matched Element has any text content, that is not just whitespace.
236     @return true if any element has non-blank text content.
237     @see Element#hasText()
238     */
239    public boolean hasText() {
240        for (Element element: this) {
241            if (element.hasText())
242                return true;
243        }
244        return false;
245    }
246
247    /**
248     * Get the text content of each of the matched elements. If an element has no text, then it is not included in the
249     * result.
250     * @return A list of each matched element's text content.
251     * @see Element#text()
252     * @see Element#hasText()
253     * @see #text()
254     */
255    public List<String> eachText() {
256        ArrayList<String> texts = new ArrayList<>(size());
257        for (Element el: this) {
258            if (el.hasText())
259                texts.add(el.text());
260        }
261        return texts;
262    }
263    
264    /**
265     * Get the combined inner HTML of all matched elements.
266     * @return string of all element's inner HTML.
267     * @see #text()
268     * @see #outerHtml()
269     */
270    public String html() {
271        return stream()
272            .map(Element::html)
273            .collect(StringUtil.joining("\n"));
274    }
275    
276    /**
277     * Get the combined outer HTML of all matched elements.
278     * @return string of all element's outer HTML.
279     * @see #text()
280     * @see #html()
281     */
282    public String outerHtml() {
283        return stream()
284            .map(Element::outerHtml)
285            .collect(StringUtil.joining("\n"));
286    }
287
288    /**
289     * Get the combined outer HTML of all matched elements. Alias of {@link #outerHtml()}.
290     * @return string of all element's outer HTML.
291     * @see #text()
292     * @see #html()
293     */
294    @Override
295    public String toString() {
296        return outerHtml();
297    }
298
299    /**
300     * Update (rename) the tag name of each matched element. For example, to change each {@code <i>} to a {@code <em>}, do
301     * {@code doc.select("i").tagName("em");}
302     *
303     * @param tagName the new tag name
304     * @return this, for chaining
305     * @see Element#tagName(String)
306     */
307    public Elements tagName(String tagName) {
308        for (Element element : this) {
309            element.tagName(tagName);
310        }
311        return this;
312    }
313    
314    /**
315     * Set the inner HTML of each matched element.
316     * @param html HTML to parse and set into each matched element.
317     * @return this, for chaining
318     * @see Element#html(String)
319     */
320    public Elements html(String html) {
321        for (Element element : this) {
322            element.html(html);
323        }
324        return this;
325    }
326    
327    /**
328     * Add the supplied HTML to the start of each matched element's inner HTML.
329     * @param html HTML to add inside each element, before the existing HTML
330     * @return this, for chaining
331     * @see Element#prepend(String)
332     */
333    public Elements prepend(String html) {
334        for (Element element : this) {
335            element.prepend(html);
336        }
337        return this;
338    }
339    
340    /**
341     * Add the supplied HTML to the end of each matched element's inner HTML.
342     * @param html HTML to add inside each element, after the existing HTML
343     * @return this, for chaining
344     * @see Element#append(String)
345     */
346    public Elements append(String html) {
347        for (Element element : this) {
348            element.append(html);
349        }
350        return this;
351    }
352    
353    /**
354     * Insert the supplied HTML before each matched element's outer HTML.
355     * @param html HTML to insert before each element
356     * @return this, for chaining
357     * @see Element#before(String)
358     */
359    public Elements before(String html) {
360        for (Element element : this) {
361            element.before(html);
362        }
363        return this;
364    }
365    
366    /**
367     * Insert the supplied HTML after each matched element's outer HTML.
368     * @param html HTML to insert after each element
369     * @return this, for chaining
370     * @see Element#after(String)
371     */
372    public Elements after(String html) {
373        for (Element element : this) {
374            element.after(html);
375        }
376        return this;
377    }
378
379    /**
380     Wrap the supplied HTML around each matched elements. For example, with HTML
381     {@code <p><b>This</b> is <b>Jsoup</b></p>},
382     <code>doc.select("b").wrap("&lt;i&gt;&lt;/i&gt;");</code>
383     becomes {@code <p><i><b>This</b></i> is <i><b>jsoup</b></i></p>}
384     @param html HTML to wrap around each element, e.g. {@code <div class="head"></div>}. Can be arbitrarily deep.
385     @return this (for chaining)
386     @see Element#wrap
387     */
388    public Elements wrap(String html) {
389        Validate.notEmpty(html);
390        for (Element element : this) {
391            element.wrap(html);
392        }
393        return this;
394    }
395
396    /**
397     * Removes the matched elements from the DOM, and moves their children up into their parents. This has the effect of
398     * dropping the elements but keeping their children.
399     * <p>
400     * This is useful for e.g removing unwanted formatting elements but keeping their contents.
401     * </p>
402     * 
403     * E.g. with HTML: <p>{@code <div><font>One</font> <font><a href="/">Two</a></font></div>}</p>
404     * <p>{@code doc.select("font").unwrap();}</p>
405     * <p>HTML = {@code <div>One <a href="/">Two</a></div>}</p>
406     *
407     * @return this (for chaining)
408     * @see Node#unwrap
409     */
410    public Elements unwrap() {
411        for (Element element : this) {
412            element.unwrap();
413        }
414        return this;
415    }
416
417    /**
418     * Empty (remove all child nodes from) each matched element. This is similar to setting the inner HTML of each
419     * element to nothing.
420     * <p>
421     * E.g. HTML: {@code <div><p>Hello <b>there</b></p> <p>now</p></div>}<br>
422     * <code>doc.select("p").empty();</code><br>
423     * HTML = {@code <div><p></p> <p></p></div>}
424     * @return this, for chaining
425     * @see Element#empty()
426     * @see #remove()
427     */
428    public Elements empty() {
429        for (Element element : this) {
430            element.empty();
431        }
432        return this;
433    }
434
435    /**
436     * Remove each matched element from the DOM. This is similar to setting the outer HTML of each element to nothing.
437     * <p>The elements will still be retained in this list, in case further processing of them is desired.</p>
438     * <p>
439     * E.g. HTML: {@code <div><p>Hello</p> <p>there</p> <img /></div>}<br>
440     * <code>doc.select("p").remove();</code><br>
441     * HTML = {@code <div> <img /></div>}
442     * <p>
443     * Note that this method should not be used to clean user-submitted HTML; rather, use {@link org.jsoup.safety.Cleaner} to clean HTML.
444     * @return this, for chaining
445     * @see Element#empty()
446     * @see #empty()
447     * @see #clear()
448     */
449    public Elements remove() {
450        for (Element element : this) {
451            element.remove();
452        }
453        return this;
454    }
455    
456    // filters
457    
458    /**
459     * Find matching elements within this element list.
460     * @param query A {@link Selector} query
461     * @return the filtered list of elements, or an empty list if none match.
462     */
463    public Elements select(String query) {
464        return Selector.select(query, this);
465    }
466
467    /**
468     Find the first Element that matches the {@link Selector} CSS query within this element list.
469     <p>This is effectively the same as calling {@code elements.select(query).first()}, but is more efficient as query
470     execution stops on the first hit.</p>
471
472     @param cssQuery a {@link Selector} query
473     @return the first matching element, or <b>{@code null}</b> if there is no match.
474     @see #expectFirst(String)
475     @since 1.19.1
476     */
477    public @Nullable Element selectFirst(String cssQuery) {
478        return Selector.selectFirst(cssQuery, this);
479    }
480
481    /**
482     Just like {@link #selectFirst(String)}, but if there is no match, throws an {@link IllegalArgumentException}.
483
484     @param cssQuery a {@link Selector} query
485     @return the first matching element
486     @throws IllegalArgumentException if no match is found
487     @since 1.19.1
488     */
489    public Element expectFirst(String cssQuery) {
490        return (Element) Validate.ensureNotNull(
491            Selector.selectFirst(cssQuery, this),
492            "No elements matched the query '%s' in the elements.", cssQuery
493        );
494    }
495
496    /**
497     * Remove elements from this list that match the {@link Selector} query.
498     * <p>
499     * E.g. HTML: {@code <div class=logo>One</div> <div>Two</div>}<br>
500     * <code>Elements divs = doc.select("div").not(".logo");</code><br>
501     * Result: {@code divs: [<div>Two</div>]}
502     * <p>
503     * @param query the selector query whose results should be removed from these elements
504     * @return a new elements list that contains only the filtered results
505     */
506    public Elements not(String query) {
507        Elements out = Selector.select(query, this);
508        return Selector.filterOut(this, out);
509    }
510    
511    /**
512     * Get the <i>nth</i> matched element as an Elements object.
513     * <p>
514     * See also {@link #get(int)} to retrieve an Element.
515     * @param index the (zero-based) index of the element in the list to retain
516     * @return Elements containing only the specified element, or, if that element did not exist, an empty list.
517     */
518    public Elements eq(int index) {
519        return size() > index ? new Elements(get(index)) : new Elements();
520    }
521    
522    /**
523     * Test if any of the matched elements match the supplied query.
524     * @param query A selector
525     * @return true if at least one element in the list matches the query.
526     */
527    public boolean is(String query) {
528        Evaluator eval = QueryParser.parse(query);
529        for (Element e : this) {
530            if (e.is(eval))
531                return true;
532        }
533        return false;
534    }
535
536    /**
537     * Get the immediate next element sibling of each element in this list.
538     * @return next element siblings.
539     */
540    public Elements next() {
541        return siblings(null, true, false);
542    }
543
544    /**
545     * Get the immediate next element sibling of each element in this list, filtered by the query.
546     * @param query CSS query to match siblings against
547     * @return next element siblings.
548     */
549    public Elements next(String query) {
550        return siblings(query, true, false);
551    }
552
553    /**
554     * Get each of the following element siblings of each element in this list.
555     * @return all following element siblings.
556     */
557    public Elements nextAll() {
558        return siblings(null, true, true);
559    }
560
561    /**
562     * Get each of the following element siblings of each element in this list, that match the query.
563     * @param query CSS query to match siblings against
564     * @return all following element siblings.
565     */
566    public Elements nextAll(String query) {
567        return siblings(query, true, true);
568    }
569
570    /**
571     * Get the immediate previous element sibling of each element in this list.
572     * @return previous element siblings.
573     */
574    public Elements prev() {
575        return siblings(null, false, false);
576    }
577
578    /**
579     * Get the immediate previous element sibling of each element in this list, filtered by the query.
580     * @param query CSS query to match siblings against
581     * @return previous element siblings.
582     */
583    public Elements prev(String query) {
584        return siblings(query, false, false);
585    }
586
587    /**
588     * Get each of the previous element siblings of each element in this list.
589     * @return all previous element siblings.
590     */
591    public Elements prevAll() {
592        return siblings(null, false, true);
593    }
594
595    /**
596     * Get each of the previous element siblings of each element in this list, that match the query.
597     * @param query CSS query to match siblings against
598     * @return all previous element siblings.
599     */
600    public Elements prevAll(String query) {
601        return siblings(query, false, true);
602    }
603
604    private Elements siblings(@Nullable String query, boolean next, boolean all) {
605        Elements els = new Elements();
606        Evaluator eval = query != null? QueryParser.parse(query) : null;
607        for (Element e : this) {
608            do {
609                Element sib = next ? e.nextElementSibling() : e.previousElementSibling();
610                if (sib == null) break;
611                if (eval == null || sib.is(eval)) els.add(sib);
612                e = sib;
613            } while (all);
614        }
615        return els;
616    }
617
618    /**
619     * Get all of the parents and ancestor elements of the matched elements.
620     * @return all of the parents and ancestor elements of the matched elements
621     */
622    public Elements parents() {
623        HashSet<Element> combo = new LinkedHashSet<>();
624        for (Element e: this) {
625            combo.addAll(e.parents());
626        }
627        return new Elements(combo);
628    }
629
630    // list-like methods
631    /**
632     Get the first matched element.
633     @return The first matched element, or <code>null</code> if contents is empty.
634     */
635    public @Nullable Element first() {
636        return isEmpty() ? null : get(0);
637    }
638
639    /**
640     Get the last matched element.
641     @return The last matched element, or <code>null</code> if contents is empty.
642     */
643    public @Nullable Element last() {
644        return isEmpty() ? null : get(size() - 1);
645    }
646
647    /**
648     * Perform a depth-first traversal on each of the selected elements.
649     * @param nodeVisitor the visitor callbacks to perform on each node
650     * @return this, for chaining
651     */
652    public Elements traverse(NodeVisitor nodeVisitor) {
653        NodeTraversor.traverse(nodeVisitor, this);
654        return this;
655    }
656
657    /**
658     * Perform a depth-first filtering on each of the selected elements.
659     * @param nodeFilter the filter callbacks to perform on each node
660     * @return this, for chaining
661     */
662    public Elements filter(NodeFilter nodeFilter) {
663        NodeTraversor.filter(nodeFilter, this);
664        return this;
665    }
666
667    /**
668     * Get the {@link FormElement} forms from the selected elements, if any.
669     * @return a list of {@link FormElement}s pulled from the matched elements. The list will be empty if the elements contain
670     * no forms.
671     */
672    public List<FormElement> forms() {
673        ArrayList<FormElement> forms = new ArrayList<>();
674        for (Element el: this)
675            if (el instanceof FormElement)
676                forms.add((FormElement) el);
677        return forms;
678    }
679
680    /**
681     * Get {@link Comment} nodes that are direct child nodes of the selected elements.
682     * @return Comment nodes, or an empty list if none.
683     */
684    public List<Comment> comments() {
685        return childNodesOfType(Comment.class);
686    }
687
688    /**
689     * Get {@link TextNode} nodes that are direct child nodes of the selected elements.
690     * @return TextNode nodes, or an empty list if none.
691     */
692    public List<TextNode> textNodes() {
693        return childNodesOfType(TextNode.class);
694    }
695
696    /**
697     * Get {@link DataNode} nodes that are direct child nodes of the selected elements. DataNode nodes contain the
698     * content of tags such as {@code script}, {@code style} etc and are distinct from {@link TextNode}s.
699     * @return Comment nodes, or an empty list if none.
700     */
701    public List<DataNode> dataNodes() {
702        return childNodesOfType(DataNode.class);
703    }
704
705    private <T extends Node> List<T> childNodesOfType(Class<T> tClass) {
706        ArrayList<T> nodes = new ArrayList<>();
707        for (Element el: this) {
708            for (int i = 0; i < el.childNodeSize(); i++) {
709                Node node = el.childNode(i);
710                if (tClass.isInstance(node))
711                    nodes.add(tClass.cast(node));
712            }
713        }
714        return nodes;
715    }
716
717    // list methods that update the DOM:
718
719    /**
720     Replace the Element at the specified index in this list, and in the DOM.
721     * @param index index of the element to replace
722     * @param element element to be stored at the specified position
723     * @return the old Element at this index
724     * @since 1.17.1
725     */
726    @Override public Element set(int index, Element element) {
727        Validate.notNull(element);
728        Element old = super.set(index, element);
729        old.replaceWith(element);
730        return old;
731    }
732
733    /**
734     Remove the Element at the specified index in this ist, and from the DOM.
735     @param index the index of the element to be removed
736     @return the old element at this index
737     @see #deselect(int)
738     @since 1.17.1
739     */
740    @Override public Element remove(int index) {
741        Element old = super.remove(index);
742        old.remove();
743        return old;
744    }
745
746    /**
747     Remove the specified Element from this list, and from the DOM.
748     @param o element to be removed from this list, if present
749     @return if this list contained the Element
750     @see #deselect(Object)
751     @since 1.17.1
752     */
753    @Override public boolean remove(Object o) {
754        int index = super.indexOf(o);
755        if (index == -1) {
756            return false;
757        } else {
758            remove(index);
759            return true;
760        }
761    }
762
763    /**
764     Remove the Element at the specified index in this list, but not from the DOM.
765     @param index the index of the element to be removed
766     @return the old element at this index
767     @see #remove(int) 
768     @since 1.19.2
769     */
770    public Element deselect(int index) {
771        return super.remove(index);
772    }
773
774    /**
775     Remove the specified Element from this list, but not from the DOM.
776     @param o element to be removed from this list, if present
777     @return if this list contained the Element
778     @see #remove(Object) 
779     @since 1.19.2
780     */
781    public boolean deselect(Object o) {
782        return super.remove(o);
783    }
784
785    /**
786     Removes all the elements from this list, and each of them from the DOM.
787     @since 1.17.1
788     @see #deselectAll()
789     */
790    @Override public void clear() {
791        remove();
792        super.clear();
793    }
794
795    /**
796     Like {@link #clear()}, removes all the elements from this list, but not from the DOM.
797     @see #clear()
798     @since 1.19.2
799     */
800    public void deselectAll() {
801        super.clear();
802    }
803
804    /**
805     Removes from this list, and from the DOM, each of the elements that are contained in the specified collection and
806     are in this list.
807     * @param c collection containing elements to be removed from this list
808     * @return {@code true} if elements were removed from this list
809     * @since 1.17.1
810     */
811    @Override public boolean removeAll(Collection<?> c) {
812        boolean anyRemoved = false;
813        for (Object o : c) {
814            anyRemoved |= this.remove(o);
815        }
816        return anyRemoved;
817    }
818
819    /**
820     Retain in this list, and in the DOM, only the elements that are in the specified collection and are in this list.
821     In other words, remove elements from this list and the DOM any item that is in this list but not in the specified
822     collection.
823     * @param c collection containing elements to be retained in this list
824     * @return {@code true} if elements were removed from this list
825     * @since 1.17.1
826     */
827    @Override public boolean retainAll(Collection<?> c) {
828        boolean anyRemoved = false;
829        for (Iterator<Element> it = this.iterator(); it.hasNext(); ) {
830            Element el = it.next();
831            if (!c.contains(el)) {
832                it.remove();
833                anyRemoved = true;
834            }
835        }
836        return anyRemoved;
837    }
838
839    /**
840     Remove from the list, and from the DOM, all elements in this list that mach the given filter.
841     * @param filter a predicate which returns {@code true} for elements to be removed
842     * @return {@code true} if elements were removed from this list
843     * @since 1.17.1
844     */
845    @Override public boolean removeIf(Predicate<? super Element> filter) {
846        boolean anyRemoved = false;
847        for (Iterator<Element> it = this.iterator(); it.hasNext(); ) {
848            Element el = it.next();
849            if (filter.test(el)) {
850                it.remove();
851                anyRemoved = true;
852            }
853        }
854        return anyRemoved;
855    }
856
857    /**
858     Replace each element in this list with the result of the operator, and update the DOM.
859     * @param operator the operator to apply to each element
860     * @since 1.17.1
861     */
862    @Override public void replaceAll(UnaryOperator<Element> operator) {
863        for (int i = 0; i < this.size(); i++) {
864            this.set(i, operator.apply(this.get(i)));
865        }
866    }
867}