Minor DOM FragmentInstance refactors (#35641)
Handles TODOs, small follow up refactors
Jack Pope committed
Feb 11, 2026 at 10:03 UTC
cd515d7e22636238adef912356f522168946313d
1 file changed
+44
-26
packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
+44
-26
@@ -3056,13 +3056,16 @@ function indexOfEventListener(
3056
listener: EventListener,
3057
optionsOrUseCapture: void | EventListenerOptionsOrUseCapture,
3058
): number {
3059
+ if (eventListeners.length === 0) {
3060
+ return -1;
3061
+ }
3062
+ const normalizedOptions = normalizeListenerOptions(optionsOrUseCapture);
3063
for (let i = 0; i < eventListeners.length; i++) {
3064
const item = eventListeners[i];
3065
if (
3066
item.type === type &&
3067
item.listener === listener &&
3064
- normalizeListenerOptions(item.optionsOrUseCapture) ===
3065
- normalizeListenerOptions(optionsOrUseCapture)
3068
+ normalizeListenerOptions(item.optionsOrUseCapture) === normalizedOptions
3069
) {
3070
return i;
3071
}
@@ -3154,18 +3157,34 @@ function collectChildren(child: Fiber, collection: Array<Fiber>): boolean {
3157
}
3158
// $FlowFixMe[prop-missing]
3159
FragmentInstance.prototype.blur = function (this: FragmentInstanceType): void {
3157
- // TODO: When we have a parent element reference, we can skip traversal if the fragment's parent
3158
- // does not contain document.activeElement
3160
+ // Early exit if activeElement is not within the fragment's parent
3161
+ const parentHostFiber = getFragmentParentHostFiber(this._fragmentFiber);
3162
+ if (parentHostFiber === null) {
3163
+ return;
3164
+ }
3165
+ const parentHostInstance =
3166
+ getInstanceFromHostFiber<Instance>(parentHostFiber);
3167
+ const activeElement = parentHostInstance.ownerDocument.activeElement;
3168
+ if (activeElement === null || !parentHostInstance.contains(activeElement)) {
3169
+ return;
3170
+ }
3171
+
3172
traverseFragmentInstance(
3173
this._fragmentFiber,
3174
blurActiveElementWithinFragment,
3175
+ activeElement,
3176
);
3177
};
3164
-function blurActiveElementWithinFragment(child: Fiber): boolean {
3165
- // TODO: We can get the activeElement from the parent outside of the loop when we have a reference.
3178
+function blurActiveElementWithinFragment(
3179
+ child: Fiber,
3180
+ activeElement: Element,
3181
+): boolean {
3182
+ // Skip text nodes - they can't be focused
3183
+ if (enableFragmentRefsTextNodes && child.tag === HostText) {
3184
+ return false;
3185
+ }
3186
const instance = getInstanceFromHostFiber<Instance>(child);
3167
- const ownerDocument = instance.ownerDocument;
3168
- if (instance === ownerDocument.activeElement) {
3187
+ if (instance === activeElement) {
3188
// $FlowFixMe[prop-missing]
3189
instance.blur();
3190
return true;
@@ -3312,46 +3331,45 @@ FragmentInstance.prototype.compareDocumentPosition = function (
3331
);
3332
}
3333
3315
- const firstElement = getInstanceFromHostFiber<Instance>(children[0]);
3316
- const lastElement = getInstanceFromHostFiber<Instance>(
3334
+ const firstNode = getInstanceFromHostFiber<Instance>(children[0]);
3335
+ const lastNode = getInstanceFromHostFiber<Instance>(
3336
children[children.length - 1],
3337
);
3338
3339
// If the fragment has been portaled into another host instance, we need to
3340
// our best guess is to use the parent of the child instance, rather than
3341
// the fiber tree host parent.
3323
- const firstInstance = getInstanceFromHostFiber<Instance>(children[0]);
3342
const parentHostInstanceFromDOM = fiberIsPortaledIntoHost(this._fragmentFiber)
3325
- ? (firstInstance.parentElement: ?Instance)
3343
+ ? (firstNode.parentElement: ?Instance)
3344
: parentHostInstance;
3345
3346
if (parentHostInstanceFromDOM == null) {
3347
return Node.DOCUMENT_POSITION_DISCONNECTED;
3348
}
3349
3332
- // Check if first and last element are actually in the expected document position
3333
- // before relying on them as source of truth for other contained elements
3334
- const firstElementIsContained =
3335
- parentHostInstanceFromDOM.compareDocumentPosition(firstElement) &
3350
+ // Check if first and last node are actually in the expected document position
3351
+ // before relying on them as source of truth for other contained nodes
3352
+ const firstNodeIsContained =
3353
+ parentHostInstanceFromDOM.compareDocumentPosition(firstNode) &
3354
Node.DOCUMENT_POSITION_CONTAINED_BY;
3337
- const lastElementIsContained =
3338
- parentHostInstanceFromDOM.compareDocumentPosition(lastElement) &
3355
+ const lastNodeIsContained =
3356
+ parentHostInstanceFromDOM.compareDocumentPosition(lastNode) &
3357
Node.DOCUMENT_POSITION_CONTAINED_BY;
3340
- const firstResult = firstElement.compareDocumentPosition(otherNode);
3341
- const lastResult = lastElement.compareDocumentPosition(otherNode);
3358
+ const firstResult = firstNode.compareDocumentPosition(otherNode);
3359
+ const lastResult = lastNode.compareDocumentPosition(otherNode);
3360
3361
const otherNodeIsFirstOrLastChild =
3344
- (firstElementIsContained && firstElement === otherNode) ||
3345
- (lastElementIsContained && lastElement === otherNode);
3362
+ (firstNodeIsContained && firstNode === otherNode) ||
3363
+ (lastNodeIsContained && lastNode === otherNode);
3364
const otherNodeIsFirstOrLastChildDisconnected =
3347
- (!firstElementIsContained && firstElement === otherNode) ||
3348
- (!lastElementIsContained && lastElement === otherNode);
3365
+ (!firstNodeIsContained && firstNode === otherNode) ||
3366
+ (!lastNodeIsContained && lastNode === otherNode);
3367
const otherNodeIsWithinFirstOrLastChild =
3368
firstResult & Node.DOCUMENT_POSITION_CONTAINED_BY ||
3369
lastResult & Node.DOCUMENT_POSITION_CONTAINED_BY;
3370
const otherNodeIsBetweenFirstAndLastChildren =
3353
- firstElementIsContained &&
3354
- lastElementIsContained &&
3371
+ firstNodeIsContained &&
3372
+ lastNodeIsContained &&
3373
firstResult & Node.DOCUMENT_POSITION_FOLLOWING &&
3374
lastResult & Node.DOCUMENT_POSITION_PRECEDING;
3375