diff --git a/src/browser/ReactTextComponent.js b/src/browser/ReactTextComponent.js
index e23c720232180..58d1f64f82cae 100644
--- a/src/browser/ReactTextComponent.js
+++ b/src/browser/ReactTextComponent.js
@@ -33,8 +33,9 @@ var mixInto = require('mixInto');
* - When mounting text into the DOM, adjacent text nodes are merged.
* - Text nodes cannot be assigned a React root ID.
*
- * This component is used to wrap strings in elements so that they can undergo
- * the same reconciliation that is applied to elements.
+ * This component is used to add a ' + escapedText
);
},
@@ -95,7 +95,7 @@ mixInto(ReactTextComponent, {
var nextProps = nextComponent.props;
if (nextProps !== this.props) {
this.props = nextProps;
- ReactComponent.BackendIDOperations.updateTextContentByID(
+ ReactComponent.BackendIDOperations.updateTextContentAfterByID(
this._rootNodeID,
nextProps
);
diff --git a/src/browser/server/__tests__/ReactServerRendering-test.js b/src/browser/server/__tests__/ReactServerRendering-test.js
index 895ce51238e81..69b0968a4ae03 100644
--- a/src/browser/server/__tests__/ReactServerRendering-test.js
+++ b/src/browser/server/__tests__/ReactServerRendering-test.js
@@ -94,8 +94,8 @@ describe('ReactServerRendering', function() {
'
' +
'' +
- 'My name is ' +
- 'child' +
+ 'My name is ' +
+ 'child' +
'' +
'
'
);
@@ -143,8 +143,10 @@ describe('ReactServerRendering', function() {
expect(response).toMatch(
'' +
- 'Component name: ' +
- 'TestComponent' +
+ '' +
+ 'Component name: ' +
+ '' +
+ 'TestComponent' +
''
);
expect(lifecycle).toEqual(
diff --git a/src/browser/ui/ReactDOMIDOperations.js b/src/browser/ui/ReactDOMIDOperations.js
index ea206cd065b90..98456c4c47331 100644
--- a/src/browser/ui/ReactDOMIDOperations.js
+++ b/src/browser/ui/ReactDOMIDOperations.js
@@ -135,18 +135,18 @@ var ReactDOMIDOperations = {
),
/**
- * Updates a DOM node's text content set by `props.content`.
+ * Updates the text content after a DOM node.
*
* @param {string} id ID of the node to update.
* @param {string} content Text content.
* @internal
*/
- updateTextContentByID: ReactPerf.measure(
+ updateTextContentAfterByID: ReactPerf.measure(
'ReactDOMIDOperations',
- 'updateTextContentByID',
+ 'updateTextContentAfterByID',
function(id, content) {
var node = ReactMount.getNode(id);
- DOMChildrenOperations.updateTextContent(node, content);
+ DOMChildrenOperations.updateTextContentAfter(node, content);
}
),
diff --git a/src/browser/ui/dom/DOMChildrenOperations.js b/src/browser/ui/dom/DOMChildrenOperations.js
index 0c53caf2c968f..46a42ebf6ea98 100644
--- a/src/browser/ui/dom/DOMChildrenOperations.js
+++ b/src/browser/ui/dom/DOMChildrenOperations.js
@@ -25,6 +25,8 @@ var ReactMultiChildUpdateTypes = require('ReactMultiChildUpdateTypes');
var getTextContentAccessor = require('getTextContentAccessor');
var invariant = require('invariant');
+var TEXT_NODE = 3;
+
/**
* The DOM property to use when setting text content.
*
@@ -48,7 +50,7 @@ function insertChildAt(parentNode, childNode, index) {
// browsers so we must replace it with `null`.
parentNode.insertBefore(
childNode,
- parentNode.childNodes[index] || null
+ parentNode.children[index] || null
);
}
@@ -83,6 +85,16 @@ if (textContentAccessor === 'textContent') {
};
}
+function updateTextContentAfter(node, text) {
+ while (node.nextSibling && node.nextSibling.nodeType === TEXT_NODE) {
+ node.parentNode.removeChild(node.nextSibling);
+ }
+ if (text.length) {
+ var doc = node.ownerDocument || document;
+ node.parentNode.insertBefore(doc.createTextNode(text), node.nextSibling);
+ }
+}
+
/**
* Operations for updating with DOM children.
*/
@@ -90,7 +102,7 @@ var DOMChildrenOperations = {
dangerouslyReplaceNodeWithMarkup: Danger.dangerouslyReplaceNodeWithMarkup,
- updateTextContent: updateTextContent,
+ updateTextContentAfter: updateTextContentAfter,
/**
* Updates a component's children by processing a series of updates. The
@@ -111,7 +123,7 @@ var DOMChildrenOperations = {
if (update.type === ReactMultiChildUpdateTypes.MOVE_EXISTING ||
update.type === ReactMultiChildUpdateTypes.REMOVE_NODE) {
var updatedIndex = update.fromIndex;
- var updatedChild = update.parentNode.childNodes[updatedIndex];
+ var updatedChild = update.parentNode.children[updatedIndex];
var parentID = update.parentID;
invariant(
@@ -140,7 +152,13 @@ var DOMChildrenOperations = {
// Remove updated children first so that `toIndex` is consistent.
if (updatedChildren) {
for (var j = 0; j < updatedChildren.length; j++) {
- updatedChildren[j].parentNode.removeChild(updatedChildren[j]);
+ var nodeToRemove = updatedChildren[j];
+ // Remove trailing text nodes, probably from ReactTextComponent
+ while (nodeToRemove.nextSibling &&
+ nodeToRemove.nextSibling.nodeType === TEXT_NODE) {
+ nodeToRemove.parentNode.removeChild(nodeToRemove.nextSibling);
+ }
+ nodeToRemove.parentNode.removeChild(nodeToRemove);
}
}
diff --git a/src/browser/ui/dom/Danger.js b/src/browser/ui/dom/Danger.js
index 6e2dd071056e9..4b42f8579a993 100644
--- a/src/browser/ui/dom/Danger.js
+++ b/src/browser/ui/dom/Danger.js
@@ -30,6 +30,7 @@ var invariant = require('invariant');
var OPEN_TAG_NAME_EXP = /^(<[^ \/>]+)/;
var RESULT_INDEX_ATTR = 'data-danger-index';
+var TEXT_NODE = 3;
/**
* Extracts the `nodeName` from a string of markup.
@@ -54,7 +55,7 @@ var Danger = {
* `markupList` should be the same.
*
* @param {array} markupList List of markup strings to render.
- * @return {array} List of rendered nodes.
+ * @return {array} List of rendered nodes.
* @internal
*/
dangerouslyRenderMarkup: function(markupList) {
@@ -109,6 +110,7 @@ var Danger = {
emptyFunction // Do nothing special with