3 Copyright 2012 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
7 YUI.add('dom-create', function(Y) {
9 var re_tag = /<([a-z]+)/i,
13 addFeature = Y.Features.add,
14 testFeature = Y.Features.test,
18 createFromDIV = function(html, tag) {
19 var div = Y.config.doc.createElement('div'),
23 if (!div.firstChild || div.firstChild.tagName !== tag.toUpperCase()) {
30 re_tbody = /(?:\/(?:thead|tfoot|tbody|caption|col|colgroup)>)+\s*<tbody/,
32 TABLE_OPEN = '<table>',
33 TABLE_CLOSE = '</table>';
38 _create: function(html, doc, tag) {
41 var frag = Y_DOM._fragClones[tag];
43 frag = frag.cloneNode(false);
45 frag = Y_DOM._fragClones[tag] = doc.createElement(tag);
47 frag.innerHTML = html;
52 * Creates a new dom node using the provided markup string.
54 * @param {String} html The markup used to create the element
55 * @param {HTMLDocument} doc An optional document context
56 * @return {HTMLElement|DocumentFragment} returns a single HTMLElement
57 * when creating one node, and a documentFragment when creating
60 create: function(html, doc) {
61 if (typeof html === 'string') {
62 html = Y.Lang.trim(html); // match IE which trims whitespace from innerHTML
66 doc = doc || Y.config.doc;
67 var m = re_tag.exec(html),
68 create = Y_DOM._create,
74 if (html != undefined) { // not undefined or null
76 creator = custom[m[1].toLowerCase()];
77 if (typeof creator === 'function') {
84 nodes = create(html, doc, tag).childNodes;
86 if (nodes.length === 1) { // return single node, breaking parentNode ref from "fragment"
87 ret = nodes[0].parentNode.removeChild(nodes[0]);
88 } else if (nodes[0] && nodes[0].className === 'yui3-big-dummy') { // using dummy node to preserve some attributes (e.g. OPTION not selected)
89 if (nodes.length === 2) {
90 ret = nodes[0].nextSibling;
92 nodes[0].parentNode.removeChild(nodes[0]);
93 ret = Y_DOM._nl2frag(nodes, doc);
95 } else { // return multiple nodes as a fragment
96 ret = Y_DOM._nl2frag(nodes, doc);
103 _nl2frag: function(nodes, doc) {
107 if (nodes && (nodes.push || nodes.item) && nodes[0]) {
108 doc = doc || nodes[0].ownerDocument;
109 ret = doc.createDocumentFragment();
111 if (nodes.item) { // convert live list to static array
112 nodes = Y.Array(nodes, 0, true);
115 for (i = 0, len = nodes.length; i < len; i++) {
116 ret.appendChild(nodes[i]);
118 } // else inline with log for minification
123 * Inserts content in a node at the given location
125 * @param {HTMLElement} node The node to insert into
126 * @param {HTMLElement | Array | HTMLCollection} content The content to be inserted
127 * @param {HTMLElement} where Where to insert the content
128 * If no "where" is given, content is appended to the node
129 * Possible values for "where"
131 * <dt>HTMLElement</dt>
132 * <dd>The element to insert before</dd>
134 * <dd>Replaces the existing HTML</dd>
136 * <dd>Inserts before the existing HTML</dd>
138 * <dd>Inserts content before the node</dd>
140 * <dd>Inserts content after the node</dd>
143 addHTML: function(node, content, where) {
144 var nodeParent = node.parentNode,
151 if (content != undefined) { // not null or undefined (maybe 0)
152 if (content.nodeType) { // DOM node, just add it
154 } else if (typeof content == 'string' || typeof content == 'number') {
155 ret = newNode = Y_DOM.create(content);
156 } else if (content[0] && content[0].nodeType) { // array or collection
157 newNode = Y.config.doc.createDocumentFragment();
158 while ((item = content[i++])) {
159 newNode.appendChild(item); // append to fragment for insertion
165 if (where.nodeType) { // insert regardless of relationship to node
166 where.parentNode.insertBefore(newNode, where);
170 while (node.firstChild) {
171 node.removeChild(node.firstChild);
173 if (newNode) { // allow empty content to clear node
174 node.appendChild(newNode);
178 nodeParent.insertBefore(newNode, node);
181 if (node.nextSibling) { // IE errors if refNode is null
182 nodeParent.insertBefore(newNode, node.nextSibling);
184 nodeParent.appendChild(newNode);
188 node.appendChild(newNode);
191 } else if (newNode) {
192 node.appendChild(newNode);
199 addFeature('innerhtml', 'table', {
201 var node = Y.config.doc.createElement('table');
203 node.innerHTML = '<tbody></tbody>';
207 return (node.firstChild && node.firstChild.nodeName === 'TBODY');
211 addFeature('innerhtml-div', 'tr', {
213 return createFromDIV('<tr></tr>', 'tr');
217 addFeature('innerhtml-div', 'script', {
219 return createFromDIV('<script></script>', 'script');
223 if (!testFeature('innerhtml', 'table')) {
224 // TODO: thead/tfoot with nested tbody
225 // IE adds TBODY when creating TABLE elements (which may share this impl)
226 creators.tbody = function(html, doc) {
227 var frag = Y_DOM.create(TABLE_OPEN + html + TABLE_CLOSE, doc),
228 tb = frag.children.tags('tbody')[0];
230 if (frag.children.length > 1 && tb && !re_tbody.test(html)) {
231 tb.parentNode.removeChild(tb); // strip extraneous tbody
237 if (!testFeature('innerhtml-div', 'script')) {
238 creators.script = function(html, doc) {
239 var frag = doc.createElement('div');
241 frag.innerHTML = '-' + html;
242 frag.removeChild(frag.firstChild);
246 creators.link = creators.style = creators.script;
249 if (!testFeature('innerhtml-div', 'tr')) {
251 option: function(html, doc) {
252 return Y_DOM.create('<select><option class="yui3-big-dummy" selected></option>' + html + '</select>', doc);
255 tr: function(html, doc) {
256 return Y_DOM.create('<tbody>' + html + '</tbody>', doc);
259 td: function(html, doc) {
260 return Y_DOM.create('<tr>' + html + '</tr>', doc);
263 col: function(html, doc) {
264 return Y_DOM.create('<colgroup>' + html + '</colgroup>', doc);
273 thead: creators.tbody,
274 tfoot: creators.tbody,
275 caption: creators.tbody,
276 colgroup: creators.tbody,
277 optgroup: creators.option
281 Y_DOM.creators = creators;
284 }, '3.7.2' ,{requires:['dom-core']});