domUtils.js 80 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548
  1. /**
  2. * Dom操作工具包
  3. * @file
  4. * @module UE.dom.domUtils
  5. * @since 1.2.6.1
  6. */
  7. /**
  8. * Dom操作工具包
  9. * @unfile
  10. * @module UE.dom.domUtils
  11. */
  12. function getDomNode(node, start, ltr, startFromChild, fn, guard) {
  13. var tmpNode = startFromChild && node[start],
  14. parent;
  15. !tmpNode && (tmpNode = node[ltr]);
  16. while (!tmpNode && (parent = (parent || node).parentNode)) {
  17. if (parent.tagName == "BODY" || (guard && !guard(parent))) {
  18. return null;
  19. }
  20. tmpNode = parent[ltr];
  21. }
  22. if (tmpNode && fn && !fn(tmpNode)) {
  23. return getDomNode(tmpNode, start, ltr, false, fn);
  24. }
  25. return tmpNode;
  26. }
  27. var attrFix = ie && browser.version < 9
  28. ? {
  29. tabindex: "tabIndex",
  30. readonly: "readOnly",
  31. for: "htmlFor",
  32. class: "className",
  33. maxlength: "maxLength",
  34. cellspacing: "cellSpacing",
  35. cellpadding: "cellPadding",
  36. rowspan: "rowSpan",
  37. colspan: "colSpan",
  38. usemap: "useMap",
  39. frameborder: "frameBorder"
  40. }
  41. : {
  42. tabindex: "tabIndex",
  43. readonly: "readOnly"
  44. },
  45. styleBlock = utils.listToMap([
  46. "-webkit-box",
  47. "-moz-box",
  48. "block",
  49. "list-item",
  50. "table",
  51. "table-row-group",
  52. "table-header-group",
  53. "table-footer-group",
  54. "table-row",
  55. "table-column-group",
  56. "table-column",
  57. "table-cell",
  58. "table-caption"
  59. ]);
  60. var domUtils = (dom.domUtils = {
  61. //节点常量
  62. NODE_ELEMENT: 1,
  63. NODE_DOCUMENT: 9,
  64. NODE_TEXT: 3,
  65. NODE_COMMENT: 8,
  66. NODE_DOCUMENT_FRAGMENT: 11,
  67. //位置关系
  68. POSITION_IDENTICAL: 0,
  69. POSITION_DISCONNECTED: 1,
  70. POSITION_FOLLOWING: 2,
  71. POSITION_PRECEDING: 4,
  72. POSITION_IS_CONTAINED: 8,
  73. POSITION_CONTAINS: 16,
  74. //ie6使用其他的会有一段空白出现
  75. fillChar: ie && browser.version == "6" ? "\ufeff" : "\u200B",
  76. //-------------------------Node部分--------------------------------
  77. keys: {
  78. /*Backspace*/ 8: 1,
  79. /*Delete*/ 46: 1,
  80. /*Shift*/ 16: 1,
  81. /*Ctrl*/ 17: 1,
  82. /*Alt*/ 18: 1,
  83. 37: 1,
  84. 38: 1,
  85. 39: 1,
  86. 40: 1,
  87. 13: 1 /*enter*/
  88. },
  89. /**
  90. * 获取节点A相对于节点B的位置关系
  91. * @method getPosition
  92. * @param { Node } nodeA 需要查询位置关系的节点A
  93. * @param { Node } nodeB 需要查询位置关系的节点B
  94. * @return { Number } 节点A与节点B的关系
  95. * @example
  96. * ```javascript
  97. * //output: 20
  98. * var position = UE.dom.domUtils.getPosition( document.documentElement, document.body );
  99. *
  100. * switch ( position ) {
  101. *
  102. * //0
  103. * case UE.dom.domUtils.POSITION_IDENTICAL:
  104. * console.log('元素相同');
  105. * break;
  106. * //1
  107. * case UE.dom.domUtils.POSITION_DISCONNECTED:
  108. * console.log('两个节点在不同的文档中');
  109. * break;
  110. * //2
  111. * case UE.dom.domUtils.POSITION_FOLLOWING:
  112. * console.log('节点A在节点B之后');
  113. * break;
  114. * //4
  115. * case UE.dom.domUtils.POSITION_PRECEDING;
  116. * console.log('节点A在节点B之前');
  117. * break;
  118. * //8
  119. * case UE.dom.domUtils.POSITION_IS_CONTAINED:
  120. * console.log('节点A被节点B包含');
  121. * break;
  122. * case 10:
  123. * console.log('节点A被节点B包含且节点A在节点B之后');
  124. * break;
  125. * //16
  126. * case UE.dom.domUtils.POSITION_CONTAINS:
  127. * console.log('节点A包含节点B');
  128. * break;
  129. * case 20:
  130. * console.log('节点A包含节点B且节点A在节点B之前');
  131. * break;
  132. *
  133. * }
  134. * ```
  135. */
  136. getPosition: function(nodeA, nodeB) {
  137. // 如果两个节点是同一个节点
  138. if (nodeA === nodeB) {
  139. // domUtils.POSITION_IDENTICAL
  140. return 0;
  141. }
  142. var node,
  143. parentsA = [nodeA],
  144. parentsB = [nodeB];
  145. node = nodeA;
  146. while ((node = node.parentNode)) {
  147. // 如果nodeB是nodeA的祖先节点
  148. if (node === nodeB) {
  149. // domUtils.POSITION_IS_CONTAINED + domUtils.POSITION_FOLLOWING
  150. return 10;
  151. }
  152. parentsA.push(node);
  153. }
  154. node = nodeB;
  155. while ((node = node.parentNode)) {
  156. // 如果nodeA是nodeB的祖先节点
  157. if (node === nodeA) {
  158. // domUtils.POSITION_CONTAINS + domUtils.POSITION_PRECEDING
  159. return 20;
  160. }
  161. parentsB.push(node);
  162. }
  163. parentsA.reverse();
  164. parentsB.reverse();
  165. if (parentsA[0] !== parentsB[0]) {
  166. // domUtils.POSITION_DISCONNECTED
  167. return 1;
  168. }
  169. var i = -1;
  170. while ((i++, parentsA[i] === parentsB[i])) {}
  171. nodeA = parentsA[i];
  172. nodeB = parentsB[i];
  173. while ((nodeA = nodeA.nextSibling)) {
  174. if (nodeA === nodeB) {
  175. // domUtils.POSITION_PRECEDING
  176. return 4;
  177. }
  178. }
  179. // domUtils.POSITION_FOLLOWING
  180. return 2;
  181. },
  182. /**
  183. * 检测节点node在父节点中的索引位置
  184. * @method getNodeIndex
  185. * @param { Node } node 需要检测的节点对象
  186. * @return { Number } 该节点在父节点中的位置
  187. * @see UE.dom.domUtils.getNodeIndex(Node,Boolean)
  188. */
  189. /**
  190. * 检测节点node在父节点中的索引位置, 根据给定的mergeTextNode参数决定是否要合并多个连续的文本节点为一个节点
  191. * @method getNodeIndex
  192. * @param { Node } node 需要检测的节点对象
  193. * @param { Boolean } mergeTextNode 是否合并多个连续的文本节点为一个节点
  194. * @return { Number } 该节点在父节点中的位置
  195. * @example
  196. * ```javascript
  197. *
  198. * var node = document.createElement("div");
  199. *
  200. * node.appendChild( document.createTextNode( "hello" ) );
  201. * node.appendChild( document.createTextNode( "world" ) );
  202. * node.appendChild( node = document.createElement( "div" ) );
  203. *
  204. * //output: 2
  205. * console.log( UE.dom.domUtils.getNodeIndex( node ) );
  206. *
  207. * //output: 1
  208. * console.log( UE.dom.domUtils.getNodeIndex( node, true ) );
  209. *
  210. * ```
  211. */
  212. getNodeIndex: function(node, ignoreTextNode) {
  213. var preNode = node,
  214. i = 0;
  215. while ((preNode = preNode.previousSibling)) {
  216. if (ignoreTextNode && preNode.nodeType == 3) {
  217. if (preNode.nodeType != preNode.nextSibling.nodeType) {
  218. i++;
  219. }
  220. continue;
  221. }
  222. i++;
  223. }
  224. return i;
  225. },
  226. /**
  227. * 检测节点node是否在给定的document对象上
  228. * @method inDoc
  229. * @param { Node } node 需要检测的节点对象
  230. * @param { DomDocument } doc 需要检测的document对象
  231. * @return { Boolean } 该节点node是否在给定的document的dom树上
  232. * @example
  233. * ```javascript
  234. *
  235. * var node = document.createElement("div");
  236. *
  237. * //output: false
  238. * console.log( UE.do.domUtils.inDoc( node, document ) );
  239. *
  240. * document.body.appendChild( node );
  241. *
  242. * //output: true
  243. * console.log( UE.do.domUtils.inDoc( node, document ) );
  244. *
  245. * ```
  246. */
  247. inDoc: function(node, doc) {
  248. return domUtils.getPosition(node, doc) == 10;
  249. },
  250. /**
  251. * 根据给定的过滤规则filterFn, 查找符合该过滤规则的node节点的第一个祖先节点,
  252. * 查找的起点是给定node节点的父节点。
  253. * @method findParent
  254. * @param { Node } node 需要查找的节点
  255. * @param { Function } filterFn 自定义的过滤方法。
  256. * @warning 查找的终点是到body节点为止
  257. * @remind 自定义的过滤方法filterFn接受一个Node对象作为参数, 该对象代表当前执行检测的祖先节点。 如果该
  258. * 节点满足过滤条件, 则要求返回true, 这时将直接返回该节点作为findParent()的结果, 否则, 请返回false。
  259. * @return { Node | Null } 如果找到符合过滤条件的节点, 就返回该节点, 否则返回NULL
  260. * @example
  261. * ```javascript
  262. * var filterNode = UE.dom.domUtils.findParent( document.body.firstChild, function ( node ) {
  263. *
  264. * //由于查找的终点是body节点, 所以永远也不会匹配当前过滤器的条件, 即这里永远会返回false
  265. * return node.tagName === "HTML";
  266. *
  267. * } );
  268. *
  269. * //output: true
  270. * console.log( filterNode === null );
  271. * ```
  272. */
  273. /**
  274. * 根据给定的过滤规则filterFn, 查找符合该过滤规则的node节点的第一个祖先节点,
  275. * 如果includeSelf的值为true,则查找的起点是给定的节点node, 否则, 起点是node的父节点
  276. * @method findParent
  277. * @param { Node } node 需要查找的节点
  278. * @param { Function } filterFn 自定义的过滤方法。
  279. * @param { Boolean } includeSelf 查找过程是否包含自身
  280. * @warning 查找的终点是到body节点为止
  281. * @remind 自定义的过滤方法filterFn接受一个Node对象作为参数, 该对象代表当前执行检测的祖先节点。 如果该
  282. * 节点满足过滤条件, 则要求返回true, 这时将直接返回该节点作为findParent()的结果, 否则, 请返回false。
  283. * @remind 如果includeSelf为true, 则过滤器第一次执行时的参数会是节点本身。
  284. * 反之, 过滤器第一次执行时的参数将是该节点的父节点。
  285. * @return { Node | Null } 如果找到符合过滤条件的节点, 就返回该节点, 否则返回NULL
  286. * @example
  287. * ```html
  288. * <body>
  289. *
  290. * <div id="test">
  291. * </div>
  292. *
  293. * <script type="text/javascript">
  294. *
  295. * //output: DIV, BODY
  296. * var filterNode = UE.dom.domUtils.findParent( document.getElementById( "test" ), function ( node ) {
  297. *
  298. * console.log( node.tagName );
  299. * return false;
  300. *
  301. * }, true );
  302. *
  303. * </script>
  304. * </body>
  305. * ```
  306. */
  307. findParent: function(node, filterFn, includeSelf) {
  308. if (node && !domUtils.isBody(node)) {
  309. node = includeSelf ? node : node.parentNode;
  310. while (node) {
  311. if (!filterFn || filterFn(node) || domUtils.isBody(node)) {
  312. return filterFn && !filterFn(node) && domUtils.isBody(node)
  313. ? null
  314. : node;
  315. }
  316. node = node.parentNode;
  317. }
  318. }
  319. return null;
  320. },
  321. /**
  322. * 查找node的节点名为tagName的第一个祖先节点, 查找的起点是node节点的父节点。
  323. * @method findParentByTagName
  324. * @param { Node } node 需要查找的节点对象
  325. * @param { Array } tagNames 需要查找的父节点的名称数组
  326. * @warning 查找的终点是到body节点为止
  327. * @return { Node | NULL } 如果找到符合条件的节点, 则返回该节点, 否则返回NULL
  328. * @example
  329. * ```javascript
  330. * var node = UE.dom.domUtils.findParentByTagName( document.getElementsByTagName("div")[0], [ "BODY" ] );
  331. * //output: BODY
  332. * console.log( node.tagName );
  333. * ```
  334. */
  335. /**
  336. * 查找node的节点名为tagName的祖先节点, 如果includeSelf的值为true,则查找的起点是给定的节点node,
  337. * 否则, 起点是node的父节点。
  338. * @method findParentByTagName
  339. * @param { Node } node 需要查找的节点对象
  340. * @param { Array } tagNames 需要查找的父节点的名称数组
  341. * @param { Boolean } includeSelf 查找过程是否包含node节点自身
  342. * @warning 查找的终点是到body节点为止
  343. * @return { Node | NULL } 如果找到符合条件的节点, 则返回该节点, 否则返回NULL
  344. * @example
  345. * ```javascript
  346. * var queryTarget = document.getElementsByTagName("div")[0];
  347. * var node = UE.dom.domUtils.findParentByTagName( queryTarget, [ "DIV" ], true );
  348. * //output: true
  349. * console.log( queryTarget === node );
  350. * ```
  351. */
  352. findParentByTagName: function(node, tagNames, includeSelf, excludeFn) {
  353. tagNames = utils.listToMap(utils.isArray(tagNames) ? tagNames : [tagNames]);
  354. return domUtils.findParent(
  355. node,
  356. function(node) {
  357. return tagNames[node.tagName] && !(excludeFn && excludeFn(node));
  358. },
  359. includeSelf
  360. );
  361. },
  362. /**
  363. * 查找节点node的祖先节点集合, 查找的起点是给定节点的父节点,结果集中不包含给定的节点。
  364. * @method findParents
  365. * @param { Node } node 需要查找的节点对象
  366. * @return { Array } 给定节点的祖先节点数组
  367. * @grammar UE.dom.domUtils.findParents(node) => Array //返回一个祖先节点数组集合,不包含自身
  368. * @grammar UE.dom.domUtils.findParents(node,includeSelf) => Array //返回一个祖先节点数组集合,includeSelf指定是否包含自身
  369. * @grammar UE.dom.domUtils.findParents(node,includeSelf,filterFn) => Array //返回一个祖先节点数组集合,filterFn指定过滤条件,返回true的node将被选取
  370. * @grammar UE.dom.domUtils.findParents(node,includeSelf,filterFn,closerFirst) => Array //返回一个祖先节点数组集合,closerFirst为true的话,node的直接父亲节点是数组的第0个
  371. */
  372. /**
  373. * 查找节点node的祖先节点集合, 如果includeSelf的值为true,
  374. * 则返回的结果集中允许出现当前给定的节点, 否则, 该节点不会出现在其结果集中。
  375. * @method findParents
  376. * @param { Node } node 需要查找的节点对象
  377. * @param { Boolean } includeSelf 查找的结果中是否允许包含当前查找的节点对象
  378. * @return { Array } 给定节点的祖先节点数组
  379. */
  380. findParents: function(node, includeSelf, filterFn, closerFirst) {
  381. var parents = includeSelf && ((filterFn && filterFn(node)) || !filterFn)
  382. ? [node]
  383. : [];
  384. while ((node = domUtils.findParent(node, filterFn))) {
  385. parents.push(node);
  386. }
  387. return closerFirst ? parents : parents.reverse();
  388. },
  389. /**
  390. * 在节点node后面插入新节点newNode
  391. * @method insertAfter
  392. * @param { Node } node 目标节点
  393. * @param { Node } newNode 新插入的节点, 该节点将置于目标节点之后
  394. * @return { Node } 新插入的节点
  395. */
  396. insertAfter: function(node, newNode) {
  397. return node.nextSibling
  398. ? node.parentNode.insertBefore(newNode, node.nextSibling)
  399. : node.parentNode.appendChild(newNode);
  400. },
  401. /**
  402. * 删除节点node及其下属的所有节点
  403. * @method remove
  404. * @param { Node } node 需要删除的节点对象
  405. * @return { Node } 返回刚删除的节点对象
  406. * @example
  407. * ```html
  408. * <div id="test">
  409. * <div id="child">你好</div>
  410. * </div>
  411. * <script>
  412. * UE.dom.domUtils.remove( document.body, false );
  413. * //output: false
  414. * console.log( document.getElementById( "child" ) !== null );
  415. * </script>
  416. * ```
  417. */
  418. /**
  419. * 删除节点node,并根据keepChildren的值决定是否保留子节点
  420. * @method remove
  421. * @param { Node } node 需要删除的节点对象
  422. * @param { Boolean } keepChildren 是否需要保留子节点
  423. * @return { Node } 返回刚删除的节点对象
  424. * @example
  425. * ```html
  426. * <div id="test">
  427. * <div id="child">你好</div>
  428. * </div>
  429. * <script>
  430. * UE.dom.domUtils.remove( document.body, true );
  431. * //output: true
  432. * console.log( document.getElementById( "child" ) !== null );
  433. * </script>
  434. * ```
  435. */
  436. remove: function(node, keepChildren) {
  437. var parent = node.parentNode,
  438. child;
  439. if (parent) {
  440. if (keepChildren && node.hasChildNodes()) {
  441. while ((child = node.firstChild)) {
  442. parent.insertBefore(child, node);
  443. }
  444. }
  445. parent.removeChild(node);
  446. }
  447. return node;
  448. },
  449. /**
  450. * 取得node节点的下一个兄弟节点, 如果该节点其后没有兄弟节点, 则递归查找其父节点之后的第一个兄弟节点,
  451. * 直到找到满足条件的节点或者递归到BODY节点之后才会结束。
  452. * @method getNextDomNode
  453. * @param { Node } node 需要获取其后的兄弟节点的节点对象
  454. * @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL
  455. * @example
  456. * ```html
  457. * <body>
  458. * <div id="test">
  459. * <span></span>
  460. * </div>
  461. * <i>xxx</i>
  462. * </body>
  463. * <script>
  464. *
  465. * //output: i节点
  466. * console.log( UE.dom.domUtils.getNextDomNode( document.getElementById( "test" ) ) );
  467. *
  468. * </script>
  469. * ```
  470. * @example
  471. * ```html
  472. * <body>
  473. * <div>
  474. * <span></span>
  475. * <i id="test">xxx</i>
  476. * </div>
  477. * <b>xxx</b>
  478. * </body>
  479. * <script>
  480. *
  481. * //由于id为test的i节点之后没有兄弟节点, 则查找其父节点(div)后面的兄弟节点
  482. * //output: b节点
  483. * console.log( UE.dom.domUtils.getNextDomNode( document.getElementById( "test" ) ) );
  484. *
  485. * </script>
  486. * ```
  487. */
  488. /**
  489. * 取得node节点的下一个兄弟节点, 如果startFromChild的值为ture,则先获取其子节点,
  490. * 如果有子节点则直接返回第一个子节点;如果没有子节点或者startFromChild的值为false,
  491. * 则执行<a href="#UE.dom.domUtils.getNextDomNode(Node)">getNextDomNode(Node node)</a>的查找过程。
  492. * @method getNextDomNode
  493. * @param { Node } node 需要获取其后的兄弟节点的节点对象
  494. * @param { Boolean } startFromChild 查找过程是否从其子节点开始
  495. * @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL
  496. * @see UE.dom.domUtils.getNextDomNode(Node)
  497. */
  498. getNextDomNode: function(node, startFromChild, filterFn, guard) {
  499. return getDomNode(
  500. node,
  501. "firstChild",
  502. "nextSibling",
  503. startFromChild,
  504. filterFn,
  505. guard
  506. );
  507. },
  508. getPreDomNode: function(node, startFromChild, filterFn, guard) {
  509. return getDomNode(
  510. node,
  511. "lastChild",
  512. "previousSibling",
  513. startFromChild,
  514. filterFn,
  515. guard
  516. );
  517. },
  518. /**
  519. * 检测节点node是否属是UEditor定义的bookmark节点
  520. * @method isBookmarkNode
  521. * @private
  522. * @param { Node } node 需要检测的节点对象
  523. * @return { Boolean } 是否是bookmark节点
  524. * @example
  525. * ```html
  526. * <span id="_baidu_bookmark_1"></span>
  527. * <script>
  528. * var bookmarkNode = document.getElementById("_baidu_bookmark_1");
  529. * //output: true
  530. * console.log( UE.dom.domUtils.isBookmarkNode( bookmarkNode ) );
  531. * </script>
  532. * ```
  533. */
  534. isBookmarkNode: function(node) {
  535. return node.nodeType == 1 && node.id && /^_baidu_bookmark_/i.test(node.id);
  536. },
  537. /**
  538. * 获取节点node所属的window对象
  539. * @method getWindow
  540. * @param { Node } node 节点对象
  541. * @return { Window } 当前节点所属的window对象
  542. * @example
  543. * ```javascript
  544. * //output: true
  545. * console.log( UE.dom.domUtils.getWindow( document.body ) === window );
  546. * ```
  547. */
  548. getWindow: function(node) {
  549. var doc = node.ownerDocument || node;
  550. return doc.defaultView || doc.parentWindow;
  551. },
  552. /**
  553. * 获取离nodeA与nodeB最近的公共的祖先节点
  554. * @method getCommonAncestor
  555. * @param { Node } nodeA 第一个节点
  556. * @param { Node } nodeB 第二个节点
  557. * @remind 如果给定的两个节点是同一个节点, 将直接返回该节点。
  558. * @return { Node | NULL } 如果未找到公共节点, 返回NULL, 否则返回最近的公共祖先节点。
  559. * @example
  560. * ```javascript
  561. * var commonAncestor = UE.dom.domUtils.getCommonAncestor( document.body, document.body.firstChild );
  562. * //output: true
  563. * console.log( commonAncestor.tagName.toLowerCase() === 'body' );
  564. * ```
  565. */
  566. getCommonAncestor: function(nodeA, nodeB) {
  567. if (nodeA === nodeB) return nodeA;
  568. var parentsA = [nodeA],
  569. parentsB = [nodeB],
  570. parent = nodeA,
  571. i = -1;
  572. while ((parent = parent.parentNode)) {
  573. if (parent === nodeB) {
  574. return parent;
  575. }
  576. parentsA.push(parent);
  577. }
  578. parent = nodeB;
  579. while ((parent = parent.parentNode)) {
  580. if (parent === nodeA) return parent;
  581. parentsB.push(parent);
  582. }
  583. parentsA.reverse();
  584. parentsB.reverse();
  585. while ((i++, parentsA[i] === parentsB[i])) {}
  586. return i == 0 ? null : parentsA[i - 1];
  587. },
  588. /**
  589. * 清除node节点左右连续为空的兄弟inline节点
  590. * @method clearEmptySibling
  591. * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
  592. * 则这些兄弟节点将被删除
  593. * @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext) //ignoreNext指定是否忽略右边空节点
  594. * @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext,ignorePre) //ignorePre指定是否忽略左边空节点
  595. * @example
  596. * ```html
  597. * <body>
  598. * <div></div>
  599. * <span id="test"></span>
  600. * <i></i>
  601. * <b></b>
  602. * <em>xxx</em>
  603. * <span></span>
  604. * </body>
  605. * <script>
  606. *
  607. * UE.dom.domUtils.clearEmptySibling( document.getElementById( "test" ) );
  608. *
  609. * //output: <div></div><span id="test"></span><em>xxx</em><span></span>
  610. * console.log( document.body.innerHTML );
  611. *
  612. * </script>
  613. * ```
  614. */
  615. /**
  616. * 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true,
  617. * 则忽略对右边兄弟节点的操作。
  618. * @method clearEmptySibling
  619. * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
  620. * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作
  621. * 则这些兄弟节点将被删除
  622. * @see UE.dom.domUtils.clearEmptySibling(Node)
  623. */
  624. /**
  625. * 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true,
  626. * 则忽略对右边兄弟节点的操作, 如果ignorePre的值为true,则忽略对左边兄弟节点的操作。
  627. * @method clearEmptySibling
  628. * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
  629. * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作
  630. * @param { Boolean } ignorePre 是否忽略忽略对左边的兄弟节点的操作
  631. * 则这些兄弟节点将被删除
  632. * @see UE.dom.domUtils.clearEmptySibling(Node)
  633. */
  634. clearEmptySibling: function(node, ignoreNext, ignorePre) {
  635. function clear(next, dir) {
  636. var tmpNode;
  637. while (
  638. next &&
  639. !domUtils.isBookmarkNode(next) &&
  640. (domUtils.isEmptyInlineElement(next) ||
  641. //这里不能把空格算进来会吧空格干掉,出现文字间的空格丢掉了
  642. !new RegExp("[^\t\n\r" + domUtils.fillChar + "]").test(
  643. next.nodeValue
  644. ))
  645. ) {
  646. tmpNode = next[dir];
  647. domUtils.remove(next);
  648. next = tmpNode;
  649. }
  650. }
  651. !ignoreNext && clear(node.nextSibling, "nextSibling");
  652. !ignorePre && clear(node.previousSibling, "previousSibling");
  653. },
  654. /**
  655. * 将一个文本节点textNode拆分成两个文本节点,offset指定拆分位置
  656. * @method split
  657. * @param { Node } textNode 需要拆分的文本节点对象
  658. * @param { int } offset 需要拆分的位置, 位置计算从0开始
  659. * @return { Node } 拆分后形成的新节点
  660. * @example
  661. * ```html
  662. * <div id="test">abcdef</div>
  663. * <script>
  664. * var newNode = UE.dom.domUtils.split( document.getElementById( "test" ).firstChild, 3 );
  665. * //output: def
  666. * console.log( newNode.nodeValue );
  667. * </script>
  668. * ```
  669. */
  670. split: function(node, offset) {
  671. var doc = node.ownerDocument;
  672. if (browser.ie && offset == node.nodeValue.length) {
  673. var next = doc.createTextNode("");
  674. return domUtils.insertAfter(node, next);
  675. }
  676. var retval = node.splitText(offset);
  677. //ie8下splitText不会跟新childNodes,我们手动触发他的更新
  678. if (browser.ie8) {
  679. var tmpNode = doc.createTextNode("");
  680. domUtils.insertAfter(retval, tmpNode);
  681. domUtils.remove(tmpNode);
  682. }
  683. return retval;
  684. },
  685. /**
  686. * 检测文本节点textNode是否为空节点(包括空格、换行、占位符等字符)
  687. * @method isWhitespace
  688. * @param { Node } node 需要检测的节点对象
  689. * @return { Boolean } 检测的节点是否为空
  690. * @example
  691. * ```html
  692. * <div id="test">
  693. *
  694. * </div>
  695. * <script>
  696. * //output: true
  697. * console.log( UE.dom.domUtils.isWhitespace( document.getElementById("test").firstChild ) );
  698. * </script>
  699. * ```
  700. */
  701. isWhitespace: function(node) {
  702. return !new RegExp("[^ \t\n\r" + domUtils.fillChar + "]").test(
  703. node.nodeValue
  704. );
  705. },
  706. /**
  707. * 获取元素element相对于viewport的位置坐标
  708. * @method getXY
  709. * @param { Node } element 需要计算位置的节点对象
  710. * @return { Object } 返回形如{x:left,y:top}的一个key-value映射对象, 其中键x代表水平偏移距离,
  711. * y代表垂直偏移距离。
  712. *
  713. * @example
  714. * ```javascript
  715. * var location = UE.dom.domUtils.getXY( document.getElementById("test") );
  716. * //output: test的坐标为: 12, 24
  717. * console.log( 'test的坐标为: ', location.x, ',', location.y );
  718. * ```
  719. */
  720. getXY: function(element) {
  721. var x = 0,
  722. y = 0;
  723. while (element.offsetParent) {
  724. y += element.offsetTop;
  725. x += element.offsetLeft;
  726. element = element.offsetParent;
  727. }
  728. return { x: x, y: y };
  729. },
  730. /**
  731. * 为元素element绑定原生DOM事件,type为事件类型,handler为处理函数
  732. * @method on
  733. * @param { Node } element 需要绑定事件的节点对象
  734. * @param { String } type 绑定的事件类型
  735. * @param { Function } handler 事件处理器
  736. * @example
  737. * ```javascript
  738. * UE.dom.domUtils.on(document.body,"click",function(e){
  739. * //e为事件对象,this为被点击元素对戏那个
  740. * });
  741. * ```
  742. */
  743. /**
  744. * 为元素element绑定原生DOM事件,type为事件类型,handler为处理函数
  745. * @method on
  746. * @param { Node } element 需要绑定事件的节点对象
  747. * @param { Array } type 绑定的事件类型数组
  748. * @param { Function } handler 事件处理器
  749. * @example
  750. * ```javascript
  751. * UE.dom.domUtils.on(document.body,["click","mousedown"],function(evt){
  752. * //evt为事件对象,this为被点击元素对象
  753. * });
  754. * ```
  755. */
  756. on: function(element, type, handler) {
  757. var types = utils.isArray(type) ? type : utils.trim(type).split(/\s+/),
  758. k = types.length;
  759. if (k)
  760. while (k--) {
  761. type = types[k];
  762. if (element.addEventListener) {
  763. element.addEventListener(type, handler, false);
  764. } else {
  765. if (!handler._d) {
  766. handler._d = {
  767. els: []
  768. };
  769. }
  770. var key = type + handler.toString(),
  771. index = utils.indexOf(handler._d.els, element);
  772. if (!handler._d[key] || index == -1) {
  773. if (index == -1) {
  774. handler._d.els.push(element);
  775. }
  776. if (!handler._d[key]) {
  777. handler._d[key] = function(evt) {
  778. return handler.call(evt.srcElement, evt || window.event);
  779. };
  780. }
  781. element.attachEvent("on" + type, handler._d[key]);
  782. }
  783. }
  784. }
  785. element = null;
  786. },
  787. /**
  788. * 解除DOM事件绑定
  789. * @method un
  790. * @param { Node } element 需要解除事件绑定的节点对象
  791. * @param { String } type 需要接触绑定的事件类型
  792. * @param { Function } handler 对应的事件处理器
  793. * @example
  794. * ```javascript
  795. * UE.dom.domUtils.un(document.body,"click",function(evt){
  796. * //evt为事件对象,this为被点击元素对象
  797. * });
  798. * ```
  799. */
  800. /**
  801. * 解除DOM事件绑定
  802. * @method un
  803. * @param { Node } element 需要解除事件绑定的节点对象
  804. * @param { Array } type 需要接触绑定的事件类型数组
  805. * @param { Function } handler 对应的事件处理器
  806. * @example
  807. * ```javascript
  808. * UE.dom.domUtils.un(document.body, ["click","mousedown"],function(evt){
  809. * //evt为事件对象,this为被点击元素对象
  810. * });
  811. * ```
  812. */
  813. un: function(element, type, handler) {
  814. var types = utils.isArray(type) ? type : utils.trim(type).split(/\s+/),
  815. k = types.length;
  816. if (k)
  817. while (k--) {
  818. type = types[k];
  819. if (element.removeEventListener) {
  820. element.removeEventListener(type, handler, false);
  821. } else {
  822. var key = type + handler.toString();
  823. try {
  824. element.detachEvent(
  825. "on" + type,
  826. handler._d ? handler._d[key] : handler
  827. );
  828. } catch (e) {}
  829. if (handler._d && handler._d[key]) {
  830. var index = utils.indexOf(handler._d.els, element);
  831. if (index != -1) {
  832. handler._d.els.splice(index, 1);
  833. }
  834. handler._d.els.length == 0 && delete handler._d[key];
  835. }
  836. }
  837. }
  838. },
  839. /**
  840. * 比较节点nodeA与节点nodeB是否具有相同的标签名、属性名以及属性值
  841. * @method isSameElement
  842. * @param { Node } nodeA 需要比较的节点
  843. * @param { Node } nodeB 需要比较的节点
  844. * @return { Boolean } 两个节点是否具有相同的标签名、属性名以及属性值
  845. * @example
  846. * ```html
  847. * <span style="font-size:12px">ssss</span>
  848. * <span style="font-size:12px">bbbbb</span>
  849. * <span style="font-size:13px">ssss</span>
  850. * <span style="font-size:14px">bbbbb</span>
  851. *
  852. * <script>
  853. *
  854. * var nodes = document.getElementsByTagName( "span" );
  855. *
  856. * //output: true
  857. * console.log( UE.dom.domUtils.isSameElement( nodes[0], nodes[1] ) );
  858. *
  859. * //output: false
  860. * console.log( UE.dom.domUtils.isSameElement( nodes[2], nodes[3] ) );
  861. *
  862. * </script>
  863. * ```
  864. */
  865. isSameElement: function(nodeA, nodeB) {
  866. if (nodeA.tagName != nodeB.tagName) {
  867. return false;
  868. }
  869. var thisAttrs = nodeA.attributes,
  870. otherAttrs = nodeB.attributes;
  871. if (!ie && thisAttrs.length != otherAttrs.length) {
  872. return false;
  873. }
  874. var attrA,
  875. attrB,
  876. al = 0,
  877. bl = 0;
  878. for (var i = 0; (attrA = thisAttrs[i++]); ) {
  879. if (attrA.nodeName == "style") {
  880. if (attrA.specified) {
  881. al++;
  882. }
  883. if (domUtils.isSameStyle(nodeA, nodeB)) {
  884. continue;
  885. } else {
  886. return false;
  887. }
  888. }
  889. if (ie) {
  890. if (attrA.specified) {
  891. al++;
  892. attrB = otherAttrs.getNamedItem(attrA.nodeName);
  893. } else {
  894. continue;
  895. }
  896. } else {
  897. attrB = nodeB.attributes[attrA.nodeName];
  898. }
  899. if (!attrB.specified || attrA.nodeValue != attrB.nodeValue) {
  900. return false;
  901. }
  902. }
  903. // 有可能attrB的属性包含了attrA的属性之外还有自己的属性
  904. if (ie) {
  905. for (i = 0; (attrB = otherAttrs[i++]); ) {
  906. if (attrB.specified) {
  907. bl++;
  908. }
  909. }
  910. if (al != bl) {
  911. return false;
  912. }
  913. }
  914. return true;
  915. },
  916. /**
  917. * 判断节点nodeA与节点nodeB的元素的style属性是否一致
  918. * @method isSameStyle
  919. * @param { Node } nodeA 需要比较的节点
  920. * @param { Node } nodeB 需要比较的节点
  921. * @return { Boolean } 两个节点是否具有相同的style属性值
  922. * @example
  923. * ```html
  924. * <span style="font-size:12px">ssss</span>
  925. * <span style="font-size:12px">bbbbb</span>
  926. * <span style="font-size:13px">ssss</span>
  927. * <span style="font-size:14px">bbbbb</span>
  928. *
  929. * <script>
  930. *
  931. * var nodes = document.getElementsByTagName( "span" );
  932. *
  933. * //output: true
  934. * console.log( UE.dom.domUtils.isSameStyle( nodes[0], nodes[1] ) );
  935. *
  936. * //output: false
  937. * console.log( UE.dom.domUtils.isSameStyle( nodes[2], nodes[3] ) );
  938. *
  939. * </script>
  940. * ```
  941. */
  942. isSameStyle: function(nodeA, nodeB) {
  943. var styleA = nodeA.style.cssText
  944. .replace(/( ?; ?)/g, ";")
  945. .replace(/( ?: ?)/g, ":"),
  946. styleB = nodeB.style.cssText
  947. .replace(/( ?; ?)/g, ";")
  948. .replace(/( ?: ?)/g, ":");
  949. if (browser.opera) {
  950. styleA = nodeA.style;
  951. styleB = nodeB.style;
  952. if (styleA.length != styleB.length) return false;
  953. for (var p in styleA) {
  954. if (/^(\d+|csstext)$/i.test(p)) {
  955. continue;
  956. }
  957. if (styleA[p] != styleB[p]) {
  958. return false;
  959. }
  960. }
  961. return true;
  962. }
  963. if (!styleA || !styleB) {
  964. return styleA == styleB;
  965. }
  966. styleA = styleA.split(";");
  967. styleB = styleB.split(";");
  968. if (styleA.length != styleB.length) {
  969. return false;
  970. }
  971. for (var i = 0, ci; (ci = styleA[i++]); ) {
  972. if (utils.indexOf(styleB, ci) == -1) {
  973. return false;
  974. }
  975. }
  976. return true;
  977. },
  978. /**
  979. * 检查节点node是否为block元素
  980. * @method isBlockElm
  981. * @param { Node } node 需要检测的节点对象
  982. * @return { Boolean } 是否是block元素节点
  983. * @warning 该方法的判断规则如下: 如果该元素原本是block元素, 则不论该元素当前的css样式是什么都会返回true;
  984. * 否则,检测该元素的css样式, 如果该元素当前是block元素, 则返回true。 其余情况下都返回false。
  985. * @example
  986. * ```html
  987. * <span id="test1" style="display: block"></span>
  988. * <span id="test2"></span>
  989. * <div id="test3" style="display: inline"></div>
  990. *
  991. * <script>
  992. *
  993. * //output: true
  994. * console.log( UE.dom.domUtils.isBlockElm( document.getElementById("test1") ) );
  995. *
  996. * //output: false
  997. * console.log( UE.dom.domUtils.isBlockElm( document.getElementById("test2") ) );
  998. *
  999. * //output: true
  1000. * console.log( UE.dom.domUtils.isBlockElm( document.getElementById("test3") ) );
  1001. *
  1002. * </script>
  1003. * ```
  1004. */
  1005. isBlockElm: function(node) {
  1006. return (
  1007. node.nodeType == 1 &&
  1008. (dtd.$block[node.tagName] ||
  1009. styleBlock[domUtils.getComputedStyle(node, "display")]) &&
  1010. !dtd.$nonChild[node.tagName]
  1011. );
  1012. },
  1013. /**
  1014. * 检测node节点是否为body节点
  1015. * @method isBody
  1016. * @param { Element } node 需要检测的dom元素
  1017. * @return { Boolean } 给定的元素是否是body元素
  1018. * @example
  1019. * ```javascript
  1020. * //output: true
  1021. * console.log( UE.dom.domUtils.isBody( document.body ) );
  1022. * ```
  1023. */
  1024. isBody: function(node) {
  1025. return node && node.nodeType == 1 && node.tagName.toLowerCase() == "body";
  1026. },
  1027. /**
  1028. * 以node节点为分界,将该节点的指定祖先节点parent拆分成两个独立的节点,
  1029. * 拆分形成的两个节点之间是node节点
  1030. * @method breakParent
  1031. * @param { Node } node 作为分界的节点对象
  1032. * @param { Node } parent 该节点必须是node节点的祖先节点, 且是block节点。
  1033. * @return { Node } 给定的node分界节点
  1034. * @example
  1035. * ```javascript
  1036. *
  1037. * var node = document.createElement("span"),
  1038. * wrapNode = document.createElement( "div" ),
  1039. * parent = document.createElement("p");
  1040. *
  1041. * parent.appendChild( node );
  1042. * wrapNode.appendChild( parent );
  1043. *
  1044. * //拆分前
  1045. * //output: <p><span></span></p>
  1046. * console.log( wrapNode.innerHTML );
  1047. *
  1048. *
  1049. * UE.dom.domUtils.breakParent( node, parent );
  1050. * //拆分后
  1051. * //output: <p></p><span></span><p></p>
  1052. * console.log( wrapNode.innerHTML );
  1053. *
  1054. * ```
  1055. */
  1056. breakParent: function(node, parent) {
  1057. var tmpNode,
  1058. parentClone = node,
  1059. clone = node,
  1060. leftNodes,
  1061. rightNodes;
  1062. do {
  1063. parentClone = parentClone.parentNode;
  1064. if (leftNodes) {
  1065. tmpNode = parentClone.cloneNode(false);
  1066. tmpNode.appendChild(leftNodes);
  1067. leftNodes = tmpNode;
  1068. tmpNode = parentClone.cloneNode(false);
  1069. tmpNode.appendChild(rightNodes);
  1070. rightNodes = tmpNode;
  1071. } else {
  1072. leftNodes = parentClone.cloneNode(false);
  1073. rightNodes = leftNodes.cloneNode(false);
  1074. }
  1075. while ((tmpNode = clone.previousSibling)) {
  1076. leftNodes.insertBefore(tmpNode, leftNodes.firstChild);
  1077. }
  1078. while ((tmpNode = clone.nextSibling)) {
  1079. rightNodes.appendChild(tmpNode);
  1080. }
  1081. clone = parentClone;
  1082. } while (parent !== parentClone);
  1083. tmpNode = parent.parentNode;
  1084. tmpNode.insertBefore(leftNodes, parent);
  1085. tmpNode.insertBefore(rightNodes, parent);
  1086. tmpNode.insertBefore(node, rightNodes);
  1087. domUtils.remove(parent);
  1088. return node;
  1089. },
  1090. /**
  1091. * 检查节点node是否是空inline节点
  1092. * @method isEmptyInlineElement
  1093. * @param { Node } node 需要检测的节点对象
  1094. * @return { Number } 如果给定的节点是空的inline节点, 则返回1, 否则返回0。
  1095. * @example
  1096. * ```html
  1097. * <b><i></i></b> => 1
  1098. * <b><i></i><u></u></b> => 1
  1099. * <b></b> => 1
  1100. * <b>xx<i></i></b> => 0
  1101. * ```
  1102. */
  1103. isEmptyInlineElement: function(node) {
  1104. if (node.nodeType != 1 || !dtd.$removeEmpty[node.tagName]) {
  1105. return 0;
  1106. }
  1107. node = node.firstChild;
  1108. while (node) {
  1109. //如果是创建的bookmark就跳过
  1110. if (domUtils.isBookmarkNode(node)) {
  1111. return 0;
  1112. }
  1113. if (
  1114. (node.nodeType == 1 && !domUtils.isEmptyInlineElement(node)) ||
  1115. (node.nodeType == 3 && !domUtils.isWhitespace(node))
  1116. ) {
  1117. return 0;
  1118. }
  1119. node = node.nextSibling;
  1120. }
  1121. return 1;
  1122. },
  1123. /**
  1124. * 删除node节点下首尾两端的空白文本子节点
  1125. * @method trimWhiteTextNode
  1126. * @param { Element } node 需要执行删除操作的元素对象
  1127. * @example
  1128. * ```javascript
  1129. * var node = document.createElement("div");
  1130. *
  1131. * node.appendChild( document.createTextNode( "" ) );
  1132. *
  1133. * node.appendChild( document.createElement("div") );
  1134. *
  1135. * node.appendChild( document.createTextNode( "" ) );
  1136. *
  1137. * //3
  1138. * console.log( node.childNodes.length );
  1139. *
  1140. * UE.dom.domUtils.trimWhiteTextNode( node );
  1141. *
  1142. * //1
  1143. * console.log( node.childNodes.length );
  1144. * ```
  1145. */
  1146. trimWhiteTextNode: function(node) {
  1147. function remove(dir) {
  1148. var child;
  1149. while (
  1150. (child = node[dir]) &&
  1151. child.nodeType == 3 &&
  1152. domUtils.isWhitespace(child)
  1153. ) {
  1154. node.removeChild(child);
  1155. }
  1156. }
  1157. remove("firstChild");
  1158. remove("lastChild");
  1159. },
  1160. /**
  1161. * 合并node节点下相同的子节点
  1162. * @name mergeChild
  1163. * @desc
  1164. * UE.dom.domUtils.mergeChild(node,tagName) //tagName要合并的子节点的标签
  1165. * @example
  1166. * <p><span style="font-size:12px;">xx<span style="font-size:12px;">aa</span>xx</span></p>
  1167. * ==> UE.dom.domUtils.mergeChild(node,'span')
  1168. * <p><span style="font-size:12px;">xxaaxx</span></p>
  1169. */
  1170. mergeChild: function(node, tagName, attrs) {
  1171. var list = domUtils.getElementsByTagName(node, node.tagName.toLowerCase());
  1172. for (var i = 0, ci; (ci = list[i++]); ) {
  1173. if (!ci.parentNode || domUtils.isBookmarkNode(ci)) {
  1174. continue;
  1175. }
  1176. //span单独处理
  1177. if (ci.tagName.toLowerCase() == "span") {
  1178. if (node === ci.parentNode) {
  1179. domUtils.trimWhiteTextNode(node);
  1180. if (node.childNodes.length == 1) {
  1181. node.style.cssText = ci.style.cssText + ";" + node.style.cssText;
  1182. domUtils.remove(ci, true);
  1183. continue;
  1184. }
  1185. }
  1186. ci.style.cssText = node.style.cssText + ";" + ci.style.cssText;
  1187. if (attrs) {
  1188. var style = attrs.style;
  1189. if (style) {
  1190. style = style.split(";");
  1191. for (var j = 0, s; (s = style[j++]); ) {
  1192. ci.style[utils.cssStyleToDomStyle(s.split(":")[0])] = s.split(
  1193. ":"
  1194. )[1];
  1195. }
  1196. }
  1197. }
  1198. if (domUtils.isSameStyle(ci, node)) {
  1199. domUtils.remove(ci, true);
  1200. }
  1201. continue;
  1202. }
  1203. if (domUtils.isSameElement(node, ci)) {
  1204. domUtils.remove(ci, true);
  1205. }
  1206. }
  1207. },
  1208. /**
  1209. * 原生方法getElementsByTagName的封装
  1210. * @method getElementsByTagName
  1211. * @param { Node } node 目标节点对象
  1212. * @param { String } tagName 需要查找的节点的tagName, 多个tagName以空格分割
  1213. * @return { Array } 符合条件的节点集合
  1214. */
  1215. getElementsByTagName: function(node, name, filter) {
  1216. if (filter && utils.isString(filter)) {
  1217. var className = filter;
  1218. filter = function(node) {
  1219. return domUtils.hasClass(node, className);
  1220. };
  1221. }
  1222. name = utils.trim(name).replace(/[ ]{2,}/g, " ").split(" ");
  1223. var arr = [];
  1224. for (var n = 0, ni; (ni = name[n++]); ) {
  1225. var list = node.getElementsByTagName(ni);
  1226. for (var i = 0, ci; (ci = list[i++]); ) {
  1227. if (!filter || filter(ci)) arr.push(ci);
  1228. }
  1229. }
  1230. return arr;
  1231. },
  1232. /**
  1233. * 将节点node提取到父节点上
  1234. * @method mergeToParent
  1235. * @param { Element } node 需要提取的元素对象
  1236. * @example
  1237. * ```html
  1238. * <div id="parent">
  1239. * <div id="sub">
  1240. * <span id="child"></span>
  1241. * </div>
  1242. * </div>
  1243. *
  1244. * <script>
  1245. *
  1246. * var child = document.getElementById( "child" );
  1247. *
  1248. * //output: sub
  1249. * console.log( child.parentNode.id );
  1250. *
  1251. * UE.dom.domUtils.mergeToParent( child );
  1252. *
  1253. * //output: parent
  1254. * console.log( child.parentNode.id );
  1255. *
  1256. * </script>
  1257. * ```
  1258. */
  1259. mergeToParent: function(node) {
  1260. var parent = node.parentNode;
  1261. while (parent && dtd.$removeEmpty[parent.tagName]) {
  1262. if (parent.tagName == node.tagName || parent.tagName == "A") {
  1263. //针对a标签单独处理
  1264. domUtils.trimWhiteTextNode(parent);
  1265. //span需要特殊处理 不处理这样的情况 <span stlye="color:#fff">xxx<span style="color:#ccc">xxx</span>xxx</span>
  1266. if (
  1267. (parent.tagName == "SPAN" && !domUtils.isSameStyle(parent, node)) ||
  1268. (parent.tagName == "A" && node.tagName == "SPAN")
  1269. ) {
  1270. if (parent.childNodes.length > 1 || parent !== node.parentNode) {
  1271. node.style.cssText =
  1272. parent.style.cssText + ";" + node.style.cssText;
  1273. parent = parent.parentNode;
  1274. continue;
  1275. } else {
  1276. parent.style.cssText += ";" + node.style.cssText;
  1277. //trace:952 a标签要保持下划线
  1278. if (parent.tagName == "A") {
  1279. parent.style.textDecoration = "underline";
  1280. }
  1281. }
  1282. }
  1283. if (parent.tagName != "A") {
  1284. parent === node.parentNode && domUtils.remove(node, true);
  1285. break;
  1286. }
  1287. }
  1288. parent = parent.parentNode;
  1289. }
  1290. },
  1291. /**
  1292. * 合并节点node的左右兄弟节点
  1293. * @method mergeSibling
  1294. * @param { Element } node 需要合并的目标节点
  1295. * @example
  1296. * ```html
  1297. * <b>xxxx</b><b id="test">ooo</b><b>xxxx</b>
  1298. *
  1299. * <script>
  1300. * var demoNode = document.getElementById("test");
  1301. * UE.dom.domUtils.mergeSibling( demoNode );
  1302. * //output: xxxxoooxxxx
  1303. * console.log( demoNode.innerHTML );
  1304. * </script>
  1305. * ```
  1306. */
  1307. /**
  1308. * 合并节点node的左右兄弟节点, 可以根据给定的条件选择是否忽略合并左节点。
  1309. * @method mergeSibling
  1310. * @param { Element } node 需要合并的目标节点
  1311. * @param { Boolean } ignorePre 是否忽略合并左节点
  1312. * @example
  1313. * ```html
  1314. * <b>xxxx</b><b id="test">ooo</b><b>xxxx</b>
  1315. *
  1316. * <script>
  1317. * var demoNode = document.getElementById("test");
  1318. * UE.dom.domUtils.mergeSibling( demoNode, true );
  1319. * //output: oooxxxx
  1320. * console.log( demoNode.innerHTML );
  1321. * </script>
  1322. * ```
  1323. */
  1324. /**
  1325. * 合并节点node的左右兄弟节点,可以根据给定的条件选择是否忽略合并左右节点。
  1326. * @method mergeSibling
  1327. * @param { Element } node 需要合并的目标节点
  1328. * @param { Boolean } ignorePre 是否忽略合并左节点
  1329. * @param { Boolean } ignoreNext 是否忽略合并右节点
  1330. * @remind 如果同时忽略左右节点, 则该操作什么也不会做
  1331. * @example
  1332. * ```html
  1333. * <b>xxxx</b><b id="test">ooo</b><b>xxxx</b>
  1334. *
  1335. * <script>
  1336. * var demoNode = document.getElementById("test");
  1337. * UE.dom.domUtils.mergeSibling( demoNode, false, true );
  1338. * //output: xxxxooo
  1339. * console.log( demoNode.innerHTML );
  1340. * </script>
  1341. * ```
  1342. */
  1343. mergeSibling: function(node, ignorePre, ignoreNext) {
  1344. function merge(rtl, start, node) {
  1345. var next;
  1346. if (
  1347. (next = node[rtl]) &&
  1348. !domUtils.isBookmarkNode(next) &&
  1349. next.nodeType == 1 &&
  1350. domUtils.isSameElement(node, next)
  1351. ) {
  1352. while (next.firstChild) {
  1353. if (start == "firstChild") {
  1354. node.insertBefore(next.lastChild, node.firstChild);
  1355. } else {
  1356. node.appendChild(next.firstChild);
  1357. }
  1358. }
  1359. domUtils.remove(next);
  1360. }
  1361. }
  1362. !ignorePre && merge("previousSibling", "firstChild", node);
  1363. !ignoreNext && merge("nextSibling", "lastChild", node);
  1364. },
  1365. /**
  1366. * 设置节点node及其子节点不会被选中
  1367. * @method unSelectable
  1368. * @param { Element } node 需要执行操作的dom元素
  1369. * @remind 执行该操作后的节点, 将不能被鼠标选中
  1370. * @example
  1371. * ```javascript
  1372. * UE.dom.domUtils.unSelectable( document.body );
  1373. * ```
  1374. */
  1375. unSelectable: (ie && browser.ie9below) || browser.opera
  1376. ? function(node) {
  1377. //for ie9
  1378. node.onselectstart = function() {
  1379. return false;
  1380. };
  1381. node.onclick = node.onkeyup = node.onkeydown = function() {
  1382. return false;
  1383. };
  1384. node.unselectable = "on";
  1385. node.setAttribute("unselectable", "on");
  1386. for (var i = 0, ci; (ci = node.all[i++]); ) {
  1387. switch (ci.tagName.toLowerCase()) {
  1388. case "iframe":
  1389. case "textarea":
  1390. case "input":
  1391. case "select":
  1392. break;
  1393. default:
  1394. ci.unselectable = "on";
  1395. node.setAttribute("unselectable", "on");
  1396. }
  1397. }
  1398. }
  1399. : function(node) {
  1400. node.style.MozUserSelect = node.style.webkitUserSelect = node.style.msUserSelect = node.style.KhtmlUserSelect =
  1401. "none";
  1402. },
  1403. /**
  1404. * 删除节点node上的指定属性名称的属性
  1405. * @method removeAttributes
  1406. * @param { Node } node 需要删除属性的节点对象
  1407. * @param { String } attrNames 可以是空格隔开的多个属性名称,该操作将会依次删除相应的属性
  1408. * @example
  1409. * ```html
  1410. * <div id="wrap">
  1411. * <span style="font-size:14px;" id="test" name="followMe">xxxxx</span>
  1412. * </div>
  1413. *
  1414. * <script>
  1415. *
  1416. * UE.dom.domUtils.removeAttributes( document.getElementById( "test" ), "id name" );
  1417. *
  1418. * //output: <span style="font-size:14px;">xxxxx</span>
  1419. * console.log( document.getElementById("wrap").innerHTML );
  1420. *
  1421. * </script>
  1422. * ```
  1423. */
  1424. /**
  1425. * 删除节点node上的指定属性名称的属性
  1426. * @method removeAttributes
  1427. * @param { Node } node 需要删除属性的节点对象
  1428. * @param { Array } attrNames 需要删除的属性名数组
  1429. * @example
  1430. * ```html
  1431. * <div id="wrap">
  1432. * <span style="font-size:14px;" id="test" name="followMe">xxxxx</span>
  1433. * </div>
  1434. *
  1435. * <script>
  1436. *
  1437. * UE.dom.domUtils.removeAttributes( document.getElementById( "test" ), ["id", "name"] );
  1438. *
  1439. * //output: <span style="font-size:14px;">xxxxx</span>
  1440. * console.log( document.getElementById("wrap").innerHTML );
  1441. *
  1442. * </script>
  1443. * ```
  1444. */
  1445. removeAttributes: function(node, attrNames) {
  1446. attrNames = utils.isArray(attrNames)
  1447. ? attrNames
  1448. : utils.trim(attrNames).replace(/[ ]{2,}/g, " ").split(" ");
  1449. for (var i = 0, ci; (ci = attrNames[i++]); ) {
  1450. ci = attrFix[ci] || ci;
  1451. switch (ci) {
  1452. case "className":
  1453. node[ci] = "";
  1454. break;
  1455. case "style":
  1456. node.style.cssText = "";
  1457. var val = node.getAttributeNode("style");
  1458. !browser.ie && val && node.removeAttributeNode(val);
  1459. }
  1460. node.removeAttribute(ci);
  1461. }
  1462. },
  1463. /**
  1464. * 在doc下创建一个标签名为tag,属性为attrs的元素
  1465. * @method createElement
  1466. * @param { DomDocument } doc 新创建的元素属于该document节点创建
  1467. * @param { String } tagName 需要创建的元素的标签名
  1468. * @param { Object } attrs 新创建的元素的属性key-value集合
  1469. * @return { Element } 新创建的元素对象
  1470. * @example
  1471. * ```javascript
  1472. * var ele = UE.dom.domUtils.createElement( document, 'div', {
  1473. * id: 'test'
  1474. * } );
  1475. *
  1476. * //output: DIV
  1477. * console.log( ele.tagName );
  1478. *
  1479. * //output: test
  1480. * console.log( ele.id );
  1481. *
  1482. * ```
  1483. */
  1484. createElement: function(doc, tag, attrs) {
  1485. return domUtils.setAttributes(doc.createElement(tag), attrs);
  1486. },
  1487. /**
  1488. * 为节点node添加属性attrs,attrs为属性键值对
  1489. * @method setAttributes
  1490. * @param { Element } node 需要设置属性的元素对象
  1491. * @param { Object } attrs 需要设置的属性名-值对
  1492. * @return { Element } 设置属性的元素对象
  1493. * @example
  1494. * ```html
  1495. * <span id="test"></span>
  1496. *
  1497. * <script>
  1498. *
  1499. * var testNode = UE.dom.domUtils.setAttributes( document.getElementById( "test" ), {
  1500. * id: 'demo'
  1501. * } );
  1502. *
  1503. * //output: demo
  1504. * console.log( testNode.id );
  1505. *
  1506. * </script>
  1507. *
  1508. */
  1509. setAttributes: function(node, attrs) {
  1510. for (var attr in attrs) {
  1511. if (attrs.hasOwnProperty(attr)) {
  1512. var value = attrs[attr];
  1513. switch (attr) {
  1514. case "class":
  1515. //ie下要这样赋值,setAttribute不起作用
  1516. node.className = value;
  1517. break;
  1518. case "style":
  1519. node.style.cssText = node.style.cssText + ";" + value;
  1520. break;
  1521. case "innerHTML":
  1522. node[attr] = value;
  1523. break;
  1524. case "value":
  1525. node.value = value;
  1526. break;
  1527. default:
  1528. node.setAttribute(attrFix[attr] || attr, value);
  1529. }
  1530. }
  1531. }
  1532. return node;
  1533. },
  1534. /**
  1535. * 获取元素element经过计算后的样式值
  1536. * @method getComputedStyle
  1537. * @param { Element } element 需要获取样式的元素对象
  1538. * @param { String } styleName 需要获取的样式名
  1539. * @return { String } 获取到的样式值
  1540. * @example
  1541. * ```html
  1542. * <style type="text/css">
  1543. * #test {
  1544. * font-size: 15px;
  1545. * }
  1546. * </style>
  1547. *
  1548. * <span id="test"></span>
  1549. *
  1550. * <script>
  1551. * //output: 15px
  1552. * console.log( UE.dom.domUtils.getComputedStyle( document.getElementById( "test" ), 'font-size' ) );
  1553. * </script>
  1554. * ```
  1555. */
  1556. getComputedStyle: function(element, styleName) {
  1557. //一下的属性单独处理
  1558. var pros = "width height top left";
  1559. if (pros.indexOf(styleName) > -1) {
  1560. return (
  1561. element[
  1562. "offset" +
  1563. styleName.replace(/^\w/, function(s) {
  1564. return s.toUpperCase();
  1565. })
  1566. ] + "px"
  1567. );
  1568. }
  1569. //忽略文本节点
  1570. if (element.nodeType == 3) {
  1571. element = element.parentNode;
  1572. }
  1573. //ie下font-size若body下定义了font-size,则从currentStyle里会取到这个font-size. 取不到实际值,故此修改.
  1574. if (
  1575. browser.ie &&
  1576. browser.version < 9 &&
  1577. styleName == "font-size" &&
  1578. !element.style.fontSize &&
  1579. !dtd.$empty[element.tagName] &&
  1580. !dtd.$nonChild[element.tagName]
  1581. ) {
  1582. var span = element.ownerDocument.createElement("span");
  1583. span.style.cssText = "padding:0;border:0;font-family:simsun;";
  1584. span.innerHTML = ".";
  1585. element.appendChild(span);
  1586. var result = span.offsetHeight;
  1587. element.removeChild(span);
  1588. span = null;
  1589. return result + "px";
  1590. }
  1591. try {
  1592. var value =
  1593. domUtils.getStyle(element, styleName) ||
  1594. (window.getComputedStyle
  1595. ? domUtils
  1596. .getWindow(element)
  1597. .getComputedStyle(element, "")
  1598. .getPropertyValue(styleName)
  1599. : (element.currentStyle || element.style)[
  1600. utils.cssStyleToDomStyle(styleName)
  1601. ]);
  1602. } catch (e) {
  1603. return "";
  1604. }
  1605. return utils.transUnitToPx(utils.fixColor(styleName, value));
  1606. },
  1607. /**
  1608. * 删除元素element指定的className
  1609. * @method removeClasses
  1610. * @param { Element } ele 需要删除class的元素节点
  1611. * @param { String } classNames 需要删除的className, 多个className之间以空格分开
  1612. * @example
  1613. * ```html
  1614. * <span id="test" class="test1 test2 test3">xxx</span>
  1615. *
  1616. * <script>
  1617. *
  1618. * var testNode = document.getElementById( "test" );
  1619. * UE.dom.domUtils.removeClasses( testNode, "test1 test2" );
  1620. *
  1621. * //output: test3
  1622. * console.log( testNode.className );
  1623. *
  1624. * </script>
  1625. * ```
  1626. */
  1627. /**
  1628. * 删除元素element指定的className
  1629. * @method removeClasses
  1630. * @param { Element } ele 需要删除class的元素节点
  1631. * @param { Array } classNames 需要删除的className数组
  1632. * @example
  1633. * ```html
  1634. * <span id="test" class="test1 test2 test3">xxx</span>
  1635. *
  1636. * <script>
  1637. *
  1638. * var testNode = document.getElementById( "test" );
  1639. * UE.dom.domUtils.removeClasses( testNode, ["test1", "test2"] );
  1640. *
  1641. * //output: test3
  1642. * console.log( testNode.className );
  1643. *
  1644. * </script>
  1645. * ```
  1646. */
  1647. removeClasses: function(elm, classNames) {
  1648. classNames = utils.isArray(classNames)
  1649. ? classNames
  1650. : utils.trim(classNames).replace(/[ ]{2,}/g, " ").split(" ");
  1651. for (var i = 0, ci, cls = elm.className; (ci = classNames[i++]); ) {
  1652. cls = cls.replace(new RegExp("\\b" + ci + "\\b"), "");
  1653. }
  1654. cls = utils.trim(cls).replace(/[ ]{2,}/g, " ");
  1655. if (cls) {
  1656. elm.className = cls;
  1657. } else {
  1658. domUtils.removeAttributes(elm, ["class"]);
  1659. }
  1660. },
  1661. /**
  1662. * 给元素element添加className
  1663. * @method addClass
  1664. * @param { Node } ele 需要增加className的元素
  1665. * @param { String } classNames 需要添加的className, 多个className之间以空格分割
  1666. * @remind 相同的类名不会被重复添加
  1667. * @example
  1668. * ```html
  1669. * <span id="test" class="cls1 cls2"></span>
  1670. *
  1671. * <script>
  1672. * var testNode = document.getElementById("test");
  1673. *
  1674. * UE.dom.domUtils.addClass( testNode, "cls2 cls3 cls4" );
  1675. *
  1676. * //output: cl1 cls2 cls3 cls4
  1677. * console.log( testNode.className );
  1678. *
  1679. * <script>
  1680. * ```
  1681. */
  1682. /**
  1683. * 给元素element添加className
  1684. * @method addClass
  1685. * @param { Node } ele 需要增加className的元素
  1686. * @param { Array } classNames 需要添加的className的数组
  1687. * @remind 相同的类名不会被重复添加
  1688. * @example
  1689. * ```html
  1690. * <span id="test" class="cls1 cls2"></span>
  1691. *
  1692. * <script>
  1693. * var testNode = document.getElementById("test");
  1694. *
  1695. * UE.dom.domUtils.addClass( testNode, ["cls2", "cls3", "cls4"] );
  1696. *
  1697. * //output: cl1 cls2 cls3 cls4
  1698. * console.log( testNode.className );
  1699. *
  1700. * <script>
  1701. * ```
  1702. */
  1703. addClass: function(elm, classNames) {
  1704. if (!elm) return;
  1705. classNames = utils.trim(classNames).replace(/[ ]{2,}/g, " ").split(" ");
  1706. for (var i = 0, ci, cls = elm.className; (ci = classNames[i++]); ) {
  1707. if (!new RegExp("\\b" + ci + "\\b").test(cls)) {
  1708. cls += " " + ci;
  1709. }
  1710. }
  1711. elm.className = utils.trim(cls);
  1712. },
  1713. /**
  1714. * 判断元素element是否包含给定的样式类名className
  1715. * @method hasClass
  1716. * @param { Node } ele 需要检测的元素
  1717. * @param { String } classNames 需要检测的className, 多个className之间用空格分割
  1718. * @return { Boolean } 元素是否包含所有给定的className
  1719. * @example
  1720. * ```html
  1721. * <span id="test1" class="cls1 cls2"></span>
  1722. *
  1723. * <script>
  1724. * var test1 = document.getElementById("test1");
  1725. *
  1726. * //output: false
  1727. * console.log( UE.dom.domUtils.hasClass( test1, "cls2 cls1 cls3" ) );
  1728. *
  1729. * //output: true
  1730. * console.log( UE.dom.domUtils.hasClass( test1, "cls2 cls1" ) );
  1731. * </script>
  1732. * ```
  1733. */
  1734. /**
  1735. * 判断元素element是否包含给定的样式类名className
  1736. * @method hasClass
  1737. * @param { Node } ele 需要检测的元素
  1738. * @param { Array } classNames 需要检测的className数组
  1739. * @return { Boolean } 元素是否包含所有给定的className
  1740. * @example
  1741. * ```html
  1742. * <span id="test1" class="cls1 cls2"></span>
  1743. *
  1744. * <script>
  1745. * var test1 = document.getElementById("test1");
  1746. *
  1747. * //output: false
  1748. * console.log( UE.dom.domUtils.hasClass( test1, [ "cls2", "cls1", "cls3" ] ) );
  1749. *
  1750. * //output: true
  1751. * console.log( UE.dom.domUtils.hasClass( test1, [ "cls2", "cls1" ]) );
  1752. * </script>
  1753. * ```
  1754. */
  1755. hasClass: function(element, className) {
  1756. if (utils.isRegExp(className)) {
  1757. return className.test(element.className);
  1758. }
  1759. className = utils.trim(className).replace(/[ ]{2,}/g, " ").split(" ");
  1760. for (var i = 0, ci, cls = element.className; (ci = className[i++]); ) {
  1761. if (!new RegExp("\\b" + ci + "\\b", "i").test(cls)) {
  1762. return false;
  1763. }
  1764. }
  1765. return i - 1 == className.length;
  1766. },
  1767. /**
  1768. * 阻止事件默认行为
  1769. * @method preventDefault
  1770. * @param { Event } evt 需要阻止默认行为的事件对象
  1771. * @example
  1772. * ```javascript
  1773. * UE.dom.domUtils.preventDefault( evt );
  1774. * ```
  1775. */
  1776. preventDefault: function(evt) {
  1777. evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false);
  1778. },
  1779. /**
  1780. * 删除元素element指定的样式
  1781. * @method removeStyle
  1782. * @param { Element } element 需要删除样式的元素
  1783. * @param { String } styleName 需要删除的样式名
  1784. * @example
  1785. * ```html
  1786. * <span id="test" style="color: red; background: blue;"></span>
  1787. *
  1788. * <script>
  1789. *
  1790. * var testNode = document.getElementById("test");
  1791. *
  1792. * UE.dom.domUtils.removeStyle( testNode, 'color' );
  1793. *
  1794. * //output: background: blue;
  1795. * console.log( testNode.style.cssText );
  1796. *
  1797. * </script>
  1798. * ```
  1799. */
  1800. removeStyle: function(element, name) {
  1801. if (browser.ie) {
  1802. //针对color先单独处理一下
  1803. if (name == "color") {
  1804. name = "(^|;)" + name;
  1805. }
  1806. element.style.cssText = element.style.cssText.replace(
  1807. new RegExp(name + "[^:]*:[^;]+;?", "ig"),
  1808. ""
  1809. );
  1810. } else {
  1811. if (element.style.removeProperty) {
  1812. element.style.removeProperty(name);
  1813. } else {
  1814. element.style.removeAttribute(utils.cssStyleToDomStyle(name));
  1815. }
  1816. }
  1817. if (!element.style.cssText) {
  1818. domUtils.removeAttributes(element, ["style"]);
  1819. }
  1820. },
  1821. /**
  1822. * 获取元素element的style属性的指定值
  1823. * @method getStyle
  1824. * @param { Element } element 需要获取属性值的元素
  1825. * @param { String } styleName 需要获取的style的名称
  1826. * @warning 该方法仅获取元素style属性中所标明的值
  1827. * @return { String } 该元素包含指定的style属性值
  1828. * @example
  1829. * ```html
  1830. * <div id="test" style="color: red;"></div>
  1831. *
  1832. * <script>
  1833. *
  1834. * var testNode = document.getElementById( "test" );
  1835. *
  1836. * //output: red
  1837. * console.log( UE.dom.domUtils.getStyle( testNode, "color" ) );
  1838. *
  1839. * //output: ""
  1840. * console.log( UE.dom.domUtils.getStyle( testNode, "background" ) );
  1841. *
  1842. * </script>
  1843. * ```
  1844. */
  1845. getStyle: function(element, name) {
  1846. var value = element.style[utils.cssStyleToDomStyle(name)];
  1847. return utils.fixColor(name, value);
  1848. },
  1849. /**
  1850. * 为元素element设置样式属性值
  1851. * @method setStyle
  1852. * @param { Element } element 需要设置样式的元素
  1853. * @param { String } styleName 样式名
  1854. * @param { String } styleValue 样式值
  1855. * @example
  1856. * ```html
  1857. * <div id="test"></div>
  1858. *
  1859. * <script>
  1860. *
  1861. * var testNode = document.getElementById( "test" );
  1862. *
  1863. * //output: ""
  1864. * console.log( testNode.style.color );
  1865. *
  1866. * UE.dom.domUtils.setStyle( testNode, 'color', 'red' );
  1867. * //output: "red"
  1868. * console.log( testNode.style.color );
  1869. *
  1870. * </script>
  1871. * ```
  1872. */
  1873. setStyle: function(element, name, value) {
  1874. element.style[utils.cssStyleToDomStyle(name)] = value;
  1875. if (!utils.trim(element.style.cssText)) {
  1876. this.removeAttributes(element, "style");
  1877. }
  1878. },
  1879. /**
  1880. * 为元素element设置多个样式属性值
  1881. * @method setStyles
  1882. * @param { Element } element 需要设置样式的元素
  1883. * @param { Object } styles 样式名值对
  1884. * @example
  1885. * ```html
  1886. * <div id="test"></div>
  1887. *
  1888. * <script>
  1889. *
  1890. * var testNode = document.getElementById( "test" );
  1891. *
  1892. * //output: ""
  1893. * console.log( testNode.style.color );
  1894. *
  1895. * UE.dom.domUtils.setStyles( testNode, {
  1896. * 'color': 'red'
  1897. * } );
  1898. * //output: "red"
  1899. * console.log( testNode.style.color );
  1900. *
  1901. * </script>
  1902. * ```
  1903. */
  1904. setStyles: function(element, styles) {
  1905. for (var name in styles) {
  1906. if (styles.hasOwnProperty(name)) {
  1907. domUtils.setStyle(element, name, styles[name]);
  1908. }
  1909. }
  1910. },
  1911. /**
  1912. * 删除_moz_dirty属性
  1913. * @private
  1914. * @method removeDirtyAttr
  1915. */
  1916. removeDirtyAttr: function(node) {
  1917. for (
  1918. var i = 0, ci, nodes = node.getElementsByTagName("*");
  1919. (ci = nodes[i++]);
  1920. ) {
  1921. ci.removeAttribute("_moz_dirty");
  1922. }
  1923. node.removeAttribute("_moz_dirty");
  1924. },
  1925. /**
  1926. * 获取子节点的数量
  1927. * @method getChildCount
  1928. * @param { Element } node 需要检测的元素
  1929. * @return { Number } 给定的node元素的子节点数量
  1930. * @example
  1931. * ```html
  1932. * <div id="test">
  1933. * <span></span>
  1934. * </div>
  1935. *
  1936. * <script>
  1937. *
  1938. * //output: 3
  1939. * console.log( UE.dom.domUtils.getChildCount( document.getElementById("test") ) );
  1940. *
  1941. * </script>
  1942. * ```
  1943. */
  1944. /**
  1945. * 根据给定的过滤规则, 获取符合条件的子节点的数量
  1946. * @method getChildCount
  1947. * @param { Element } node 需要检测的元素
  1948. * @param { Function } fn 过滤器, 要求对符合条件的子节点返回true, 反之则要求返回false
  1949. * @return { Number } 符合过滤条件的node元素的子节点数量
  1950. * @example
  1951. * ```html
  1952. * <div id="test">
  1953. * <span></span>
  1954. * </div>
  1955. *
  1956. * <script>
  1957. *
  1958. * //output: 1
  1959. * console.log( UE.dom.domUtils.getChildCount( document.getElementById("test"), function ( node ) {
  1960. *
  1961. * return node.nodeType === 1;
  1962. *
  1963. * } ) );
  1964. *
  1965. * </script>
  1966. * ```
  1967. */
  1968. getChildCount: function(node, fn) {
  1969. var count = 0,
  1970. first = node.firstChild;
  1971. fn =
  1972. fn ||
  1973. function() {
  1974. return 1;
  1975. };
  1976. while (first) {
  1977. if (fn(first)) {
  1978. count++;
  1979. }
  1980. first = first.nextSibling;
  1981. }
  1982. return count;
  1983. },
  1984. /**
  1985. * 判断给定节点是否为空节点
  1986. * @method isEmptyNode
  1987. * @param { Node } node 需要检测的节点对象
  1988. * @return { Boolean } 节点是否为空
  1989. * @example
  1990. * ```javascript
  1991. * UE.dom.domUtils.isEmptyNode( document.body );
  1992. * ```
  1993. */
  1994. isEmptyNode: function(node) {
  1995. return (
  1996. !node.firstChild ||
  1997. domUtils.getChildCount(node, function(node) {
  1998. return (
  1999. !domUtils.isBr(node) &&
  2000. !domUtils.isBookmarkNode(node) &&
  2001. !domUtils.isWhitespace(node)
  2002. );
  2003. }) == 0
  2004. );
  2005. },
  2006. clearSelectedArr: function(nodes) {
  2007. var node;
  2008. while ((node = nodes.pop())) {
  2009. domUtils.removeAttributes(node, ["class"]);
  2010. }
  2011. },
  2012. /**
  2013. * 将显示区域滚动到指定节点的位置
  2014. * @method scrollToView
  2015. * @param {Node} node 节点
  2016. * @param {window} win window对象
  2017. * @param {Number} offsetTop 距离上方的偏移量
  2018. */
  2019. scrollToView: function(node, win, offsetTop) {
  2020. var getViewPaneSize = function() {
  2021. var doc = win.document,
  2022. mode = doc.compatMode == "CSS1Compat";
  2023. return {
  2024. width:
  2025. (mode ? doc.documentElement.clientWidth : doc.body.clientWidth) || 0,
  2026. height:
  2027. (mode ? doc.documentElement.clientHeight : doc.body.clientHeight) || 0
  2028. };
  2029. },
  2030. getScrollPosition = function(win) {
  2031. if ("pageXOffset" in win) {
  2032. return {
  2033. x: win.pageXOffset || 0,
  2034. y: win.pageYOffset || 0
  2035. };
  2036. } else {
  2037. var doc = win.document;
  2038. return {
  2039. x: doc.documentElement.scrollLeft || doc.body.scrollLeft || 0,
  2040. y: doc.documentElement.scrollTop || doc.body.scrollTop || 0
  2041. };
  2042. }
  2043. };
  2044. var winHeight = getViewPaneSize().height,
  2045. offset = winHeight * -1 + offsetTop;
  2046. offset += node.offsetHeight || 0;
  2047. var elementPosition = domUtils.getXY(node);
  2048. offset += elementPosition.y;
  2049. var currentScroll = getScrollPosition(win).y;
  2050. // offset += 50;
  2051. if (offset > currentScroll || offset < currentScroll - winHeight) {
  2052. win.scrollTo(0, offset + (offset < 0 ? -20 : 20));
  2053. }
  2054. },
  2055. /**
  2056. * 判断给定节点是否为br
  2057. * @method isBr
  2058. * @param { Node } node 需要判断的节点对象
  2059. * @return { Boolean } 给定的节点是否是br节点
  2060. */
  2061. isBr: function(node) {
  2062. return node.nodeType == 1 && node.tagName == "BR";
  2063. },
  2064. /**
  2065. * 判断给定的节点是否是一个“填充”节点
  2066. * @private
  2067. * @method isFillChar
  2068. * @param { Node } node 需要判断的节点
  2069. * @param { Boolean } isInStart 是否从节点内容的开始位置匹配
  2070. * @returns { Boolean } 节点是否是填充节点
  2071. */
  2072. isFillChar: function(node, isInStart) {
  2073. if (node.nodeType != 3) return false;
  2074. var text = node.nodeValue;
  2075. if (isInStart) {
  2076. return new RegExp("^" + domUtils.fillChar).test(text);
  2077. }
  2078. return !text.replace(new RegExp(domUtils.fillChar, "g"), "").length;
  2079. },
  2080. isStartInblock: function(range) {
  2081. var tmpRange = range.cloneRange(),
  2082. flag = 0,
  2083. start = tmpRange.startContainer,
  2084. tmp;
  2085. if (start.nodeType == 1 && start.childNodes[tmpRange.startOffset]) {
  2086. start = start.childNodes[tmpRange.startOffset];
  2087. var pre = start.previousSibling;
  2088. while (pre && domUtils.isFillChar(pre)) {
  2089. start = pre;
  2090. pre = pre.previousSibling;
  2091. }
  2092. }
  2093. if (this.isFillChar(start, true) && tmpRange.startOffset == 1) {
  2094. tmpRange.setStartBefore(start);
  2095. start = tmpRange.startContainer;
  2096. }
  2097. while (start && domUtils.isFillChar(start)) {
  2098. tmp = start;
  2099. start = start.previousSibling;
  2100. }
  2101. if (tmp) {
  2102. tmpRange.setStartBefore(tmp);
  2103. start = tmpRange.startContainer;
  2104. }
  2105. if (
  2106. start.nodeType == 1 &&
  2107. domUtils.isEmptyNode(start) &&
  2108. tmpRange.startOffset == 1
  2109. ) {
  2110. tmpRange.setStart(start, 0).collapse(true);
  2111. }
  2112. while (!tmpRange.startOffset) {
  2113. start = tmpRange.startContainer;
  2114. if (domUtils.isBlockElm(start) || domUtils.isBody(start)) {
  2115. flag = 1;
  2116. break;
  2117. }
  2118. var pre = tmpRange.startContainer.previousSibling,
  2119. tmpNode;
  2120. if (!pre) {
  2121. tmpRange.setStartBefore(tmpRange.startContainer);
  2122. } else {
  2123. while (pre && domUtils.isFillChar(pre)) {
  2124. tmpNode = pre;
  2125. pre = pre.previousSibling;
  2126. }
  2127. if (tmpNode) {
  2128. tmpRange.setStartBefore(tmpNode);
  2129. } else {
  2130. tmpRange.setStartBefore(tmpRange.startContainer);
  2131. }
  2132. }
  2133. }
  2134. return flag && !domUtils.isBody(tmpRange.startContainer) ? 1 : 0;
  2135. },
  2136. /**
  2137. * 判断给定的元素是否是一个空元素
  2138. * @method isEmptyBlock
  2139. * @param { Element } node 需要判断的元素
  2140. * @return { Boolean } 是否是空元素
  2141. * @example
  2142. * ```html
  2143. * <div id="test"></div>
  2144. *
  2145. * <script>
  2146. * //output: true
  2147. * console.log( UE.dom.domUtils.isEmptyBlock( document.getElementById("test") ) );
  2148. * </script>
  2149. * ```
  2150. */
  2151. /**
  2152. * 根据指定的判断规则判断给定的元素是否是一个空元素
  2153. * @method isEmptyBlock
  2154. * @param { Element } node 需要判断的元素
  2155. * @param { RegExp } reg 对内容执行判断的正则表达式对象
  2156. * @return { Boolean } 是否是空元素
  2157. */
  2158. isEmptyBlock: function(node, reg) {
  2159. if (node.nodeType != 1) return 0;
  2160. reg = reg || new RegExp("[ \xa0\t\r\n" + domUtils.fillChar + "]", "g");
  2161. if (
  2162. node[browser.ie ? "innerText" : "textContent"].replace(reg, "").length > 0
  2163. ) {
  2164. return 0;
  2165. }
  2166. for (var n in dtd.$isNotEmpty) {
  2167. if (node.getElementsByTagName(n).length) {
  2168. return 0;
  2169. }
  2170. }
  2171. return 1;
  2172. },
  2173. /**
  2174. * 移动元素使得该元素的位置移动指定的偏移量的距离
  2175. * @method setViewportOffset
  2176. * @param { Element } element 需要设置偏移量的元素
  2177. * @param { Object } offset 偏移量, 形如{ left: 100, top: 50 }的一个键值对, 表示该元素将在
  2178. * 现有的位置上向水平方向偏移offset.left的距离, 在竖直方向上偏移
  2179. * offset.top的距离
  2180. * @example
  2181. * ```html
  2182. * <div id="test" style="top: 100px; left: 50px; position: absolute;"></div>
  2183. *
  2184. * <script>
  2185. *
  2186. * var testNode = document.getElementById("test");
  2187. *
  2188. * UE.dom.domUtils.setViewportOffset( testNode, {
  2189. * left: 200,
  2190. * top: 50
  2191. * } );
  2192. *
  2193. * //output: top: 300px; left: 100px; position: absolute;
  2194. * console.log( testNode.style.cssText );
  2195. *
  2196. * </script>
  2197. * ```
  2198. */
  2199. setViewportOffset: function(element, offset) {
  2200. var left = parseInt(element.style.left) | 0;
  2201. var top = parseInt(element.style.top) | 0;
  2202. var rect = element.getBoundingClientRect();
  2203. var offsetLeft = offset.left - rect.left;
  2204. var offsetTop = offset.top - rect.top;
  2205. if (offsetLeft) {
  2206. element.style.left = left + offsetLeft + "px";
  2207. }
  2208. if (offsetTop) {
  2209. element.style.top = top + offsetTop + "px";
  2210. }
  2211. },
  2212. /**
  2213. * 用“填充字符”填充节点
  2214. * @method fillNode
  2215. * @private
  2216. * @param { DomDocument } doc 填充的节点所在的docment对象
  2217. * @param { Node } node 需要填充的节点对象
  2218. * @example
  2219. * ```html
  2220. * <div id="test"></div>
  2221. *
  2222. * <script>
  2223. * var testNode = document.getElementById("test");
  2224. *
  2225. * //output: 0
  2226. * console.log( testNode.childNodes.length );
  2227. *
  2228. * UE.dom.domUtils.fillNode( document, testNode );
  2229. *
  2230. * //output: 1
  2231. * console.log( testNode.childNodes.length );
  2232. *
  2233. * </script>
  2234. * ```
  2235. */
  2236. fillNode: function(doc, node) {
  2237. var tmpNode = browser.ie
  2238. ? doc.createTextNode(domUtils.fillChar)
  2239. : doc.createElement("br");
  2240. node.innerHTML = "";
  2241. node.appendChild(tmpNode);
  2242. },
  2243. /**
  2244. * 把节点src的所有子节点追加到另一个节点tag上去
  2245. * @method moveChild
  2246. * @param { Node } src 源节点, 该节点下的所有子节点将被移除
  2247. * @param { Node } tag 目标节点, 从源节点移除的子节点将被追加到该节点下
  2248. * @example
  2249. * ```html
  2250. * <div id="test1">
  2251. * <span></span>
  2252. * </div>
  2253. * <div id="test2">
  2254. * <div></div>
  2255. * </div>
  2256. *
  2257. * <script>
  2258. *
  2259. * var test1 = document.getElementById("test1"),
  2260. * test2 = document.getElementById("test2");
  2261. *
  2262. * UE.dom.domUtils.moveChild( test1, test2 );
  2263. *
  2264. * //output: ""(空字符串)
  2265. * console.log( test1.innerHTML );
  2266. *
  2267. * //output: "<div></div><span></span>"
  2268. * console.log( test2.innerHTML );
  2269. *
  2270. * </script>
  2271. * ```
  2272. */
  2273. /**
  2274. * 把节点src的所有子节点移动到另一个节点tag上去, 可以通过dir参数控制附加的行为是“追加”还是“插入顶部”
  2275. * @method moveChild
  2276. * @param { Node } src 源节点, 该节点下的所有子节点将被移除
  2277. * @param { Node } tag 目标节点, 从源节点移除的子节点将被附加到该节点下
  2278. * @param { Boolean } dir 附加方式, 如果为true, 则附加进去的节点将被放到目标节点的顶部, 反之,则放到末尾
  2279. * @example
  2280. * ```html
  2281. * <div id="test1">
  2282. * <span></span>
  2283. * </div>
  2284. * <div id="test2">
  2285. * <div></div>
  2286. * </div>
  2287. *
  2288. * <script>
  2289. *
  2290. * var test1 = document.getElementById("test1"),
  2291. * test2 = document.getElementById("test2");
  2292. *
  2293. * UE.dom.domUtils.moveChild( test1, test2, true );
  2294. *
  2295. * //output: ""(空字符串)
  2296. * console.log( test1.innerHTML );
  2297. *
  2298. * //output: "<span></span><div></div>"
  2299. * console.log( test2.innerHTML );
  2300. *
  2301. * </script>
  2302. * ```
  2303. */
  2304. moveChild: function(src, tag, dir) {
  2305. while (src.firstChild) {
  2306. if (dir && tag.firstChild) {
  2307. tag.insertBefore(src.lastChild, tag.firstChild);
  2308. } else {
  2309. tag.appendChild(src.firstChild);
  2310. }
  2311. }
  2312. },
  2313. /**
  2314. * 判断节点的标签上是否不存在任何属性
  2315. * @method hasNoAttributes
  2316. * @private
  2317. * @param { Node } node 需要检测的节点对象
  2318. * @return { Boolean } 节点是否不包含任何属性
  2319. * @example
  2320. * ```html
  2321. * <div id="test"><span>xxxx</span></div>
  2322. *
  2323. * <script>
  2324. *
  2325. * //output: false
  2326. * console.log( UE.dom.domUtils.hasNoAttributes( document.getElementById("test") ) );
  2327. *
  2328. * //output: true
  2329. * console.log( UE.dom.domUtils.hasNoAttributes( document.getElementById("test").firstChild ) );
  2330. *
  2331. * </script>
  2332. * ```
  2333. */
  2334. hasNoAttributes: function(node) {
  2335. return browser.ie
  2336. ? /^<\w+\s*?>/.test(node.outerHTML)
  2337. : node.attributes.length == 0;
  2338. },
  2339. /**
  2340. * 检测节点是否是UEditor所使用的辅助节点
  2341. * @method isCustomeNode
  2342. * @private
  2343. * @param { Node } node 需要检测的节点
  2344. * @remind 辅助节点是指编辑器要完成工作临时添加的节点, 在输出的时候将会从编辑器内移除, 不会影响最终的结果。
  2345. * @return { Boolean } 给定的节点是否是一个辅助节点
  2346. */
  2347. isCustomeNode: function(node) {
  2348. return node.nodeType == 1 && node.getAttribute("_ue_custom_node_");
  2349. },
  2350. /**
  2351. * 检测节点的标签是否是给定的标签
  2352. * @method isTagNode
  2353. * @param { Node } node 需要检测的节点对象
  2354. * @param { String } tagName 标签
  2355. * @return { Boolean } 节点的标签是否是给定的标签
  2356. * @example
  2357. * ```html
  2358. * <div id="test"></div>
  2359. *
  2360. * <script>
  2361. *
  2362. * //output: true
  2363. * console.log( UE.dom.domUtils.isTagNode( document.getElementById("test"), "div" ) );
  2364. *
  2365. * </script>
  2366. * ```
  2367. */
  2368. isTagNode: function(node, tagNames) {
  2369. return (
  2370. node.nodeType == 1 &&
  2371. new RegExp("\\b" + node.tagName + "\\b", "i").test(tagNames)
  2372. );
  2373. },
  2374. /**
  2375. * 给定一个节点数组,在通过指定的过滤器过滤后, 获取其中满足过滤条件的第一个节点
  2376. * @method filterNodeList
  2377. * @param { Array } nodeList 需要过滤的节点数组
  2378. * @param { Function } fn 过滤器, 对符合条件的节点, 执行结果返回true, 反之则返回false
  2379. * @return { Node | NULL } 如果找到符合过滤条件的节点, 则返回该节点, 否则返回NULL
  2380. * @example
  2381. * ```javascript
  2382. * var divNodes = document.getElementsByTagName("div");
  2383. * divNodes = [].slice.call( divNodes, 0 );
  2384. *
  2385. * //output: null
  2386. * console.log( UE.dom.domUtils.filterNodeList( divNodes, function ( node ) {
  2387. * return node.tagName.toLowerCase() !== 'div';
  2388. * } ) );
  2389. * ```
  2390. */
  2391. /**
  2392. * 给定一个节点数组nodeList和一组标签名tagNames, 获取其中能够匹配标签名的节点集合中的第一个节点
  2393. * @method filterNodeList
  2394. * @param { Array } nodeList 需要过滤的节点数组
  2395. * @param { String } tagNames 需要匹配的标签名, 多个标签名之间用空格分割
  2396. * @return { Node | NULL } 如果找到标签名匹配的节点, 则返回该节点, 否则返回NULL
  2397. * @example
  2398. * ```javascript
  2399. * var divNodes = document.getElementsByTagName("div");
  2400. * divNodes = [].slice.call( divNodes, 0 );
  2401. *
  2402. * //output: null
  2403. * console.log( UE.dom.domUtils.filterNodeList( divNodes, 'a span' ) );
  2404. * ```
  2405. */
  2406. /**
  2407. * 给定一个节点数组,在通过指定的过滤器过滤后, 如果参数forAll为true, 则会返回所有满足过滤
  2408. * 条件的节点集合, 否则, 返回满足条件的节点集合中的第一个节点
  2409. * @method filterNodeList
  2410. * @param { Array } nodeList 需要过滤的节点数组
  2411. * @param { Function } fn 过滤器, 对符合条件的节点, 执行结果返回true, 反之则返回false
  2412. * @param { Boolean } forAll 是否返回整个节点数组, 如果该参数为false, 则返回节点集合中的第一个节点
  2413. * @return { Array | Node | NULL } 如果找到符合过滤条件的节点, 则根据参数forAll的值决定返回满足
  2414. * 过滤条件的节点数组或第一个节点, 否则返回NULL
  2415. * @example
  2416. * ```javascript
  2417. * var divNodes = document.getElementsByTagName("div");
  2418. * divNodes = [].slice.call( divNodes, 0 );
  2419. *
  2420. * //output: 3(假定有3个div)
  2421. * console.log( divNodes.length );
  2422. *
  2423. * var nodes = UE.dom.domUtils.filterNodeList( divNodes, function ( node ) {
  2424. * return node.tagName.toLowerCase() === 'div';
  2425. * }, true );
  2426. *
  2427. * //output: 3
  2428. * console.log( nodes.length );
  2429. *
  2430. * var node = UE.dom.domUtils.filterNodeList( divNodes, function ( node ) {
  2431. * return node.tagName.toLowerCase() === 'div';
  2432. * }, false );
  2433. *
  2434. * //output: div
  2435. * console.log( node.nodeName );
  2436. * ```
  2437. */
  2438. filterNodeList: function(nodelist, filter, forAll) {
  2439. var results = [];
  2440. if (!utils.isFunction(filter)) {
  2441. var str = filter;
  2442. filter = function(n) {
  2443. return (
  2444. utils.indexOf(
  2445. utils.isArray(str) ? str : str.split(" "),
  2446. n.tagName.toLowerCase()
  2447. ) != -1
  2448. );
  2449. };
  2450. }
  2451. utils.each(nodelist, function(n) {
  2452. filter(n) && results.push(n);
  2453. });
  2454. return results.length == 0
  2455. ? null
  2456. : results.length == 1 || !forAll ? results[0] : results;
  2457. },
  2458. /**
  2459. * 查询给定的range选区是否在给定的node节点内,且在该节点的最末尾
  2460. * @method isInNodeEndBoundary
  2461. * @param { UE.dom.Range } rng 需要判断的range对象, 该对象的startContainer不能为NULL
  2462. * @param node 需要检测的节点对象
  2463. * @return { Number } 如果给定的选取range对象是在node内部的最末端, 则返回1, 否则返回0
  2464. */
  2465. isInNodeEndBoundary: function(rng, node) {
  2466. var start = rng.startContainer;
  2467. if (start.nodeType == 3 && rng.startOffset != start.nodeValue.length) {
  2468. return 0;
  2469. }
  2470. if (start.nodeType == 1 && rng.startOffset != start.childNodes.length) {
  2471. return 0;
  2472. }
  2473. while (start !== node) {
  2474. if (start.nextSibling) {
  2475. return 0;
  2476. }
  2477. start = start.parentNode;
  2478. }
  2479. return 1;
  2480. },
  2481. isBoundaryNode: function(node, dir) {
  2482. var tmp;
  2483. while (!domUtils.isBody(node)) {
  2484. tmp = node;
  2485. node = node.parentNode;
  2486. if (tmp !== node[dir]) {
  2487. return false;
  2488. }
  2489. }
  2490. return true;
  2491. },
  2492. fillHtml: browser.ie11below ? "&nbsp;" : "<br/>"
  2493. });
  2494. var fillCharReg = new RegExp(domUtils.fillChar, "g");