1 /** 2 * @fileOverview dom-insertion 3 * @author yiminghe@gmail.com,lifesinger@gmail.com 4 */ 5 KISSY.add('dom/insertion', function (S, UA, DOM) { 6 7 var PARENT_NODE = 'parentNode', 8 rformEls = /^(?:button|input|object|select|textarea)$/i, 9 getNodeName = DOM.nodeName, 10 makeArray = S.makeArray, 11 splice = [].splice, 12 NEXT_SIBLING = 'nextSibling'; 13 14 /** 15 ie 6,7 lose checked status when append to dom 16 var c=S.all("<input />"); 17 c.attr("type","radio"); 18 c.attr("checked",true); 19 S.all("#t").append(c); 20 alert(c[0].checked); 21 */ 22 function fixChecked(ret) { 23 for (var i = 0; i < ret.length; i++) { 24 var el = ret[i]; 25 if (el.nodeType == DOM.DOCUMENT_FRAGMENT_NODE) { 26 fixChecked(el.childNodes); 27 } else if (getNodeName(el) == "input") { 28 fixCheckedInternal(el); 29 } else if (el.nodeType == DOM.ELEMENT_NODE) { 30 var cs = el.getElementsByTagName("input"); 31 for (var j = 0; j < cs.length; j++) { 32 fixChecked(cs[j]); 33 } 34 } 35 } 36 } 37 38 function fixCheckedInternal(el) { 39 if (el.type === "checkbox" || el.type === "radio") { 40 // after insert , in ie6/7 checked is decided by defaultChecked ! 41 el.defaultChecked = el.checked; 42 } 43 } 44 45 var rscriptType = /\/(java|ecma)script/i; 46 47 function isJs(el) { 48 return !el.type || rscriptType.test(el.type); 49 } 50 51 // extract script nodes and execute alone later 52 function filterScripts(nodes, scripts) { 53 var ret = [], i, el, nodeName; 54 for (i = 0; nodes[i]; i++) { 55 el = nodes[i]; 56 nodeName = getNodeName(el); 57 if (el.nodeType == DOM.DOCUMENT_FRAGMENT_NODE) { 58 ret.push.apply(ret, filterScripts(makeArray(el.childNodes), scripts)); 59 } else if (nodeName === "script" && isJs(el)) { 60 // remove script to make sure ie9 does not invoke when append 61 if (el.parentNode) { 62 el.parentNode.removeChild(el) 63 } 64 if (scripts) { 65 scripts.push(el); 66 } 67 } else { 68 if (el.nodeType == DOM.ELEMENT_NODE && 69 // ie checkbox getElementsByTagName 后造成 checked 丢失 70 !rformEls.test(nodeName)) { 71 var tmp = [], 72 s, 73 j, 74 ss = el.getElementsByTagName("script"); 75 for (j = 0; j < ss.length; j++) { 76 s = ss[j]; 77 if (isJs(s)) { 78 tmp.push(s); 79 } 80 } 81 splice.apply(nodes, [i + 1, 0].concat(tmp)); 82 } 83 ret.push(el); 84 } 85 } 86 return ret; 87 } 88 89 // execute script 90 function evalScript(el) { 91 if (el.src) { 92 S.getScript(el.src); 93 } else { 94 var code = S.trim(el.text || el.textContent || el.innerHTML || ""); 95 if (code) { 96 S.globalEval(code); 97 } 98 } 99 } 100 101 // fragment is easier than nodelist 102 function insertion(newNodes, refNodes, fn, scripts) { 103 newNodes = DOM.query(newNodes); 104 105 if (scripts) { 106 scripts = []; 107 } 108 109 // filter script nodes ,process script separately if needed 110 newNodes = filterScripts(newNodes, scripts); 111 112 // Resets defaultChecked for any radios and checkboxes 113 // about to be appended to the DOM in IE 6/7 114 if (UA['ie'] < 8) { 115 fixChecked(newNodes); 116 } 117 refNodes = DOM.query(refNodes); 118 var newNodesLength = newNodes.length, 119 refNodesLength = refNodes.length; 120 if ((!newNodesLength && (!scripts || !scripts.length)) || !refNodesLength) { 121 return; 122 } 123 // fragment 插入速度快点 124 // 而且能够一个操作达到批量插入 125 // refer: http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-B63ED1A3 126 var newNode = DOM.nodeListToFragment(newNodes), 127 clonedNode; 128 //fragment 一旦插入里面就空了,先复制下 129 if (refNodesLength > 1) { 130 clonedNode = DOM.clone(newNode, true); 131 refNodes = S.makeArray(refNodes) 132 } 133 for (var i = 0; i < refNodesLength; i++) { 134 var refNode = refNodes[i]; 135 if (newNode) { 136 //refNodes 超过一个,clone 137 var node = i > 0 ? DOM.clone(clonedNode, true) : newNode; 138 fn(node, refNode); 139 } 140 if (scripts && scripts.length) { 141 S.each(scripts, evalScript); 142 } 143 } 144 } 145 146 // loadScripts default to false to prevent xss 147 S.mix(DOM, 148 /** 149 * @lends DOM 150 */ 151 { 152 153 /** 154 * Insert every element in the set of newNodes before every element in the set of refNodes. 155 * @param {HTMLElement|HTMLElement[]} newNodes Nodes to be inserted 156 * @param {HTMLElement|HTMLElement[]|String} refNodes Nodes to be referred 157 */ 158 insertBefore:function (newNodes, refNodes, loadScripts) { 159 insertion(newNodes, refNodes, function (newNode, refNode) { 160 if (refNode[PARENT_NODE]) { 161 refNode[PARENT_NODE].insertBefore(newNode, refNode); 162 } 163 }, loadScripts); 164 }, 165 166 /** 167 * Insert every element in the set of newNodes after every element in the set of refNodes. 168 * @param {HTMLElement|HTMLElement[]} newNodes Nodes to be inserted 169 * @param {HTMLElement|HTMLElement[]|String} refNodes Nodes to be referred 170 */ 171 insertAfter:function (newNodes, refNodes, loadScripts) { 172 insertion(newNodes, refNodes, function (newNode, refNode) { 173 if (refNode[PARENT_NODE]) { 174 refNode[PARENT_NODE].insertBefore(newNode, refNode[NEXT_SIBLING]); 175 } 176 }, loadScripts); 177 }, 178 179 /** 180 * Insert every element in the set of newNodes to the end of every element in the set of parents. 181 * @param {HTMLElement|HTMLElement[]} newNodes Nodes to be inserted 182 * @param {HTMLElement|HTMLElement[]|String} parents Nodes to be referred as parentNode 183 */ 184 appendTo:function (newNodes, parents, loadScripts) { 185 insertion(newNodes, parents, function (newNode, parent) { 186 parent.appendChild(newNode); 187 }, loadScripts); 188 }, 189 190 /** 191 * Insert every element in the set of newNodes to the beginning of every element in the set of parents. 192 * @param {HTMLElement|HTMLElement[]} newNodes Nodes to be inserted 193 * @param {HTMLElement|HTMLElement[]|String} parents Nodes to be referred as parentNode 194 */ 195 prependTo:function (newNodes, parents, loadScripts) { 196 insertion(newNodes, parents, function (newNode, parent) { 197 parent.insertBefore(newNode, parent.firstChild); 198 }, loadScripts); 199 }, 200 201 /** 202 * Wrap a node around all elements in the set of matched elements 203 * @param {HTMLElement|HTMLElement[]|String} wrappedNodes set of matched elements 204 * @param {HTMLElement|String} wrapperNode html node or selector to get the node wrapper 205 */ 206 wrapAll:function (wrappedNodes, wrapperNode) { 207 // deep clone 208 wrapperNode = DOM.clone(DOM.get(wrapperNode), true); 209 wrappedNodes = DOM.query(wrappedNodes); 210 if (wrappedNodes[0].parentNode) { 211 DOM.insertBefore(wrapperNode, wrappedNodes[0]); 212 } 213 var c; 214 while ((c = wrapperNode.firstChild) && c.nodeType == 1) { 215 wrapperNode = c; 216 } 217 DOM.appendTo(wrappedNodes, wrapperNode); 218 }, 219 220 /** 221 * Wrap a node around each element in the set of matched elements 222 * @param {HTMLElement|HTMLElement[]|String} wrappedNodes set of matched elements 223 * @param {HTMLElement|String} wrapperNode html node or selector to get the node wrapper 224 */ 225 wrap:function (wrappedNodes, wrapperNode) { 226 wrappedNodes = DOM.query(wrappedNodes); 227 wrapperNode = DOM.get(wrapperNode); 228 S.each(wrappedNodes, function (w) { 229 DOM.wrapAll(w, wrapperNode); 230 }); 231 }, 232 233 /** 234 * Wrap a node around the childNodes of each element in the set of matched elements. 235 * @param {HTMLElement|HTMLElement[]|String} wrappedNodes set of matched elements 236 * @param {HTMLElement|String} wrapperNode html node or selector to get the node wrapper 237 */ 238 wrapInner:function (wrappedNodes, wrapperNode) { 239 wrappedNodes = DOM.query(wrappedNodes); 240 wrapperNode = DOM.get(wrapperNode); 241 S.each(wrappedNodes, function (w) { 242 var contents = w.childNodes; 243 if (contents.length) { 244 DOM.wrapAll(contents, wrapperNode); 245 } else { 246 w.appendChild(wrapperNode); 247 } 248 }); 249 }, 250 251 /** 252 * Remove the parents of the set of matched elements from the DOM, 253 * leaving the matched elements in their place. 254 * @param {HTMLElement|HTMLElement[]|String} wrappedNodes set of matched elements 255 */ 256 unwrap:function (wrappedNodes) { 257 wrappedNodes = DOM.query(wrappedNodes); 258 S.each(wrappedNodes, function (w) { 259 var p = w.parentNode; 260 DOM.replaceWith(p, p.childNodes); 261 }); 262 }, 263 264 /** 265 * Replace each element in the set of matched elements with the provided newNodes. 266 * @param {HTMLElement|HTMLElement[]|String} selector set of matched elements 267 * @param {HTMLElement|HTMLElement[]|String} newNodes new nodes to replace the matched elements 268 */ 269 replaceWith:function (selector, newNodes) { 270 var nodes = DOM.query(selector); 271 newNodes = DOM.query(newNodes); 272 DOM.remove(newNodes, true); 273 DOM.insertBefore(newNodes, nodes); 274 DOM.remove(nodes); 275 } 276 }); 277 var alias = { 278 prepend:"prependTo", 279 append:"appendTo", 280 before:"insertBefore", 281 after:"insertAfter" 282 }; 283 for (var a in alias) { 284 DOM[a] = DOM[alias[a]]; 285 } 286 return DOM; 287 }, { 288 requires:["ua", "./create"] 289 }); 290 291 /** 292 * 2012-04-05 yiminghe@gmail.com 293 * - 增加 replaceWith/wrap/wrapAll/wrapInner/unwrap 294 * 295 * 2011-05-25 296 * - 承玉:参考 jquery 处理多对多的情形 :http://api.jquery.com/append/ 297 * DOM.append(".multi1",".multi2"); 298 * 299 */ 300