@samitouri / QOS-React / commits / 751edd6e2c

[DevTools] Measure text nodes (#34851)

We can't measure Text nodes directly but we can measure a Range around them. This is useful since it's common, at least in examples, to use text nodes as children of a Suspense boundary. Especially fallbacks.

Sebastian Markbåge committed Oct 15, 2025 at 10:24 UTC 751edd6e2cd2cb44aa302ffa667a0693e84e579d
2 files changed +24 -4
packages/react-devtools-shared/src/backend/fiber/renderer.js
+19 -2
@@ -2251,7 +2251,10 @@ export function attach(
2251 if (typeof instance !== 'object' || instance === null) {
2252 return null;
2253 }
2254 - if (typeof instance.getClientRects === 'function') {
2254 + if (
2255 + typeof instance.getClientRects === 'function' ||
2256 + instance.nodeType === 3
2257 + ) {
2258 // DOM
2259 const doc = instance.ownerDocument;
2260 if (instance === doc.documentElement) {
@@ -2273,7 +2276,21 @@ export function attach(
2276 const win = doc && doc.defaultView;
2277 const scrollX = win ? win.scrollX : 0;
2278 const scrollY = win ? win.scrollY : 0;
2276 - const rects = instance.getClientRects();
2279 + let rects;
2280 + if (instance.nodeType === 3) {
2281 + // Text nodes cannot be measured directly but we can measure a Range.
2282 + if (typeof doc.createRange !== 'function') {
2283 + return null;
2284 + }
2285 + const range = doc.createRange();
2286 + if (typeof range.getClientRects !== 'function') {
2287 + return null;
2288 + }
2289 + range.selectNodeContents(instance);
2290 + rects = range.getClientRects();
2291 + } else {
2292 + rects = instance.getClientRects();
2293 + }
2294 for (let i = 0; i < rects.length; i++) {
2295 const rect = rects[i];
2296 result.push({
packages/react-devtools-shared/src/backend/views/Highlighter/Overlay.js
+5 -2
@@ -187,10 +187,13 @@ export default class Overlay {
187 }
188 }
189
190 - inspect(nodes: $ReadOnlyArray<HTMLElement>, name?: ?string) {
190 + inspect(nodes: $ReadOnlyArray<HTMLElement | Text>, name?: ?string) {
191 // We can't get the size of text nodes or comment nodes. React as of v15
192 // heavily uses comment nodes to delimit text.
193 - const elements = nodes.filter(node => node.nodeType === Node.ELEMENT_NODE);
193 + // TODO: We actually can measure text nodes. We should.
194 + const elements: $ReadOnlyArray<HTMLElement> = (nodes.filter(
195 + node => node.nodeType === Node.ELEMENT_NODE,
196 + ): any);
197
198 while (this.rects.length > elements.length) {
199 const rect = this.rects.pop();