﻿id	summary	reporter	owner	description	type	status	priority	milestone	component	version	severity	resolution	keywords	cc	focuses
66120	wp-exclude-emoji is ignored when the excluded element is the node being parsed	kimjiwoon		"#52219 made `wp-emoji.js` skip elements with the `wp-exclude-emoji` class ([55186]) through a `doNotParse()` callback added to `twemoji.js`. That callback is only consulted in `grabAllTextNodes()`, for the child elements of the node passed to `parse()`. The node passed to `parse()` itself is never checked.

The `MutationObserver` in `wp-emoji.js` passes exactly such a node: for an added text node it moves up to the parent and calls `parse( node )` on it. So an excluded element is parsed, and its emoji replaced, whenever it becomes that node:

* the excluded element itself is inserted after the page has loaded, or
* its own text is replaced, for example with `textContent`.

To reproduce, load a front-end page where the emoji fallback script is active (on current releases #66104 makes that every browser; otherwise one where a support test fails, such as Chromium on Windows for `flag`) and run in the console:

{{{
const span = document.createElement( 'span' );
span.className = 'wp-exclude-emoji';
span.textContent = '\uD83C\uDDF0\uD83C\uDDF7'; // Flag: South Korea
document.body.appendChild( span );
}}}

After the observer runs, the span contains an `img.emoji` whose `alt` is the original South Korea flag and whose `src` is `https://s.w.org/images/core/emoji/17.0.2/svg/1f1f0-1f1f7.svg`.

What I measured on a local page in Chromium on Windows with Core's `wp-emoji.js`, `twemoji.js` and `emoji-loader.js` (with the #66104 patch, so only `flag` failed), counting `img.emoji` after 1.5 seconds:

||= Case =||= Images =||
|| `span.wp-exclude-emoji` in the initial HTML || 0 ||
|| the same span appended after load || 1 ||
|| the initial span's `textContent` replaced || 1 ||
|| `div > p > span.wp-exclude-emoji` appended in one piece || 0 ||
|| the span appended inside a wrapper element || 0 ||
|| plain text appended (control) || 1 ||

Reading the source, an element or text added anywhere inside an excluded element should be replaced the same way, since its parent becomes the node passed to `parse()`; I have not tested that case.

A possible fix is to skip the node in the observer when it is inside an excluded element, for example `node.closest( '.wp-exclude-emoji' )` before `parse( node )`, or to apply `doNotParse()` to the node itself and its ancestors in `parse()`. The first keeps the change out of the vendored `twemoji.js`.

This matters for anything that inserts text the fallback should leave alone after load: editors on the front end (the reason for #52219), and code that draws emoji another way and marks it with this class. Wider context in [https://github.com/WordPress/gutenberg/discussions/83032 Gutenberg discussion #83032]."	defect (bug)	new	normal	7.2	Emoji	6.2	normal		has-patch has-unit-tests		
