WP_HTML_Tag_Processor::set_bookmark( string $name ): bool

Sets a bookmark in the HTML document.


Bookmarks represent specific places or tokens in the HTML document, such as a tag opener or closer. When applying edits to a document, such as setting an attribute, the text offsets of that token may shift; the bookmark is kept updated with those shifts and remains stable unless the entire span of text in which the token sits is removed.

Release bookmarks when they are no longer needed.


<main><h2>Surprising fact you may not know!</h2></main>
      ^  ^
       \-|-- this `H2` opener bookmark tracks the token

<main class="clickbait"><h2>Surprising fact you may no…
                        ^  ^
                         \-|-- it shifts with edits

Bookmarks provide the ability to seek to a previously-scanned place in the HTML document. This avoids the need to re-scan the entire document.


                            want to note this last item

$p = new WP_HTML_Tag_Processor( $html );
$in_list = false;
while ( $p->next_tag( array( 'tag_closers' => $in_list ? 'visit' : 'skip' ) ) ) {
    if ( 'UL' === $p->get_tag() ) {
        if ( $p->is_tag_closer() ) {
            $in_list = false;
            $p->set_bookmark( 'resume' );
            if ( $p->seek( 'last-li' ) ) {
                $p->add_class( 'last-li' );
            $p->seek( 'resume' );
            $p->release_bookmark( 'last-li' );
            $p->release_bookmark( 'resume' );
        } else {
            $in_list = true;

    if ( 'LI' === $p->get_tag() ) {
        $p->set_bookmark( 'last-li' );

Bookmarks intentionally hide the internal string offsets to which they refer. They are maintained internally as updates are applied to the HTML document and therefore retain their "position" – the location to which they originally pointed. The inability to use bookmarks with functions like substr is therefore intentional to guard against accidentally breaking the HTML.

Because bookmarks allocate memory and require processing for every applied update, they are limited and require a name. They should not be created with programmatically-made names, such as "li_{$index}" with some loop. As a general rule they should only be created with string-literal names like "start-of-section" or "last-paragraph".

Bookmarks are a powerful tool to enable complicated behavior.
Consider double-checking that you need this tool if you are reaching for it, as inappropriate use could lead to broken HTML structure or unwanted processing overhead.


Identifies this particular bookmark.


bool Whether the bookmark was successfully created.


public function set_bookmark( $name ) {
	// It only makes sense to set a bookmark if the parser has paused on a concrete token.
	if (
		self::STATE_COMPLETE === $this->parser_state ||
		self::STATE_INCOMPLETE_INPUT === $this->parser_state
	) {
		return false;

	if ( ! array_key_exists( $name, $this->bookmarks ) && count( $this->bookmarks ) >= static::MAX_BOOKMARKS ) {
			__( 'Too many bookmarks: cannot create any more.' ),
		return false;

	$this->bookmarks[ $name ] = new WP_HTML_Span( $this->token_starts_at, $this->token_length );

	return true;



User Contributed Notes

You must log in before being able to contribute a note or feedback.