dom-construct.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. define("dojo/dom-construct", ["exports", "./_base/kernel", "./_base/sniff", "./_base/window", "./dom", "./dom-attr", "./on"],
  2. function(exports, dojo, has, win, dom, attr, on){
  3. // module:
  4. // dojo/dom-construct
  5. // summary:
  6. // This module defines the core dojo DOM construction API.
  7. /*=====
  8. dojo.toDom = function(frag, doc){
  9. // summary:
  10. // instantiates an HTML fragment returning the corresponding DOM.
  11. // frag: String
  12. // the HTML fragment
  13. // doc: DocumentNode?
  14. // optional document to use when creating DOM nodes, defaults to
  15. // dojo.doc if not specified.
  16. // returns: DocumentFragment
  17. //
  18. // example:
  19. // Create a table row:
  20. // | var tr = dojo.toDom("<tr><td>First!</td></tr>");
  21. };
  22. =====*/
  23. /*=====
  24. dojo._toDom = function(frag, doc){
  25. // summary:
  26. // Existing alias for `dojo.toDom`. Deprecated, will be removed in 2.0.
  27. };
  28. =====*/
  29. /*=====
  30. dojo.place = function(node, refNode, position){
  31. // summary:
  32. // Attempt to insert node into the DOM, choosing from various positioning options.
  33. // Returns the first argument resolved to a DOM node.
  34. //
  35. // node: DOMNode|String
  36. // id or node reference, or HTML fragment starting with "<" to place relative to refNode
  37. //
  38. // refNode: DOMNode|String
  39. // id or node reference to use as basis for placement
  40. //
  41. // position: String|Number?
  42. // string noting the position of node relative to refNode or a
  43. // number indicating the location in the childNodes collection of refNode.
  44. // Accepted string values are:
  45. // | * before
  46. // | * after
  47. // | * replace
  48. // | * only
  49. // | * first
  50. // | * last
  51. // "first" and "last" indicate positions as children of refNode, "replace" replaces refNode,
  52. // "only" replaces all children. position defaults to "last" if not specified
  53. //
  54. // returns: DOMNode
  55. // Returned values is the first argument resolved to a DOM node.
  56. //
  57. // .place() is also a method of `dojo.NodeList`, allowing `dojo.query` node lookups.
  58. //
  59. // example:
  60. // Place a node by string id as the last child of another node by string id:
  61. // | dojo.place("someNode", "anotherNode");
  62. //
  63. // example:
  64. // Place a node by string id before another node by string id
  65. // | dojo.place("someNode", "anotherNode", "before");
  66. //
  67. // example:
  68. // Create a Node, and place it in the body element (last child):
  69. // | dojo.place("<div></div>", dojo.body());
  70. //
  71. // example:
  72. // Put a new LI as the first child of a list by id:
  73. // | dojo.place("<li></li>", "someUl", "first");
  74. };
  75. =====*/
  76. /*=====
  77. dojo.create = function(tag, attrs, refNode, pos){
  78. // summary:
  79. // Create an element, allowing for optional attribute decoration
  80. // and placement.
  81. //
  82. // description:
  83. // A DOM Element creation function. A shorthand method for creating a node or
  84. // a fragment, and allowing for a convenient optional attribute setting step,
  85. // as well as an optional DOM placement reference.
  86. //|
  87. // Attributes are set by passing the optional object through `dojo.setAttr`.
  88. // See `dojo.setAttr` for noted caveats and nuances, and API if applicable.
  89. //|
  90. // Placement is done via `dojo.place`, assuming the new node to be the action
  91. // node, passing along the optional reference node and position.
  92. //
  93. // tag: DOMNode|String
  94. // A string of the element to create (eg: "div", "a", "p", "li", "script", "br"),
  95. // or an existing DOM node to process.
  96. //
  97. // attrs: Object
  98. // An object-hash of attributes to set on the newly created node.
  99. // Can be null, if you don't want to set any attributes/styles.
  100. // See: `dojo.setAttr` for a description of available attributes.
  101. //
  102. // refNode: DOMNode?|String?
  103. // Optional reference node. Used by `dojo.place` to place the newly created
  104. // node somewhere in the dom relative to refNode. Can be a DomNode reference
  105. // or String ID of a node.
  106. //
  107. // pos: String?
  108. // Optional positional reference. Defaults to "last" by way of `dojo.place`,
  109. // though can be set to "first","after","before","last", "replace" or "only"
  110. // to further control the placement of the new node relative to the refNode.
  111. // 'refNode' is required if a 'pos' is specified.
  112. //
  113. // returns: DOMNode
  114. //
  115. // example:
  116. // Create a DIV:
  117. // | var n = dojo.create("div");
  118. //
  119. // example:
  120. // Create a DIV with content:
  121. // | var n = dojo.create("div", { innerHTML:"<p>hi</p>" });
  122. //
  123. // example:
  124. // Place a new DIV in the BODY, with no attributes set
  125. // | var n = dojo.create("div", null, dojo.body());
  126. //
  127. // example:
  128. // Create an UL, and populate it with LI's. Place the list as the first-child of a
  129. // node with id="someId":
  130. // | var ul = dojo.create("ul", null, "someId", "first");
  131. // | var items = ["one", "two", "three", "four"];
  132. // | dojo.forEach(items, function(data){
  133. // | dojo.create("li", { innerHTML: data }, ul);
  134. // | });
  135. //
  136. // example:
  137. // Create an anchor, with an href. Place in BODY:
  138. // | dojo.create("a", { href:"foo.html", title:"Goto FOO!" }, dojo.body());
  139. //
  140. // example:
  141. // Create a `dojo.NodeList()` from a new element (for syntatic sugar):
  142. // | dojo.query(dojo.create('div'))
  143. // | .addClass("newDiv")
  144. // | .onclick(function(e){ console.log('clicked', e.target) })
  145. // | .place("#someNode"); // redundant, but cleaner.
  146. };
  147. =====*/
  148. /*=====
  149. dojo.empty = function(node){
  150. // summary:
  151. // safely removes all children of the node.
  152. // node: DOMNode|String
  153. // a reference to a DOM node or an id.
  154. // example:
  155. // Destroy node's children byId:
  156. // | dojo.empty("someId");
  157. //
  158. // example:
  159. // Destroy all nodes' children in a list by reference:
  160. // | dojo.query(".someNode").forEach(dojo.empty);
  161. }
  162. =====*/
  163. /*=====
  164. dojo.destroy = function(node){
  165. // summary:
  166. // Removes a node from its parent, clobbering it and all of its
  167. // children.
  168. //
  169. // description:
  170. // Removes a node from its parent, clobbering it and all of its
  171. // children. Function only works with DomNodes, and returns nothing.
  172. //
  173. // node: DOMNode|String
  174. // A String ID or DomNode reference of the element to be destroyed
  175. //
  176. // example:
  177. // Destroy a node byId:
  178. // | dojo.destroy("someId");
  179. //
  180. // example:
  181. // Destroy all nodes in a list by reference:
  182. // | dojo.query(".someNode").forEach(dojo.destroy);
  183. };
  184. =====*/
  185. /*=====
  186. dojo._destroyElement = function(node){
  187. // summary:
  188. // Existing alias for `dojo.destroy`. Deprecated, will be removed in 2.0.
  189. };
  190. =====*/
  191. // support stuff for dojo.toDom
  192. var tagWrap = {
  193. option: ["select"],
  194. tbody: ["table"],
  195. thead: ["table"],
  196. tfoot: ["table"],
  197. tr: ["table", "tbody"],
  198. td: ["table", "tbody", "tr"],
  199. th: ["table", "thead", "tr"],
  200. legend: ["fieldset"],
  201. caption: ["table"],
  202. colgroup: ["table"],
  203. col: ["table", "colgroup"],
  204. li: ["ul"]
  205. },
  206. reTag = /<\s*([\w\:]+)/,
  207. masterNode = {}, masterNum = 0,
  208. masterName = "__" + dojo._scopeName + "ToDomId";
  209. // generate start/end tag strings to use
  210. // for the injection for each special tag wrap case.
  211. for(var param in tagWrap){
  212. if(tagWrap.hasOwnProperty(param)){
  213. var tw = tagWrap[param];
  214. tw.pre = param == "option" ? '<select multiple="multiple">' : "<" + tw.join("><") + ">";
  215. tw.post = "</" + tw.reverse().join("></") + ">";
  216. // the last line is destructive: it reverses the array,
  217. // but we don't care at this point
  218. }
  219. }
  220. function _insertBefore(/*DomNode*/node, /*DomNode*/ref){
  221. var parent = ref.parentNode;
  222. if(parent){
  223. parent.insertBefore(node, ref);
  224. }
  225. }
  226. function _insertAfter(/*DomNode*/node, /*DomNode*/ref){
  227. // summary:
  228. // Try to insert node after ref
  229. var parent = ref.parentNode;
  230. if(parent){
  231. if(parent.lastChild == ref){
  232. parent.appendChild(node);
  233. }else{
  234. parent.insertBefore(node, ref.nextSibling);
  235. }
  236. }
  237. }
  238. exports.toDom = function toDom(frag, doc){
  239. doc = doc || win.doc;
  240. var masterId = doc[masterName];
  241. if(!masterId){
  242. doc[masterName] = masterId = ++masterNum + "";
  243. masterNode[masterId] = doc.createElement("div");
  244. }
  245. // make sure the frag is a string.
  246. frag += "";
  247. // find the starting tag, and get node wrapper
  248. var match = frag.match(reTag),
  249. tag = match ? match[1].toLowerCase() : "",
  250. master = masterNode[masterId],
  251. wrap, i, fc, df;
  252. if(match && tagWrap[tag]){
  253. wrap = tagWrap[tag];
  254. master.innerHTML = wrap.pre + frag + wrap.post;
  255. for(i = wrap.length; i; --i){
  256. master = master.firstChild;
  257. }
  258. }else{
  259. master.innerHTML = frag;
  260. }
  261. // one node shortcut => return the node itself
  262. if(master.childNodes.length == 1){
  263. return master.removeChild(master.firstChild); // DOMNode
  264. }
  265. // return multiple nodes as a document fragment
  266. df = doc.createDocumentFragment();
  267. while((fc = master.firstChild)){ // intentional assignment
  268. df.appendChild(fc);
  269. }
  270. return df; // DOMNode
  271. };
  272. exports.place = function place(/*DOMNode|String*/node, /*DOMNode|String*/refNode, /*String|Number?*/position){
  273. refNode = dom.byId(refNode);
  274. if(typeof node == "string"){ // inline'd type check
  275. node = /^\s*</.test(node) ? exports.toDom(node, refNode.ownerDocument) : dom.byId(node);
  276. }
  277. if(typeof position == "number"){ // inline'd type check
  278. var cn = refNode.childNodes;
  279. if(!cn.length || cn.length <= position){
  280. refNode.appendChild(node);
  281. }else{
  282. _insertBefore(node, cn[position < 0 ? 0 : position]);
  283. }
  284. }else{
  285. switch(position){
  286. case "before":
  287. _insertBefore(node, refNode);
  288. break;
  289. case "after":
  290. _insertAfter(node, refNode);
  291. break;
  292. case "replace":
  293. refNode.parentNode.replaceChild(node, refNode);
  294. break;
  295. case "only":
  296. exports.empty(refNode);
  297. refNode.appendChild(node);
  298. break;
  299. case "first":
  300. if(refNode.firstChild){
  301. _insertBefore(node, refNode.firstChild);
  302. break;
  303. }
  304. // else fallthrough...
  305. default: // aka: last
  306. refNode.appendChild(node);
  307. }
  308. }
  309. return node; // DomNode
  310. };
  311. exports.create = function create(/*DOMNode|String*/tag, /*Object*/attrs, /*DOMNode?|String?*/refNode, /*String?*/pos){
  312. var doc = win.doc;
  313. if(refNode){
  314. refNode = dom.byId(refNode);
  315. doc = refNode.ownerDocument;
  316. }
  317. if(typeof tag == "string"){ // inline'd type check
  318. tag = doc.createElement(tag);
  319. }
  320. if(attrs){ attr.set(tag, attrs); }
  321. if(refNode){ exports.place(tag, refNode, pos); }
  322. return tag; // DomNode
  323. };
  324. function _empty(/*DomNode*/ node){
  325. if(node.canHaveChildren){
  326. try{
  327. // fast path
  328. node.innerHTML = "";
  329. return;
  330. }catch(e){
  331. // innerHTML is readOnly (e.g. TABLE (sub)elements in quirks mode)
  332. // Fall through (saves bytes)
  333. }
  334. }
  335. // SVG/strict elements don't support innerHTML/canHaveChildren, and OBJECT/APPLET elements in quirks node have canHaveChildren=false
  336. for(var c; c = node.lastChild;){ // intentional assignment
  337. _destroy(c, node); // destroy is better than removeChild so TABLE subelements are removed in proper order
  338. }
  339. }
  340. exports.empty = function empty(/*DOMNode|String*/ node){
  341. _empty(dom.byId(node));
  342. };
  343. function _destroy(/*DomNode*/ node, /*DomNode*/ parent){
  344. if(node.firstChild){
  345. _empty(node);
  346. }
  347. if(parent){
  348. // removeNode(false) doesn't leak in IE 6+, but removeChild() and removeNode(true) are known to leak under IE 8- while 9+ is TBD
  349. has("ie") && parent.canHaveChildren && 'removeNode' in node ? node.removeNode(false) :
  350. parent.removeChild(node);
  351. }
  352. }
  353. exports.destroy = function destroy(/*DOMNode|String*/ node){
  354. node = dom.byId(node);
  355. if(!node){ return; }
  356. _destroy(node, node.parentNode);
  357. };
  358. });