can-dom-mutate
Version:
Dispatch and listen for DOM mutations
192 lines (174 loc) • 6.06 kB
JavaScript
'use strict';
var globals = require('can-globals');
var namespace = require('can-namespace');
var domMutate = require('../can-dom-mutate');
var util = require('../-util');
var getParents = util.getParents;
var contains = util.contains;
var isConnected = require("../-is-connected");
var compat = {
replaceChild: function (newChild, oldChild) {
var newChildren = getParents(newChild);
var result = this.replaceChild(newChild, oldChild);
domMutate.dispatchNodeRemoval(oldChild, this);
for (var i = 0; i < newChildren.length; i++) {
domMutate.dispatchNodeInsertion(newChildren[i], this);
}
return result;
},
setAttribute: function (name, value) {
var oldAttributeValue = this.getAttribute(name);
var result = this.setAttribute(name, value);
var newAttributeValue = this.getAttribute(name);
if (oldAttributeValue !== newAttributeValue) {
domMutate.dispatchNodeAttributeChange(this, name, oldAttributeValue);
}
return result;
},
setAttributeNS: function (namespace, name, value) {
var oldAttributeValue = this.getAttribute(name);
var result = this.setAttributeNS(namespace, name, value);
var newAttributeValue = this.getAttribute(name);
if (oldAttributeValue !== newAttributeValue) {
domMutate.dispatchNodeAttributeChange(this, name, oldAttributeValue);
}
return result;
},
removeAttribute: function (name) {
var oldAttributeValue = this.getAttribute(name);
var result = this.removeAttribute(name);
if (oldAttributeValue) {
domMutate.dispatchNodeAttributeChange(this, name, oldAttributeValue);
}
return result;
}
};
var compatData = [
['appendChild', 'Insertion'],
['insertBefore', 'Insertion'],
['removeChild', 'Removal']
];
compatData.forEach(function (pair) {
var nodeMethod = pair[0];
var dispatchMethod = 'dispatchNode' + pair[1];
compat[nodeMethod] = function (node) {
var nodes = getParents(node);
var result = this[nodeMethod].apply(this, arguments);
for (var i = 0; i < nodes.length; i++) {
domMutate[dispatchMethod](nodes[i], this);
}
return result;
};
});
var normal = {};
var nodeMethods = ['appendChild', 'insertBefore', 'removeChild', 'replaceChild', 'setAttribute', 'setAttributeNS', 'removeAttribute'];
nodeMethods.forEach(function (methodName) {
normal[methodName] = function () {
if(isConnected.isConnected(this)) {
return this[methodName].apply(this, arguments);
} else {
return compat[methodName].apply(this, arguments);
}
};
});
/**
* @module {{}} can-dom-mutate/node node
* @parent can-dom-mutate/modules
*
* Append, insert, and remove DOM nodes. Also, change node attributes.
* This allows mutations to be dispatched in environments where MutationObserver is not supported.
* @signature `mutateNode`
*
* Exports an `Object` with methods that shouhld be used to mutate HTML.
*
* ```js
* var mutateNode = require('can-dom-mutate/node');
* var el = document.createElement('div');
*
* mutateNode.appendChild.call(document.body, el);
*
* ```
*/
var mutate = {};
/**
* @function can-dom-mutate/node.appendChild appendChild
* @parent can-dom-mutate/node
*
* Append a node to an element, effectively `Node.prototype.appendChild`.
*
* @signature `mutate.appendChild.call(parent, child)`
*
* @param {Node} parent The parent into which the child is inserted.
* @param {Node} child The child which will be inserted into the parent.
* @return {Node} The appended child.
*/
/**
* @function can-dom-mutate/node.insertBefore insertBefore
* @parent can-dom-mutate/node
*
* Insert a node before a given reference node in an element, effectively `Node.prototype.insertBefore`.
*
* @signature `mutate.insertBefore.call(parent, child, reference)`
* @param {Node} parent The parent into which the child is inserted.
* @param {Node} child The child which will be inserted into the parent.
* @param {Node} reference The reference which the child will be placed before.
* @return {Node} The inserted child.
*/
/**
* @function can-dom-mutate/node.removeChild removeChild
* @parent can-dom-mutate/node
*
* Remove a node from an element, effectively `Node.prototype.removeChild`.
*
* @signature `mutate.removeChild.call(parent, child)`
*
* @param {Node} parent The parent from which the child is removed.
* @param {Node} child The child which will be removed from the parent.
* @return {Node} The removed child.
*/
/**
* @function can-dom-mutate/node.replaceChild replaceChild
* @parent can-dom-mutate/node
*
* Insert a node before a given reference node in an element, effectively `Node.prototype.replaceChild`.
*
* @signature `mutate.replaceChild.call(parent, newChild, oldChild)`
*
* @param {Node} parent The parent into which the newChild is inserted.
* @param {Node} newChild The child which is inserted into the parent.
* @param {Node} oldChild The child which is removed from the parent.
* @return {Node} The replaced child.
*/
/**
* @function can-dom-mutate/node.setAttribute setAttribute
* @parent can-dom-mutate/node
*
* Set an attribute value on an element, effectively `Element.prototype.setAttribute`.
*
* @signature `mutate.setAttribute.call(element, name, value)`
*
* @param {Element} element The element on which to set the attribute.
* @param {String} name The name of the attribute to set.
* @param {String} value The value to set on the attribute.
*/
/**
* @function can-dom-mutate/node.removeAttribute removeAttribute
* @parent can-dom-mutate/node
*
* Removes an attribute from an element, effectively `Element.prototype.removeAttribute`.
*
* @signature `mutate.removeAttribute.call(element, name, value)`
*
* @param {Element} element The element from which to remove the attribute.
* @param {String} name The name of the attribute to remove.
*/
function setMutateStrategy(observer) {
var strategy = observer ? normal : compat;
for (var key in strategy) {
mutate[key] = strategy[key];
}
}
var mutationObserverKey = 'MutationObserver';
setMutateStrategy(globals.getKeyValue(mutationObserverKey));
globals.onKeyValue(mutationObserverKey, setMutateStrategy);
module.exports = namespace.domMutateNode = domMutate.node = mutate;