diff --git a/src/core/ReactDOMNodeCache.js b/src/core/ReactDOMNodeCache.js index eaec6501ba..908c2ddbcd 100644 --- a/src/core/ReactDOMNodeCache.js +++ b/src/core/ReactDOMNodeCache.js @@ -19,70 +19,8 @@ "use strict"; -var ExecutionEnvironment = require('ExecutionEnvironment'); -var ReactMount = require('ReactMount'); +var ReactID = require('ReactID'); -var invariant = require('invariant'); - -var nodeCache = {}; - -/** - * DOM node cache only intended for use by React. Placed into a shared module so - * that both read and write utilities may benefit from a shared cache. - * - * @internal - */ -var ReactDOMNodeCache = { - - /** - * Finds the node with the supplied React-generated DOM ID. - * - * @param {string} id A React-generated DOM ID. - * @return {?DOMElement} DOM node with the suppled `id`. - * @internal - */ - getNodeByID: function(id) { - invariant( - ExecutionEnvironment.canUseDOM, - 'getDOMNode(): The DOM is not supported in the current environment.' - ); - if (!nodeCache[id]) { - nodeCache[id] = - document.getElementById(id) || - ReactMount.findReactRenderedDOMNodeSlow(id); - } - return nodeCache[id]; - }, - - /** - * Purges the supplied ID from cache. - * - * @param {string} id A React-generated DOM ID. - * @internal - */ - purgeID: function(id) { - nodeCache[id] = null; - }, - - /** - * Purges the entire node cache used for fast ID lookups. - * - * This implementation is aggressive with purging because the bookkeeping - * associated with doing fine-grained deletes from the cache may outweight the - * benefits of the cache. - * - * The heuristic used to purge is 'any time anything is deleted'. Typically - * this means that a large amount of content is being replaced and several - * elements would need purging regardless. This is also when applications are - * less likely to be in the middle of "smooth operations" such as animations - * or scrolling. - * - * @internal - */ - purgeEntireCache: function() { - nodeCache = {}; - } - -}; - -module.exports = ReactDOMNodeCache; +exports.getNodeByID = ReactID.getNode; +exports.purgeID = ReactID.purgeID; +exports.purgeEntireCache = ReactID.purgeEntireCache; diff --git a/src/core/ReactID.js b/src/core/ReactID.js new file mode 100644 index 0000000000..313ccebefe --- /dev/null +++ b/src/core/ReactID.js @@ -0,0 +1,128 @@ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule ReactID + * @typechecks + */ + +"use strict"; + +var ReactMount = require('ReactMount'); +var ATTR_NAME = 'id'; +var nodeCache = {}; + +/** + * Accessing node[ATTR_NAME] or calling getAttribute(ATTR_NAME) on a form + * element can return its control whose name or ID equals ATTR_NAME. All + * DOM nodes support `getAttributeNode` but this can also get called on + * other objects so just return '' if we're given something other than a + * DOM node (such as window). + * + * @param {DOMElement|DOMWindow|DOMDocument} node DOM node. + * @returns {string} ID of the supplied `domNode`. + */ +function getID(node) { + if (node && node.getAttributeNode) { + var attributeNode = node.getAttributeNode(ATTR_NAME); + return attributeNode && attributeNode.value || ''; + } + + return ''; +} + +/** + * Sets the React-specific ID of the given node. + * + * @param {DOMElement} node The DOM node whose ID will be set. + * @param {string} id The value of the ID attribute. + */ +function setID(node, id) { + var oldID = getID(node); + if (oldID !== id) { + delete nodeCache[oldID]; + } + node.setAttribute(ATTR_NAME, id); + nodeCache[id] = node; +} + +/** + * Finds the node with the supplied React-generated DOM ID. + * + * @param {string} id A React-generated DOM ID. + * @return {?DOMElement} DOM node with the suppled `id`. + * @internal + */ +function getNode(id) { + if (!nodeCache.hasOwnProperty(id)) { + nodeCache[id] = + document.getElementById(id) || // TODO Quit using getElementById. + ReactMount.findReactRenderedDOMNodeSlow(id); + } + + var node = nodeCache[id]; + if (getID(node) === id) { + return node; + } + + return null; +} + +/** + * Efficiently finds all nodes with a React-specific ID and primes the + * cache so that getNode lookups take constant time. + * + * @param {DOMElement} root The root element to scan. + */ +function primeTree(root) { + var nodes = root.querySelectorAll + ? root.querySelectorAll('[' + ATTR_NAME + ']') + : root.getElementsByTagName('*'); + + for (var i = 0; i < nodes.length; ++i) { + prime(nodes.item(i)); + } + + prime(root); +} + +function prime(node) { + var attributeNode = node.getAttributeNode(ATTR_NAME); + if (attributeNode) { + nodeCache[attributeNode.value] = node; + } +} + +/** + * Causes the cache to forget about one React-specific ID. + * + * @param {string} id The ID to forget. + */ +function purgeID(id) { + delete nodeCache[id]; +} + +/** + * Clears the entire cache. + */ +function purgeEntireCache() { + nodeCache = {}; +} + +exports.getID = getID; +exports.setID = setID; +exports.getNode = getNode; +exports.primeTree = primeTree; +exports.purgeID = purgeID; +exports.purgeEntireCache = purgeEntireCache; diff --git a/src/dom/getDOMNodeID.js b/src/dom/getDOMNodeID.js index f9632d8de5..dddd6fadee 100644 --- a/src/dom/getDOMNodeID.js +++ b/src/dom/getDOMNodeID.js @@ -19,22 +19,9 @@ "use strict"; -/** - * Accessing "id" or calling getAttribute('id') on a form element can return its - * control whose name or ID is "id". All DOM nodes support `getAttributeNode` - * but this can also get called on other objects so just return '' if we're - * given something other than a DOM node (such as window). - * - * @param {DOMElement|DOMWindow|DOMDocument} domNode DOM node. - * @returns {string} ID of the supplied `domNode`. - */ -function getDOMNodeID(domNode) { - if (domNode.getAttributeNode) { - var attributeNode = domNode.getAttributeNode('id'); - return attributeNode && attributeNode.value || ''; - } else { - return ''; - } -} +var ReactID = require('ReactID'); +function getDOMNodeID(domNode) { + return ReactID.getID(domNode); +} module.exports = getDOMNodeID;