diff --git a/src/Helmet.js b/src/Helmet.js index d88ee7e4..5278a8af 100644 --- a/src/Helmet.js +++ b/src/Helmet.js @@ -14,22 +14,22 @@ import {TAG_NAMES, VALID_TAG_NAMES} from "./HelmetConstants.js"; const Helmet = Component => class HelmetWrapper extends React.Component { /** - * @param {Object} base: {"target": "_blank", "href": "http://mysite.com/"} - * @param {Object} bodyAttributes: {"className": "root"} - * @param {String} defaultTitle: "Default Title" - * @param {Boolean} defer: true - * @param {Boolean} encodeSpecialCharacters: true - * @param {Object} htmlAttributes: {"lang": "en", "amp": undefined} - * @param {Array} link: [{"rel": "canonical", "href": "http://mysite.com/example"}] - * @param {Array} meta: [{"name": "description", "content": "Test description"}] - * @param {Array} noscript: [{"innerHTML": " console.log(newState)" - * @param {Array} script: [{"type": "text/javascript", "src": "http://mysite.com/js/test.js"}] - * @param {Array} style: [{"type": "text/css", "cssText": "div { display: block; color: blue; }"}] - * @param {String} title: "Title" - * @param {Object} titleAttributes: {"itemprop": "name"} - * @param {String} titleTemplate: "MySite.com - %s" - */ + * @param {Object} base: {"target": "_blank", "href": "http://mysite.com/"} + * @param {Object} bodyAttributes: {"className": "root"} + * @param {String} defaultTitle: "Default Title" + * @param {Boolean} defer: true + * @param {Boolean} encodeSpecialCharacters: true + * @param {Object} htmlAttributes: {"lang": "en", "amp": undefined} + * @param {Array} link: [{"rel": "canonical", "href": "http://mysite.com/example"}] + * @param {Array} meta: [{"name": "description", "content": "Test description"}] + * @param {Array} noscript: [{"innerHTML": " console.log(newState)" + * @param {Array} script: [{"type": "text/javascript", "src": "http://mysite.com/js/test.js"}] + * @param {Array} style: [{"type": "text/css", "cssText": "div { display: block; color: blue; }"}] + * @param {String} title: "Title" + * @param {Object} titleAttributes: {"itemprop": "name"} + * @param {String} titleTemplate: "MySite.com - %s" + */ static propTypes = { base: PropTypes.object, bodyAttributes: PropTypes.object, diff --git a/src/HelmetUtils.js b/src/HelmetUtils.js index 63ad85a6..9bc3284d 100644 --- a/src/HelmetUtils.js +++ b/src/HelmetUtils.js @@ -416,40 +416,61 @@ const updateAttributes = (tagName, attributes) => { } }; +const setAttributes = (node, tag) => { + for (const attribute in tag) { + if (tag.hasOwnProperty(attribute)) { + if (attribute === TAG_PROPERTIES.INNER_HTML) { + node.innerHTML = tag.innerHTML; + } else if (attribute === TAG_PROPERTIES.CSS_TEXT) { + if (node.styleSheet) { + node.styleSheet.cssText = tag.cssText; + } else { + node.appendChild(document.createTextNode(tag.cssText)); + } + } else { + const value = typeof tag[attribute] === "undefined" + ? "" + : tag[attribute]; + node.setAttribute(attribute, value); + } + } + } +}; + const updateTags = (type, tags) => { const headElement = document.head || document.querySelector(TAG_NAMES.HEAD); const tagNodes = headElement.querySelectorAll( `${type}[${HELMET_ATTRIBUTE}]` ); - const oldTags = Array.prototype.slice.call(tagNodes); + const oldTags = Array.prototype.slice.call(tagNodes) || []; const newTags = []; + const updatedTags = []; let indexToDelete; - if (tags && tags.length) { tags.forEach(tag => { - const newElement = document.createElement(type); + // Check if exists? + let found; + if (oldTags && Array.isArray(oldTags)) { + found = oldTags.find(node => { + const name = + node.getAttribute("name") || + node.getAttribute("property"); + return name === (tag.name || tag.property); + }); + } - for (const attribute in tag) { - if (tag.hasOwnProperty(attribute)) { - if (attribute === TAG_PROPERTIES.INNER_HTML) { - newElement.innerHTML = tag.innerHTML; - } else if (attribute === TAG_PROPERTIES.CSS_TEXT) { - if (newElement.styleSheet) { - newElement.styleSheet.cssText = tag.cssText; - } else { - newElement.appendChild( - document.createTextNode(tag.cssText) - ); - } - } else { - const value = typeof tag[attribute] === "undefined" - ? "" - : tag[attribute]; - newElement.setAttribute(attribute, value); - } + if (found) { + const index = oldTags.indexOf(found); + if (index > -1) { + oldTags.splice(index, 1); } + setAttributes(found, tag); + updatedTags.push(found); + return; } + const newElement = document.createElement(type); + setAttributes(newElement, tag); newElement.setAttribute(HELMET_ATTRIBUTE, "true"); // Remove a duplicate tag from domTagstoRemove, so it isn't cleared. @@ -471,7 +492,7 @@ const updateTags = (type, tags) => { return { oldTags, - newTags + newTags: [...updatedTags, ...newTags] }; }; diff --git a/test/HelmetDeclarativeTest.js b/test/HelmetDeclarativeTest.js index bdcf5229..28776e3f 100644 --- a/test/HelmetDeclarativeTest.js +++ b/test/HelmetDeclarativeTest.js @@ -3485,7 +3485,6 @@ describe("Helmet - Declarative API", () => { expect(spy.called).to.equal(true); addedTags = spy.getCall(0).args[1]; removedTags = spy.getCall(0).args[2]; - expect(addedTags).to.have.property("metaTags"); expect(addedTags.metaTags).to.have.deep.property("[0]"); expect(addedTags.metaTags[0].outerHTML).to.equal( diff --git a/test/HelmetTest.js b/test/HelmetTest.js index c472e2c8..91edf2ff 100644 --- a/test/HelmetTest.js +++ b/test/HelmetTest.js @@ -3183,7 +3183,6 @@ describe("Helmet", () => { expect(spy.called).to.equal(true); addedTags = spy.getCall(0).args[1]; removedTags = spy.getCall(0).args[2]; - expect(addedTags).to.have.property("metaTags"); expect(addedTags.metaTags).to.have.deep.property("[0]"); expect(addedTags.metaTags[0].outerHTML).to.equal(