DOMUtils

使用js重新对jQuery的部分函数进行了仿写

目前为 2024-01-07 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.cn-greasyfork.org/scripts/465772/1307582/DOMUtils.js

  1. /// <reference path="../库.ts/DOMUtils.d.ts" />
  2.  
  3. /**
  4. * 自己常用的元素工具类
  5. * @copyright GPL-3.0-only
  6. * @author WhiteSev
  7. **/
  8. (function (global, factory) {
  9. /**
  10. * 不使用define
  11. * typeof define === "function" && define.amd
  12. * define(factory)
  13. */
  14. if (typeof exports === "object" && typeof module !== "undefined") {
  15. /* 适用于NodeJs或typeScript */
  16. module.exports = factory();
  17. } else {
  18. global = typeof globalThis !== "undefined" ? globalThis : global || self;
  19. /* 适用于浏览器中,且this对象是window,如果this是其它,那么会在其它对象下注册对象 */
  20. global.DOMUtils = factory(global.DOMUtils);
  21. }
  22. })(typeof window !== "undefined" ? window : this, function (AnotherDOMUtils) {
  23. const DOMUtils = {};
  24. /**
  25. * @type {string} 元素工具类的版本
  26. */
  27. DOMUtils.version = "2024-1-7";
  28.  
  29. let CommonUtils = {
  30. /**
  31. * 判断元素是否已显示或已连接
  32. * @param {HTMLElement} element
  33. * @returns {boolean}
  34. */
  35. isShow(element) {
  36. return Boolean(element.getClientRects().length);
  37. },
  38. /**
  39. * 用于显示元素并获取它的高度宽度等其它属性
  40. * @param {HTMLElement} element
  41. * @returns {{recovery: Function}} - 恢复
  42. */
  43. showElement(element) {
  44. let oldStyleAttribute = element.getAttribute("style");
  45. element.setAttribute(
  46. "style",
  47. "visibility: !important;display:block !important;"
  48. );
  49. return {
  50. recovery() {
  51. if (oldStyleAttribute) {
  52. element.setAttribute("style", oldStyleAttribute);
  53. } else {
  54. element.removeAttribute("style");
  55. }
  56. },
  57. };
  58. },
  59. /**
  60. * 获取元素上的Float格式的属性px
  61. * @param {HTMLElement|CSSStyleDeclaration} element
  62. * @param {string} styleName style名
  63. * @return {number}
  64. */
  65. getStyleValue(element, styleName) {
  66. let view = null;
  67. let styles = null;
  68. if (element instanceof CSSStyleDeclaration) {
  69. /* 直接就获取了style属性 */
  70. styles = element;
  71. } else {
  72. view = element.ownerDocument.defaultView;
  73. if (!view || !view.opener) {
  74. view = window;
  75. }
  76. styles = view.getComputedStyle(element);
  77. }
  78. let value = parseFloat(styles[styleName]);
  79. if (isNaN(value)) {
  80. return 0;
  81. } else {
  82. return value;
  83. }
  84. },
  85. /**
  86. * 判断是否是window,例如window、self、globalThis
  87. * @param {any} target
  88. * @returns {boolean}
  89. */
  90. isWin(target) {
  91. if (!typeof target === "object") {
  92. return false;
  93. }
  94. if (target instanceof Node) {
  95. return false;
  96. }
  97. if (target === globalThis) {
  98. return true;
  99. }
  100. if (target === window) {
  101. return true;
  102. }
  103. if (target === self) {
  104. return true;
  105. }
  106. if (target?.Math?.toString() !== "[object Math]") {
  107. return false;
  108. }
  109. return true;
  110. },
  111. };
  112. /**
  113. * 获取或设置元素的属性值
  114. * @param {HTMLElement|string} element 目标元素
  115. * @param {string} attrName 属性名
  116. * @param {string} [attrValue] 属性值(可选)
  117. * @returns {?string} 如果传入了attrValue,则返回undefined;否则返回属性值
  118. * @example
  119. * // 获取a.xx元素的href属性
  120. * DOMUtils.attr(document.querySelector("a.xx"),"href");
  121. * DOMUtils.attr("a.xx","href");
  122. * > https://xxxx....
  123. * @example
  124. * // 修改a.xx元素的href属性为abcd
  125. * DOMUtils.attr(document.querySelector("a.xx"),"href","abcd");
  126. * DOMUtils.attr("a.xx","href","abcd");
  127. * */
  128. DOMUtils.attr = function (element, attrName, attrValue) {
  129. if (typeof element === "string") {
  130. element = document.querySelector(element);
  131. }
  132. if (element == void 0) {
  133. return;
  134. }
  135. if (attrValue === void 0) {
  136. return element.getAttribute(attrName);
  137. } else {
  138. element.setAttribute(attrName, attrValue);
  139. }
  140. };
  141.  
  142. /**
  143. * 创建元素
  144. * @param {keyof HTMLElementTagNameMap} tagName 元素类型
  145. * @param {HTMLElement|undefined} property 元素属性,对已有元素上的属性或者自定义的属性赋值
  146. * @param {object|undefined} attributes 元素自定义属性,通过setAttribute赋值
  147. * @returns {HTMLElement}
  148. * @example
  149. * // 创建一个DIV元素,且属性class为xxx
  150. * DOMUtils.createElement("div",undefined,{ class:"xxx" });
  151. * > <div class="xxx"></div>
  152. * @example
  153. * // 创建一个DIV元素
  154. * DOMUtils.createElement("div");
  155. * > <div></div>
  156. * @example
  157. * // 创建一个DIV元素
  158. * DOMUtils.createElement("div","测试");
  159. * > <div>测试</div>
  160. */
  161. DOMUtils.createElement = function (tagName, property, attributes) {
  162. let tempElement = document.createElement(tagName);
  163. if (typeof property === "string") {
  164. tempElement.innerHTML = property;
  165. return tempElement;
  166. }
  167. if (property == void 0) {
  168. property = {};
  169. }
  170. if (attributes == void 0) {
  171. attributes = {};
  172. }
  173. Object.keys(property).forEach((key) => {
  174. let value = property[key];
  175. tempElement[key] = value;
  176. });
  177. Object.keys(attributes).forEach((key) => {
  178. let value = attributes[key];
  179. if (typeof value === "object") {
  180. /* object转字符串 */
  181. value = JSON.stringify(value);
  182. } else if (typeof value === "function") {
  183. /* function转字符串 */
  184. value = value.toString();
  185. }
  186. tempElement.setAttribute(key, value);
  187. });
  188. return tempElement;
  189. };
  190.  
  191. /**
  192. * 获取或设置元素的样式属性值
  193. * @param {HTMLElement|string} element 目标元素
  194. * @param {CSSStyleDeclaration|string} property 样式属性名或包含多个属性名和属性值的对象
  195. * @param {any} [value] 样式属性值(可选)
  196. * @returns {?string} 如果传入了value,则返回undefined;否则返回样式属性值
  197. * @example
  198. * // 获取元素a.xx的CSS属性display
  199. * DOMUtils.css(document.querySelector("a.xx"),"display");
  200. * DOMUtils.css("a.xx","display");
  201. * > "none"
  202. * @example
  203. * // 设置元素a.xx的CSS属性display为block
  204. * DOMUtils.css(document.querySelector("a.xx"),"display","block");
  205. * DOMUtils.css(document.querySelector("a.xx"),"display","block !important");
  206. * DOMUtils.css(document.querySelector("a.xx"),{ display: "block" }});
  207. * DOMUtils.css(document.querySelector("a.xx"),{ display: "block !important" }});
  208. * DOMUtils.css("a.xx","display","block");
  209. * DOMUtils.css("a.xx","display","block !important");
  210. * @example
  211. * // 设置元素a.xx的CSS属性top为10px
  212. * DOMUtils.css(document.querySelector("a.xx"),"top","10px");
  213. * DOMUtils.css(document.querySelector("a.xx"),"top",10);
  214. * DOMUtils.css(document.querySelector("a.xx"),{ top: "10px" });
  215. * DOMUtils.css(document.querySelector("a.xx"),{ top: 10 });
  216. * */
  217. DOMUtils.css = function (element, property, value) {
  218. /**
  219. * 把纯数字没有px的加上
  220. */
  221. function handlePixe(propertyName, propertyValue) {
  222. let allowAddPixe = [
  223. "width",
  224. "height",
  225. "top",
  226. "left",
  227. "right",
  228. "bottom",
  229. "font-size",
  230. ];
  231. if (typeof propertyValue === "number") {
  232. propertyValue = propertyValue.toString();
  233. }
  234. if (
  235. typeof propertyValue === "string" &&
  236. allowAddPixe.includes(propertyName) &&
  237. propertyValue.match(/[0-9]$/gi)
  238. ) {
  239. propertyValue = propertyValue + "px";
  240. }
  241. return propertyValue;
  242. }
  243. if (typeof element === "string") {
  244. element = document.querySelector(element);
  245. }
  246. if (element == void 0) {
  247. return;
  248. }
  249. if (typeof property === "string") {
  250. if (value === void 0) {
  251. return getComputedStyle(element).getPropertyValue(property);
  252. } else {
  253. if (value === "string" && value.includes("!important")) {
  254. element.style.setProperty(property, value, "important");
  255. } else {
  256. value = handlePixe(property, value);
  257. element.style.setProperty(property, value);
  258. }
  259. }
  260. } else if (typeof property === "object") {
  261. for (let prop in property) {
  262. if (
  263. typeof property[prop] === "string" &&
  264. property[prop].includes("!important")
  265. ) {
  266. element.style.setProperty(prop, property[prop], "important");
  267. } else {
  268. property[prop] = handlePixe(prop, property[prop]);
  269. element.style.setProperty(prop, property[prop]);
  270. }
  271. }
  272. }
  273. };
  274. /**
  275. * 获取或设置元素的文本内容
  276. * @param {HTMLElement} element 目标元素
  277. * @param {HTMLElement|string} [text] 文本内容(可选)
  278. * @returns {?string} 如果传入了text,则返回undefined;否则返回文本内容
  279. * @example
  280. * // 设置元素a.xx的文本内容为abcd
  281. * DOMUtils.text(document.querySelector("a.xx"),"abcd")
  282. * DOMUtils.text("a.xx","abcd")
  283. * DOMUtils.html("a.xx",document.querySelector("b"))
  284. * */
  285. DOMUtils.text = function (element, text) {
  286. if (typeof element === "string") {
  287. element = document.querySelector(element);
  288. }
  289. if (element == void 0) {
  290. return;
  291. }
  292. if (text === void 0) {
  293. return element.textContent || element.innerText;
  294. } else {
  295. if (text instanceof Node) {
  296. text = text.textContent || text.innerText;
  297. }
  298. if ("textContent" in element) {
  299. element.textContent = text;
  300. } else if ("innerText" in element) {
  301. element.innerText = text;
  302. }
  303. }
  304. };
  305. /**
  306. * 获取或设置元素的HTML内容
  307. * @param {HTMLElement|string} element 目标元素
  308. * @param {HTMLElement|string} [html] HTML内容|元素(可选)
  309. * @returns {?string} 如果传入了html,则返回undefined;否则返回HTML内容
  310. * @example
  311. * // 设置元素a.xx的文本内容为<b>abcd</b>
  312. * DOMUtils.html(document.querySelector("a.xx"),"<b>abcd</b>")
  313. * DOMUtils.html("a.xx","<b>abcd</b>")
  314. * DOMUtils.html("a.xx",document.querySelector("b"))
  315. * */
  316. DOMUtils.html = function (element, html) {
  317. if (typeof element === "string") {
  318. element = document.querySelector(element);
  319. }
  320. if (element == void 0) {
  321. return;
  322. }
  323. if (html == void 0) {
  324. return element.innerHTML;
  325. } else {
  326. if (html instanceof Node) {
  327. html = html.innerHTML;
  328. }
  329. if ("innerHTML" in element) {
  330. element.innerHTML = html;
  331. }
  332. }
  333. };
  334. /**
  335. * 绑定或触发元素的click事件
  336. * @param {HTMLElement|string} element 目标元素
  337. * @param {function} [handler] 事件处理函数(可选)
  338. * @param {object|undefined} details 赋予触发的Event的额外属性
  339. * @param {boolean} [useDispatchToTriggerEvent=true] 是否使用dispatchEvent来触发事件,默认true
  340. * @example
  341. * // 触发元素a.xx的click事件
  342. * DOMUtils.click(document.querySelector("a.xx"))
  343. * DOMUtils.click("a.xx")
  344. * DOMUtils.click("a.xx",function(){
  345. * console.log("触发click事件成功")
  346. * })
  347. * */
  348. DOMUtils.click = function (
  349. element,
  350. handler,
  351. details,
  352. useDispatchToTriggerEvent = true
  353. ) {
  354. if (typeof element === "string") {
  355. element = document.querySelector(element);
  356. }
  357. if (element == void 0) {
  358. return;
  359. }
  360. if (handler === void 0) {
  361. DOMUtils.trigger(element, "click", details, useDispatchToTriggerEvent);
  362. } else {
  363. DOMUtils.on(element, "click", null, handler);
  364. }
  365. };
  366.  
  367. /**
  368. * 绑定或触发元素的blur事件
  369. * @param {HTMLElement|string} element 目标元素
  370. * @param {function} [handler] 事件处理函数(可选)
  371. * @param {object|undefined} details 赋予触发的Event的额外属性
  372. * @param {boolean} [useDispatchToTriggerEvent=true] 是否使用dispatchEvent来触发事件,默认true
  373. * @example
  374. * // 触发元素a.xx的blur事件
  375. * DOMUtils.blur(document.querySelector("a.xx"))
  376. * DOMUtils.blur("a.xx")
  377. * DOMUtils.blur("a.xx",function(){
  378. * console.log("触发blur事件成功")
  379. * })
  380. * */
  381. DOMUtils.blur = function (
  382. element,
  383. handler,
  384. details,
  385. useDispatchToTriggerEvent = true
  386. ) {
  387. if (typeof element === "string") {
  388. element = document.querySelector(element);
  389. }
  390. if (element == void 0) {
  391. return;
  392. }
  393. if (handler === void 0) {
  394. DOMUtils.trigger(element, "blur", details, useDispatchToTriggerEvent);
  395. } else {
  396. DOMUtils.on(element, "blur", null, handler);
  397. }
  398. };
  399. /**
  400. * 绑定或触发元素的focus事件
  401. * @param {HTMLElement|string|window} element 目标元素
  402. * @param {?function} [handler] 事件处理函数(可选)
  403. * @param {object|undefined} details 赋予触发的Event的额外属性
  404. * @param {boolean} [useDispatchToTriggerEvent=true] 是否使用dispatchEvent来触发事件,默认true
  405. * @example
  406. * // 触发元素a.xx的focus事件
  407. * DOMUtils.focus(document.querySelector("a.xx"))
  408. * DOMUtils.focus("a.xx")
  409. * DOMUtils.focus("a.xx",function(){
  410. * console.log("触发focus事件成功")
  411. * })
  412. * */
  413. DOMUtils.focus = function (
  414. element,
  415. handler,
  416. details,
  417. useDispatchToTriggerEvent = true
  418. ) {
  419. if (typeof element === "string") {
  420. element = document.querySelector(element);
  421. }
  422. if (element == void 0) {
  423. return;
  424. }
  425. if (handler === void 0) {
  426. DOMUtils.trigger(element, "focus", details, useDispatchToTriggerEvent);
  427. } else {
  428. DOMUtils.on(element, "focus", null, handler);
  429. }
  430. };
  431. /**
  432. * 获取或设置元素的value属性值
  433. * @param {HTMLElement|string} element 目标元素
  434. * @param {string} [value] value属性值(可选)
  435. * @returns {string|undefined} 如果传入了value,则返回undefined;否则返回value属性值
  436. * @example
  437. * // 获取元素input.xx的复选框值
  438. * DOMUtils.val(document.querySelector("input.xx"))
  439. * DOMUtils.val("input.xx")
  440. * > true
  441. * @example
  442. * // 修改元素input.xx的复选框值为true
  443. * DOMUtils.val(document.querySelector("input.xx"),true)
  444. * DOMUtils.val("input.xx",true)
  445. * */
  446. DOMUtils.val = function (element, value) {
  447. if (typeof element === "string") {
  448. element = document.querySelector(element);
  449. }
  450. if (element == void 0) {
  451. return;
  452. }
  453. if (value == void 0) {
  454. if (
  455. element.localName === "input" &&
  456. (element.type === "checkbox" || element.type === "radio")
  457. ) {
  458. return element.checked;
  459. } else {
  460. return element.value;
  461. }
  462. } else {
  463. if (
  464. element.localName === "input" &&
  465. (element.type === "checkbox" || element.type === "radio")
  466. ) {
  467. element.checked = !!value;
  468. } else {
  469. element.value = value;
  470. }
  471. }
  472. };
  473. /**
  474. * 获取或设置元素的属性值
  475. * @param {HTMLElement|string} element 目标元素
  476. * @param {string} propName 属性名
  477. * @param {string} [propValue] 属性值(可选)
  478. * @returns {string|undefined} 如果传入了propValue,则返回undefined;否则返回属性值
  479. * @example
  480. * // 获取元素a.xx的属性data-value
  481. * DOMUtils.val(document.querySelector("a.xx"),"data-value")
  482. * DOMUtils.val("a.xx","data-value")
  483. * > undefined
  484. * @example
  485. * // 设置元素a.xx的属性data-value为1
  486. * DOMUtils.val(document.querySelector("a.xx"),"data-value",1)
  487. * DOMUtils.val("a.xx","data-value",1)
  488. * */
  489. DOMUtils.prop = function (element, propName, propValue) {
  490. if (typeof element === "string") {
  491. element = document.querySelector(element);
  492. }
  493. if (element == void 0) {
  494. return;
  495. }
  496. if (propValue == void 0) {
  497. return element[propName];
  498. } else {
  499. element[propName] = propValue;
  500. }
  501. };
  502.  
  503. /**
  504. * 移除元素的属性
  505. * @param {HTMLElement|string} element 目标元素
  506. * @param {string} attrName 属性名
  507. * @example
  508. * // 移除元素a.xx的属性data-value
  509. * DOMUtils.removeAttr(document.querySelector("a.xx"),"data-value")
  510. * DOMUtils.removeAttr("a.xx","data-value")
  511. * */
  512. DOMUtils.removeAttr = function (element, attrName) {
  513. if (typeof element === "string") {
  514. element = document.querySelector(element);
  515. }
  516. if (element == void 0) {
  517. return;
  518. }
  519. element.removeAttribute(attrName);
  520. };
  521.  
  522. /**
  523. * 移除元素class名
  524. * @param {HTMLElement|string} element 目标元素
  525. * @param {string} className class名
  526. * @returns {DOMUtils} 原型链
  527. * @example
  528. * // 移除元素a.xx的className为xx
  529. * DOMUtils.removeClass(document.querySelector("a.xx"),"xx")
  530. * DOMUtils.removeClass("a.xx","xx")
  531. */
  532. DOMUtils.removeClass = function (element, className) {
  533. if (typeof element === "string") {
  534. element = document.querySelector(element);
  535. }
  536. if (element == void 0) {
  537. return;
  538. }
  539. if (className == void 0) {
  540. return;
  541. }
  542. element.classList.remove(className);
  543. };
  544.  
  545. /**
  546. * 移除元素的属性
  547. * @param {HTMLElement|string} element 目标元素
  548. * @param {string} propName 属性名
  549. * @example
  550. * // 移除元素a.xx的href属性
  551. * DOMUtils.removeProp(document.querySelector("a.xx"),"href")
  552. * DOMUtils.removeProp("a.xx","href")
  553. * */
  554. DOMUtils.removeProp = function (element, propName) {
  555. if (typeof element === "string") {
  556. element = document.querySelector(element);
  557. }
  558. if (element == void 0) {
  559. return;
  560. }
  561. delete element[propName];
  562. };
  563.  
  564. /**
  565. * 将一个元素替换为另一个元素
  566. * @param {HTMLElement|string} element 目标元素
  567. * @param {HTMLElement|string} newElement 新元素
  568. * @returns {DOMUtils} 原型链
  569. * @example
  570. * // 替换元素a.xx为b.xx
  571. * DOMUtils.replaceWith(document.querySelector("a.xx"),document.querySelector("b.xx"))
  572. * DOMUtils.replaceWith("a.xx",'<b class="xx"></b>')
  573. */
  574. DOMUtils.replaceWith = function (element, newElement) {
  575. if (typeof element === "string") {
  576. element = document.querySelector(element);
  577. }
  578. if (element == void 0) {
  579. return;
  580. }
  581. if (typeof newElement === "string") {
  582. newElement = DOMUtils.parseHTML(newElement, false, false);
  583. }
  584. if (element instanceof NodeList || element instanceof Array) {
  585. element.forEach((item) => {
  586. DOMUtils.replaceWith(item, newElement);
  587. });
  588. } else {
  589. element.parentElement.replaceChild(newElement, element);
  590. }
  591. };
  592.  
  593. /**
  594. * 给元素添加class
  595. * @param {HTMLElement|string} element 目标元素
  596. * @param {string} className class名
  597. * @example
  598. * // 元素a.xx的className添加_vue_
  599. * DOMUtils.addClass(document.querySelector("a.xx"),"_vue_")
  600. * DOMUtils.addClass("a.xx","_vue_")
  601. * */
  602. DOMUtils.addClass = function (element, className) {
  603. if (typeof element === "string") {
  604. element = document.querySelector(element);
  605. }
  606. if (element == void 0) {
  607. return;
  608. }
  609. element.classList.add(className);
  610. };
  611. /**
  612. * 函数在元素内部末尾添加子元素或HTML字符串
  613. * @param {HTMLElement|string} element 目标元素
  614. * @param {HTMLElement|string} content 子元素或HTML字符串
  615. * @example
  616. * // 元素a.xx的内部末尾添加一个元素
  617. * DOMUtils.append(document.querySelector("a.xx"),document.querySelector("b.xx"))
  618. * DOMUtils.append("a.xx","'<b class="xx"></b>")
  619. * */
  620. DOMUtils.append = function (element, content) {
  621. if (typeof element === "string") {
  622. element = document.querySelector(element);
  623. }
  624. if (element == void 0) {
  625. return;
  626. }
  627. if (typeof content === "string") {
  628. element.insertAdjacentHTML("beforeend", content);
  629. } else {
  630. element.appendChild(content);
  631. }
  632. };
  633.  
  634. /**
  635. * 函数 在元素内部开头添加子元素或HTML字符串
  636. * @param {HTMLElement|string} element 目标元素
  637. * @param {HTMLElement|string} content 子元素或HTML字符串
  638. * @example
  639. * // 元素a.xx内部开头添加一个元素
  640. * DOMUtils.prepend(document.querySelector("a.xx"),document.querySelector("b.xx"))
  641. * DOMUtils.prepend("a.xx","'<b class="xx"></b>")
  642. * */
  643. DOMUtils.prepend = function (element, content) {
  644. if (typeof element === "string") {
  645. element = document.querySelector(element);
  646. }
  647. if (element == void 0) {
  648. return;
  649. }
  650. if (typeof content === "string") {
  651. element.insertAdjacentHTML("afterbegin", content);
  652. } else {
  653. element.insertBefore(content, element.firstChild);
  654. }
  655. };
  656. /**
  657. * 在元素后面添加兄弟元素或HTML字符串
  658. * @param {HTMLElement|string} element 目标元素
  659. * @param {HTMLElement|string} content 兄弟元素或HTML字符串
  660. * @example
  661. * // 元素a.xx后面添加一个元素
  662. * DOMUtils.after(document.querySelector("a.xx"),document.querySelector("b.xx"))
  663. * DOMUtils.after("a.xx","'<b class="xx"></b>")
  664. * */
  665. DOMUtils.after = function (element, content) {
  666. if (typeof element === "string") {
  667. element = document.querySelector(element);
  668. }
  669. if (element == void 0) {
  670. return;
  671. }
  672. if (typeof content === "string") {
  673. element.insertAdjacentHTML("afterend", content);
  674. } else {
  675. element.parentElement.insertBefore(content, element.nextSibling);
  676. }
  677. };
  678.  
  679. /**
  680. * 在元素前面添加兄弟元素或HTML字符串
  681. * @param {HTMLElement|string} element 目标元素
  682. * @param {HTMLElement|string} content 兄弟元素或HTML字符串
  683. * @example
  684. * // 元素a.xx前面添加一个元素
  685. * DOMUtils.before(document.querySelector("a.xx"),document.querySelector("b.xx"))
  686. * DOMUtils.before("a.xx","'<b class="xx"></b>")
  687. * */
  688. DOMUtils.before = function (element, content) {
  689. if (typeof element === "string") {
  690. element = document.querySelector(element);
  691. }
  692. if (element == void 0) {
  693. return;
  694. }
  695. if (typeof content === "string") {
  696. element.insertAdjacentHTML("beforebegin", content);
  697. } else {
  698. element.parentElement.insertBefore(content, element);
  699. }
  700. };
  701.  
  702. /**
  703. * 移除元素
  704. * @param {HTMLElement|string|NodeList} element 目标元素
  705. * @example
  706. * // 元素a.xx前面添加一个元素
  707. * DOMUtils.remove(document.querySelector("a.xx"))
  708. * DOMUtils.remove(document.querySelectorAll("a.xx"))
  709. * DOMUtils.remove("a.xx")
  710. * */
  711. DOMUtils.remove = function (element) {
  712. if (typeof element === "string") {
  713. element = document.querySelectorAll(element);
  714. }
  715. if (element == void 0) {
  716. return;
  717. }
  718. if (element instanceof NodeList || element instanceof Array) {
  719. element.forEach(function (item) {
  720. DOMUtils.remove(item);
  721. });
  722. } else {
  723. element.remove();
  724. }
  725. };
  726. /**
  727. * 移除元素的所有子元素
  728. * @param {HTMLElement|string} element 目标元素
  729. * @example
  730. * // 移除元素a.xx元素的所有子元素
  731. * DOMUtils.empty(document.querySelector("a.xx"))
  732. * DOMUtils.empty("a.xx")
  733. * */
  734. DOMUtils.empty = function (element) {
  735. if (typeof element === "string") {
  736. element = document.querySelector(element);
  737. }
  738. if (element == void 0) {
  739. return;
  740. }
  741. while (element.firstChild) {
  742. element.removeChild(element.firstChild);
  743. }
  744. };
  745. /**
  746. * 绑定事件
  747. * @param {HTMLElement|string|NodeList|Array|Window} element 需要绑定的元素|元素数组|window
  748. * @param {DOMUtils_EventType|DOMUtils_EventType[]} eventType 需要监听的事件
  749. * @param {string|undefined} selector 子元素选择器
  750. * @param {((event: Event)=>void)|undefined} callback 绑定事件触发的回调函数
  751. * @param {boolean|AddEventListenerOptions|undefined} option
  752. * + capture 表示事件是否在捕获阶段触发。默认为false,即在冒泡阶段触发
  753. * + once 表示事件是否只触发一次。默认为false
  754. * + passive 表示事件监听器是否不会调用preventDefault()。默认为false
  755. * @example
  756. * // 监听元素a.xx的click事件
  757. * DOMUtils.on(document.querySelector("a.xx"),"click",(event)=>{
  758. * console.log("事件触发",event)
  759. * })
  760. * DOMUtils.on("a.xx","click",(event)=>{
  761. * console.log("事件触发",event)
  762. * })
  763. * @example
  764. * // 监听元素a.xx的click、tap、hover事件
  765. * DOMUtils.on(document.querySelector("a.xx"),"click tap hover",(event)=>{
  766. * console.log("事件触发",event)
  767. * })
  768. * DOMUtils.on("a.xx",["click","tap","hover"],(event)=>{
  769. * console.log("事件触发",event)
  770. * })
  771. * @example
  772. * // 监听全局document下的子元素a.xx的click事件
  773. * DOMUtils.on(document,"click tap hover","a.xx",(event)=>{
  774. * console.log("事件触发",event)
  775. * })
  776. */
  777. DOMUtils.on = function (element, eventType, selector, callback, option) {
  778. /**
  779. * 获取option配置
  780. * @param {any[]} args
  781. * @param {number} startIndex
  782. * @param {AddEventListenerOptions} option
  783. * @returns {AddEventListenerOptions}
  784. */
  785. function getOption(args, startIndex, option) {
  786. if (typeof args[startIndex] === "boolean") {
  787. option.capture = args[startIndex];
  788. if (typeof args[startIndex + 1] === "boolean") {
  789. option.once = args[startIndex + 1];
  790. }
  791. if (typeof args[startIndex + 2] === "boolean") {
  792. option.passive = args[startIndex + 2];
  793. }
  794. } else if (
  795. typeof args[startIndex] === "object" &&
  796. ("capture" in args[startIndex] ||
  797. "once" in args[startIndex] ||
  798. "passive" in args[startIndex])
  799. ) {
  800. option.capture = args[startIndex].capture;
  801. option.once = args[startIndex].once;
  802. option.passive = args[startIndex].passive;
  803. }
  804. return option;
  805. }
  806.  
  807. let args = arguments;
  808. if (typeof element === "string") {
  809. element = document.querySelectorAll(element);
  810. }
  811. if (element == void 0) {
  812. return;
  813. }
  814. /**
  815. * @type {HTMLElement[]}
  816. */
  817. let elementList = [];
  818. if (element instanceof NodeList || Array.isArray(element)) {
  819. elementList = [...element];
  820. } else {
  821. elementList.push(element);
  822. }
  823. /**
  824. * @type {string[]}
  825. */
  826. let eventTypeList = [];
  827. if (Array.isArray(eventType)) {
  828. eventTypeList = eventTypeList.concat(eventType);
  829. } else if (typeof eventType === "string") {
  830. eventTypeList = eventTypeList.concat(eventType.split(" "));
  831. }
  832. /**
  833. * 元素属性上自定义的用于暂存事件的对象名
  834. */
  835. let propEventsName = "events";
  836. /**
  837. * window属性上自定义的用于暂存事件的对象名
  838. */
  839. let windowEventsName = "DOMUtilsGlobalEvents";
  840. /**
  841. * @type {?string}
  842. */
  843. let _selector_ = selector;
  844. /**
  845. * @type {(event:Event)=>{}}
  846. */
  847. let _callback_ = callback;
  848. /**
  849. * @type {AddEventListenerOptions}
  850. */
  851. let _option_ = {
  852. capture: false,
  853. once: false,
  854. passive: false,
  855. };
  856. if (typeof selector === "function") {
  857. /* 这是为没有selector的情况 */
  858. _selector_ = void 0;
  859. _callback_ = selector;
  860. _option_ = getOption(args, 3, _option_);
  861. } else {
  862. /* 这是存在selector的情况 */
  863. _option_ = getOption(args, 4, _option_);
  864. }
  865. elementList.forEach((elementItem) => {
  866. let ownCallBack = function (event) {
  867. let target = event.target;
  868. if (_selector_) {
  869. /* 存在自定义子元素选择器 */
  870. let totalParent = CommonUtils.isWin(elementItem)
  871. ? document.documentElement
  872. : elementItem;
  873. if (target.matches(_selector_)) {
  874. /* 当前目标可以被selector所匹配到 */
  875. _callback_.call(target, event);
  876. return;
  877. } else if (
  878. target.closest(_selector_) &&
  879. totalParent.contains(target.closest(_selector_))
  880. ) {
  881. /* 在上层与主元素之间寻找可以被selector所匹配到的 */
  882. let closestElement = target.closest(_selector_);
  883. /* event的target值不能直接修改 */
  884. Object.defineProperty(event, "target", {
  885. get() {
  886. return closestElement;
  887. },
  888. });
  889. _callback_.call(closestElement, event);
  890. return;
  891. }
  892. } else {
  893. _callback_.call(elementItem, event);
  894. }
  895. };
  896. /* 判断元素是否是window */
  897. let attributeEventName = CommonUtils.isWin(elementItem)
  898. ? windowEventsName
  899. : propEventsName;
  900. /* 遍历事件名设置元素事件 */
  901. eventTypeList.forEach((eventName) => {
  902. elementItem.addEventListener(eventName, ownCallBack, _option_);
  903.  
  904. if (_callback_ && _callback_.delegate) {
  905. elementItem.setAttribute("data-delegate", _selector_);
  906. }
  907.  
  908. let elementEvents = elementItem[attributeEventName] || {};
  909. elementEvents[eventName] = elementEvents[eventName] || [];
  910. elementEvents[eventName].push({
  911. selector: _selector_,
  912. option: _option_,
  913. callback: ownCallBack,
  914. originCallBack: _callback_,
  915. });
  916. elementItem[attributeEventName] = elementEvents;
  917. });
  918. });
  919. };
  920. /**
  921. * 取消绑定事件
  922. * @param {HTMLElement|string|NodeList|Array|Window} element 需要取消绑定的元素|元素数组
  923. * @param {DOMUtils_EventType|DOMUtils_EventType[]} eventType 需要取消监听的事件
  924. * @param {string|undefined} selector 子元素选择器
  925. * @param {((event:Event)=>void)|undefined} callback 通过DOMUtils.on绑定的事件函数
  926. * @param {EventListenerOptions|boolean|undefined} option
  927. * + capture 如果在添加事件监听器时指定了useCapture为true,则在移除事件监听器时也必须指定为true
  928. * @example
  929. * // 取消监听元素a.xx的click事件
  930. * DOMUtils.off(document.querySelector("a.xx"),"click")
  931. * DOMUtils.off("a.xx","click")
  932. * // 取消监听元素a.xx的click、tap、hover事件
  933. * DOMUtils.off(document.querySelector("a.xx"),"click tap hover")
  934. * DOMUtils.off("a.xx",["click","tap","hover"])
  935. * // 取消监听全局下的a.xx的点击事件
  936. * DOMUtils.off(document,"click","a.xx")
  937. */
  938. DOMUtils.off = function (element, eventType, selector, callback, option) {
  939. /**
  940. * 获取option配置
  941. * @param {any[]} args
  942. * @param {number} startIndex
  943. * @param {EventListenerOptions} option
  944. * @returns {EventListenerOptions}
  945. */
  946. function getOption(args, startIndex, option) {
  947. if (typeof args[startIndex] === "boolean") {
  948. option.capture = args[startIndex];
  949. } else if (
  950. typeof args[startIndex] === "object" &&
  951. "capture" in args[startIndex]
  952. ) {
  953. option.capture = args[startIndex].capture;
  954. }
  955. return option;
  956. }
  957.  
  958. let args = arguments;
  959. if (typeof element === "string") {
  960. element = document.querySelectorAll(element);
  961. }
  962. if (element == void 0) {
  963. return;
  964. }
  965. /**
  966. * @type {HTMLElement[]}
  967. */
  968. let elementList = [];
  969. if (element instanceof NodeList || Array.isArray(element)) {
  970. elementList = [...element];
  971. } else {
  972. elementList.push(element);
  973. }
  974. /**
  975. * @type {string[]}
  976. */
  977. let eventTypeList = [];
  978. if (Array.isArray(eventType)) {
  979. eventTypeList = eventTypeList.concat(eventType);
  980. } else if (typeof eventType === "string") {
  981. eventTypeList = eventTypeList.concat(eventType.split(" "));
  982. }
  983. /**
  984. * 元素属性上自定义的用于暂存事件的对象名
  985. */
  986. let propEventsName = "events";
  987. /**
  988. * window属性上自定义的用于暂存事件的对象名
  989. */
  990. let windowEventsName = "DOMUtilsGlobalEvents";
  991. /**
  992. * @type {?string}
  993. */
  994. let _selector_ = selector;
  995. /**
  996. * @type {(event:Event)=>{}}
  997. */
  998. let _callback_ = callback;
  999.  
  1000. /**
  1001. * @type {EventListenerOptions}
  1002. */
  1003. let _option_ = {
  1004. capture: false,
  1005. };
  1006. if (typeof selector === "function") {
  1007. /* 这是为没有selector的情况 */
  1008. _selector_ = void 0;
  1009. _callback_ = selector;
  1010. _option_ = getOption(args, 3, _option_);
  1011. } else {
  1012. _option_ = getOption(args, 4, _option_);
  1013. }
  1014. elementList.forEach((elementItem) => {
  1015. let elementEvents = {};
  1016. if (CommonUtils.isWin(elementItem)) {
  1017. elementEvents = elementItem[windowEventsName] || {};
  1018. } else {
  1019. elementEvents = elementItem[propEventsName] || {};
  1020. }
  1021. eventTypeList.forEach((eventName) => {
  1022. let handlers = elementEvents[eventName] || [];
  1023. for (let index = 0; index < handlers.length; index++) {
  1024. let handler = handlers[index];
  1025. if (
  1026. (!_selector_ || handler.selector === _selector_) &&
  1027. (!_callback_ ||
  1028. handler.callback === _callback_ ||
  1029. handler.originCallBack === _callback_)
  1030. ) {
  1031. elementItem.removeEventListener(
  1032. eventName,
  1033. handler.callback,
  1034. _option_
  1035. );
  1036. handlers.splice(index--, 1);
  1037. }
  1038. }
  1039. if (handlers.length === 0) {
  1040. delete elementEvents[eventType];
  1041. }
  1042. });
  1043. if (CommonUtils.isWin(elementItem)) {
  1044. elementItem[windowEventsName] = elementEvents;
  1045. } else {
  1046. elementItem[propEventsName] = elementEvents;
  1047. }
  1048. });
  1049. };
  1050.  
  1051. /**
  1052. * 主动触发事件
  1053. * @param {HTMLElement|string|NodeList|Array|Window} element 需要触发的元素|元素数组|window
  1054. * @param {DOMUtils_EventType|DOMUtils_EventType[]} eventType 需要触发的事件
  1055. * @param {object|undefined} details 赋予触发的Event的额外属性
  1056. * @param {boolean} [useDispatchToTriggerEvent=true] 是否使用dispatchEvent来触发事件,默认true
  1057. * @example
  1058. * // 触发元素a.xx的click事件
  1059. * DOMUtils.trigger(document.querySelector("a.xx"),"click")
  1060. * DOMUtils.trigger("a.xx","click")
  1061. * // 触发元素a.xx的click、tap、hover事件
  1062. * DOMUtils.trigger(document.querySelector("a.xx"),"click tap hover")
  1063. * DOMUtils.trigger("a.xx",["click","tap","hover"])
  1064. */
  1065. DOMUtils.trigger = function (
  1066. element,
  1067. eventType,
  1068. details,
  1069. useDispatchToTriggerEvent = true
  1070. ) {
  1071. if (typeof element === "string") {
  1072. element = document.querySelector(element);
  1073. }
  1074. if (element == void 0) {
  1075. return;
  1076. }
  1077. let elementList = [];
  1078. if (element instanceof NodeList || Array.isArray(element)) {
  1079. elementList = [...element];
  1080. } else {
  1081. elementList = [element];
  1082. }
  1083. let eventTypeList = [];
  1084. if (!eventType) {
  1085. for (let type in events) {
  1086. eventTypeList = [...eventTypeList, type];
  1087. }
  1088. } else if (Array.isArray(eventType)) {
  1089. eventTypeList = eventType;
  1090. } else if (typeof eventType === "string") {
  1091. eventTypeList = eventType.split(" ");
  1092. }
  1093.  
  1094. elementList.forEach((elementItem) => {
  1095. let events = {};
  1096. if (CommonUtils.isWin(elementItem)) {
  1097. events = elementItem["DOMUtilsGlobalEvents"] || {};
  1098. } else {
  1099. events = elementItem.events || {};
  1100. }
  1101. eventTypeList.forEach((_eventType_) => {
  1102. let event = new Event(_eventType_);
  1103. if (details) {
  1104. Object.assign(event, details);
  1105. }
  1106. if (useDispatchToTriggerEvent == false && _eventType_ in events) {
  1107. events[_eventType_].forEach((eventsItem) => {
  1108. eventsItem.callback(event);
  1109. });
  1110. } else {
  1111. elementItem.dispatchEvent(event);
  1112. }
  1113. });
  1114. });
  1115. };
  1116. /**
  1117. * 设置或返回被选元素相对于文档的偏移坐标
  1118. * @param {HTMLElement|string} element
  1119. * @returns { {
  1120. * top: number,
  1121. * left: number
  1122. * }}
  1123. * @example
  1124. * // 获取元素a.xx的对于文档的偏移坐标
  1125. * DOMUtils.offset(document.querySelector("a.xx"))
  1126. * DOMUtils.offset("a.xx")
  1127. * > 0
  1128. */
  1129. DOMUtils.offset = function (element) {
  1130. if (typeof element === "string") {
  1131. element = document.querySelector(element);
  1132. }
  1133. if (element == void 0) {
  1134. return;
  1135. }
  1136. let rect = element.getBoundingClientRect();
  1137. let win = window;
  1138. return {
  1139. top: rect.top + win.scrollY,
  1140. left: rect.left + win.scrollX,
  1141. };
  1142. };
  1143. /**
  1144. * 获取元素的宽度
  1145. * @param {HTMLElement|string} element 要获取宽度的元素
  1146. * @param {boolean} [isShow=false] 是否已进行isShow,避免爆堆栈
  1147. * @returns {number} 元素的宽度,单位为像素
  1148. * @example
  1149. * // 获取元素a.xx的宽度
  1150. * DOMUtils.width(document.querySelector("a.xx"))
  1151. * DOMUtils.width("a.xx")
  1152. * > 100
  1153. * // 获取window的宽度
  1154. * DOMUtils.width(window)
  1155. * > 400
  1156. * @example
  1157. * // 设置元素a.xx的宽度为200
  1158. * DOMUtils.width(document.querySelector("a.xx"),200)
  1159. * DOMUtils.width("a.xx",200)
  1160. */
  1161. DOMUtils.width = function (element, isShow = false) {
  1162. if (CommonUtils.isWin(element)) {
  1163. return window.document.documentElement.clientWidth;
  1164. }
  1165. if (typeof element === "string") {
  1166. element = document.querySelector(element);
  1167. }
  1168. if (element == void 0) {
  1169. return;
  1170. }
  1171. if (element.nodeType === 9) {
  1172. /* 文档节点 */
  1173. return Math.max(
  1174. element.body.scrollWidth,
  1175. element.documentElement.scrollWidth,
  1176. element.body.offsetWidth,
  1177. element.documentElement.offsetWidth,
  1178. element.documentElement.clientWidth
  1179. );
  1180. }
  1181. if (CommonUtils.isShow(element)) {
  1182. /* 已显示 */
  1183. /* 不从style中获取对应的宽度,因为可能使用了class定义了width !important */
  1184.  
  1185. /* 如果element.style.width为空 则从css里面获取是否定义了width信息如果定义了 则读取css里面定义的宽度width */
  1186. if (parseFloat(CommonUtils.getStyleValue(element, "width")) > 0) {
  1187. return parseFloat(CommonUtils.getStyleValue(element, "width"));
  1188. }
  1189.  
  1190. /* 如果从css里获取到的值不是大于0 可能是auto 则通过offsetWidth来进行计算 */
  1191. if (element.offsetWidth > 0) {
  1192. let borderLeftWidth = CommonUtils.getStyleValue(
  1193. element,
  1194. "borderLeftWidth"
  1195. );
  1196. let borderRightWidth = CommonUtils.getStyleValue(
  1197. element,
  1198. "borderRightWidth"
  1199. );
  1200. let paddingLeft = CommonUtils.getStyleValue(element, "paddingLeft");
  1201. let paddingRight = CommonUtils.getStyleValue(element, "paddingRight");
  1202. let backHeight =
  1203. parseFloat(element.offsetWidth) -
  1204. parseFloat(borderLeftWidth) -
  1205. parseFloat(borderRightWidth) -
  1206. parseFloat(paddingLeft) -
  1207. parseFloat(paddingRight);
  1208. return parseFloat(backHeight);
  1209. }
  1210. return 0;
  1211. } else {
  1212. /* 未显示 */
  1213. let { recovery } = CommonUtils.showElement(element);
  1214. let width = DOMUtils.width(element, true);
  1215. recovery();
  1216. return width;
  1217. }
  1218. };
  1219. /**
  1220. * 获取元素的高度
  1221. * @param {HTMLElement|string} element 要获取高度的元素
  1222. * @param {boolean} [isShow=false] 是否已进行isShow,避免爆堆栈
  1223. * @returns {number} 元素的高度,单位为像素
  1224. * @example
  1225. * // 获取元素a.xx的高度
  1226. * DOMUtils.height(document.querySelector("a.xx"))
  1227. * DOMUtils.height("a.xx")
  1228. * > 100
  1229. * // 获取window的高度
  1230. * DOMUtils.height(window)
  1231. * > 700
  1232. * @example
  1233. * // 设置元素a.xx的高度为200
  1234. * DOMUtils.height(document.querySelector("a.xx"),200)
  1235. * DOMUtils.height("a.xx",200)
  1236. */
  1237. DOMUtils.height = function (element, isShow = false) {
  1238. if (CommonUtils.isWin(element)) {
  1239. return window.document.documentElement.clientHeight;
  1240. }
  1241. if (typeof element === "string") {
  1242. element = document.querySelector(element);
  1243. }
  1244. if (element == void 0) {
  1245. return;
  1246. }
  1247. if (element.nodeType === 9) {
  1248. /* 文档节点 */
  1249. return Math.max(
  1250. element.body.scrollHeight,
  1251. element.documentElement.scrollHeight,
  1252. element.body.offsetHeight,
  1253. element.documentElement.offsetHeight,
  1254. element.documentElement.clientHeight
  1255. );
  1256. }
  1257. if (CommonUtils.isShow(element)) {
  1258. /* 已显示 */
  1259. /* 从style中获取对应的高度,因为可能使用了class定义了width !important */
  1260. /* 如果element.style.height为空 则从css里面获取是否定义了height信息如果定义了 则读取css里面定义的高度height */
  1261. if (parseFloat(CommonUtils.getStyleValue(element, "height")) > 0) {
  1262. return parseFloat(CommonUtils.getStyleValue(element, "height"));
  1263. }
  1264.  
  1265. /* 如果从css里获取到的值不是大于0 可能是auto 则通过offsetHeight来进行计算 */
  1266. if (element.offsetHeight > 0) {
  1267. let borderTopWidth = CommonUtils.getStyleValue(
  1268. element,
  1269. "borderTopWidth"
  1270. );
  1271. let borderBottomWidth = CommonUtils.getStyleValue(
  1272. element,
  1273. "borderBottomWidth"
  1274. );
  1275. let paddingTop = CommonUtils.getStyleValue(element, "paddingTop");
  1276. let paddingBottom = CommonUtils.getStyleValue(element, "paddingBottom");
  1277. let backHeight =
  1278. parseFloat(element.offsetHeight) -
  1279. parseFloat(borderTopWidth) -
  1280. parseFloat(borderBottomWidth) -
  1281. parseFloat(paddingTop) -
  1282. parseFloat(paddingBottom);
  1283. return parseFloat(backHeight);
  1284. }
  1285. return 0;
  1286. } else {
  1287. /* 未显示 */
  1288. let { recovery } = CommonUtils.showElement(element);
  1289. let height = DOMUtils.height(element, true);
  1290. recovery();
  1291. return height;
  1292. }
  1293. };
  1294. /**
  1295. * 获取元素的外部宽度(包括边框和外边距)
  1296. * @param {HTMLElement|string} element 要获取外部宽度的元素
  1297. * @param {boolean} [isShow=false] 是否已进行isShow,避免爆堆栈
  1298. * @returns {number} 元素的外部宽度,单位为像素
  1299. * @example
  1300. * // 获取元素a.xx的外部宽度
  1301. * DOMUtils.outerWidth(document.querySelector("a.xx"))
  1302. * DOMUtils.outerWidth("a.xx")
  1303. * > 100
  1304. * // 获取window的外部宽度
  1305. * DOMUtils.outerWidth(window)
  1306. * > 400
  1307. */
  1308. DOMUtils.outerWidth = function (element, isShow = false) {
  1309. if (CommonUtils.isWin(element)) {
  1310. return window.innerWidth;
  1311. }
  1312. if (typeof element === "string") {
  1313. element = document.querySelector(element);
  1314. }
  1315. if (element == void 0) {
  1316. return;
  1317. }
  1318. if (CommonUtils.isShow(element)) {
  1319. let style = getComputedStyle(element, null);
  1320. let marginLeft = CommonUtils.getStyleValue(style, "marginLeft");
  1321. let marginRight = CommonUtils.getStyleValue(style, "marginRight");
  1322. return element.offsetWidth + marginLeft + marginRight;
  1323. } else {
  1324. let { recovery } = CommonUtils.showElement(element);
  1325. let outerWidth = DOMUtils.outerWidth(element, true);
  1326. recovery();
  1327. return outerWidth;
  1328. }
  1329. };
  1330. /**
  1331. * 获取元素的外部高度(包括边框和外边距)
  1332. * @param {HTMLElement|string} element 要获取外部高度的元素
  1333. * @param {boolean} [isShow=false] 是否已进行isShow,避免爆堆栈
  1334. * @returns {number} 元素的外部高度,单位为像素
  1335. * @example
  1336. * // 获取元素a.xx的外部高度
  1337. * DOMUtils.outerHeight(document.querySelector("a.xx"))
  1338. * DOMUtils.outerHeight("a.xx")
  1339. * > 100
  1340. * // 获取window的外部高度
  1341. * DOMUtils.outerHeight(window)
  1342. * > 700
  1343. */
  1344. DOMUtils.outerHeight = function (element, isShow = false) {
  1345. if (CommonUtils.isWin(element)) {
  1346. return window.innerHeight;
  1347. }
  1348. if (typeof element === "string") {
  1349. element = document.querySelector(element);
  1350. }
  1351. if (element == void 0) {
  1352. return;
  1353. }
  1354. if (CommonUtils.isShow(element)) {
  1355. let style = getComputedStyle(element, null);
  1356. let marginTop = CommonUtils.getStyleValue(style, "marginTop");
  1357. let marginBottom = CommonUtils.getStyleValue(style, "marginBottom");
  1358. return element.offsetHeight + marginTop + marginBottom;
  1359. } else {
  1360. let { recovery } = CommonUtils.showElement(element);
  1361. let outerHeight = DOMUtils.outerHeight(element, true);
  1362. recovery();
  1363. return outerHeight;
  1364. }
  1365. };
  1366.  
  1367. /**
  1368. * 等待文档加载完成后执行指定的函数
  1369. * @param {Function} callback 需要执行的函数
  1370. * @example
  1371. * DOMUtils.ready(function(){
  1372. * console.log("文档加载完毕")
  1373. * })
  1374. */
  1375. DOMUtils.ready = function (callback) {
  1376. function completed() {
  1377. document.removeEventListener("DOMContentLoaded", completed);
  1378. window.removeEventListener("load", completed);
  1379. callback();
  1380. }
  1381. if (
  1382. document.readyState === "complete" ||
  1383. (document.readyState !== "loading" && !document.documentElement.doScroll)
  1384. ) {
  1385. window.setTimeout(callback);
  1386. } else {
  1387. /* 监听DOMContentLoaded事件 */
  1388. document.addEventListener("DOMContentLoaded", completed);
  1389. /* 监听load事件 */
  1390. window.addEventListener("load", completed);
  1391. }
  1392. };
  1393.  
  1394. /**
  1395. * 在一定时间内改变元素的样式属性,实现动画效果
  1396. * @param {HTMLElement|string} element 需要进行动画的元素
  1397. * @param {CSSStyleDeclaration} styles 动画结束时元素的样式属性
  1398. * @param {number} [duration=1000] 动画持续时间,单位为毫秒
  1399. * @param {Function} [callback=null] 动画结束后执行的函数
  1400. * @example
  1401. * // 监听元素a.xx的从显示变为隐藏
  1402. * DOMUtils.animate(document.querySelector("a.xx"),{ top:100},1000,function(){
  1403. * console.log("已往上位移100px")
  1404. * })
  1405. */
  1406. DOMUtils.animate = function (
  1407. element,
  1408. styles,
  1409. duration = 1000,
  1410. callback = null
  1411. ) {
  1412. if (typeof element === "string") {
  1413. element = document.querySelector(element);
  1414. }
  1415. if (element == void 0) {
  1416. return;
  1417. }
  1418. if (typeof duration !== "number" || duration <= 0) {
  1419. throw new TypeError("duration must be a positive number");
  1420. }
  1421. if (typeof callback !== "function" && callback !== void 0) {
  1422. throw new TypeError("callback must be a function or null");
  1423. }
  1424. if (typeof styles !== "object" || styles === void 0) {
  1425. throw new TypeError("styles must be an object");
  1426. }
  1427. if (Object.keys(styles).length === 0) {
  1428. throw new Error("styles must contain at least one property");
  1429. }
  1430. let start = performance.now();
  1431. let from = {};
  1432. let to = {};
  1433. for (let prop in styles) {
  1434. from[prop] = element.style[prop] || getComputedStyle(element)[prop];
  1435. to[prop] = styles[prop];
  1436. }
  1437. let timer = setInterval(function () {
  1438. let timePassed = performance.now() - start;
  1439. let progress = timePassed / duration;
  1440. if (progress > 1) {
  1441. progress = 1;
  1442. }
  1443. for (let prop in styles) {
  1444. element.style[prop] =
  1445. from[prop] + (to[prop] - from[prop]) * progress + "px";
  1446. }
  1447. if (progress === 1) {
  1448. clearInterval(timer);
  1449. if (callback) {
  1450. callback();
  1451. }
  1452. }
  1453. }, 10);
  1454. };
  1455.  
  1456. /**
  1457. * 将一个元素包裹在指定的HTML元素中
  1458. * @param {HTMLElement|string} element 要包裹的元素
  1459. * @param {string} wrapperHTML 要包裹的HTML元素的字符串表示形式
  1460. * @example
  1461. * // 将a.xx元素外面包裹一层div
  1462. * DOMUtils.wrap(document.querySelector("a.xx"),"<div></div>")
  1463. */
  1464. DOMUtils.wrap = function (element, wrapperHTML) {
  1465. if (typeof element === "string") {
  1466. element = document.querySelector(element);
  1467. }
  1468. if (element == void 0) {
  1469. return;
  1470. }
  1471. // 创建一个新的div元素,并将wrapperHTML作为其innerHTML
  1472. let wrapper = document.createElement("div");
  1473. wrapper.innerHTML = wrapperHTML;
  1474.  
  1475. wrapper = wrapper.firstChild;
  1476. // 将要包裹的元素插入目标元素前面
  1477. element.parentElement.insertBefore(wrapper, element);
  1478.  
  1479. // 将要包裹的元素移动到wrapper中
  1480. wrapper.appendChild(element);
  1481. };
  1482. /**
  1483. * 获取当前元素的前一个兄弟元素
  1484. * @param {HTMLElement|string} element 当前元素
  1485. * @returns {?HTMLElement} 前一个兄弟元素
  1486. * @example
  1487. * // 获取a.xx元素前一个兄弟元素
  1488. * DOMUtils.prev(document.querySelector("a.xx"))
  1489. * DOMUtils.prev("a.xx")
  1490. * > <div ...>....</div>
  1491. */
  1492. DOMUtils.prev = function (element) {
  1493. if (typeof element === "string") {
  1494. element = document.querySelector(element);
  1495. }
  1496. if (element == void 0) {
  1497. return;
  1498. }
  1499. return element.previousElementSibling;
  1500. };
  1501.  
  1502. /**
  1503. * 获取当前元素的后一个兄弟元素
  1504. * @param {HTMLElement|string} element 当前元素
  1505. * @returns {?HTMLElement} 后一个兄弟元素
  1506. * @example
  1507. * // 获取a.xx元素前一个兄弟元素
  1508. * DOMUtils.next(document.querySelector("a.xx"))
  1509. * DOMUtils.next("a.xx")
  1510. * > <div ...>....</div>
  1511. */
  1512. DOMUtils.next = function (element) {
  1513. if (typeof element === "string") {
  1514. element = document.querySelector(element);
  1515. }
  1516. if (element == void 0) {
  1517. return;
  1518. }
  1519. return element.nextElementSibling;
  1520. };
  1521.  
  1522. /**
  1523. * 释放原有的DOMUtils控制权
  1524. * @example
  1525. * let DOMUtils = window.DOMUtils.noConflict()
  1526. */
  1527. DOMUtils.noConflict = function () {
  1528. if (window.DOMUtils) {
  1529. delete window.DOMUtils;
  1530. }
  1531. if (AnotherDOMUtils) {
  1532. window.DOMUtils = AnotherDOMUtils;
  1533. }
  1534. return DOMUtils;
  1535. };
  1536.  
  1537. /**
  1538. * 获取当前元素的所有兄弟元素
  1539. * @param {HTMLElement|string} element 当前元素
  1540. * @returns {HTMLElement[]} 所有兄弟元素
  1541. * @example
  1542. * // 获取a.xx元素所有兄弟元素
  1543. * DOMUtils.siblings(document.querySelector("a.xx"))
  1544. * DOMUtils.siblings("a.xx")
  1545. * > (3) [div.logo-wrapper, div.forum-block, div.more-btn-desc]
  1546. */
  1547. DOMUtils.siblings = function (element) {
  1548. if (typeof element === "string") {
  1549. element = document.querySelector(element);
  1550. }
  1551. if (element == void 0) {
  1552. return;
  1553. }
  1554. return Array.from(element.parentElement.children).filter(
  1555. (child) => child !== element
  1556. );
  1557. };
  1558.  
  1559. /**
  1560. * 获取当前元素的父元素
  1561. * @param {HTMLElement|NodeList|string} element 当前元素
  1562. * @returns {?HTMLElement[]|HTMLElement} 父元素
  1563. * @example
  1564. * // 获取a.xx元素的父元素
  1565. * DOMUtils.parent(document.querySelector("a.xx"))
  1566. * DOMUtils.parent("a.xx")
  1567. * > <div ...>....</div>
  1568. */
  1569. DOMUtils.parent = function (element) {
  1570. if (typeof element === "string") {
  1571. element = document.querySelector(element);
  1572. }
  1573. if (element == void 0) {
  1574. return;
  1575. }
  1576. if (element instanceof NodeList || element instanceof Array) {
  1577. let resultArray = [];
  1578. element.forEach((item) => {
  1579. resultArray = resultArray.concat(this.parent(item));
  1580. });
  1581. return resultArray;
  1582. } else {
  1583. return element.parentElement;
  1584. }
  1585. };
  1586.  
  1587. /**
  1588. * 将字符串转为Element元素
  1589. * @param {string} html
  1590. * @param {boolean} useParser 是否使用DOMParser来生成元素,有些时候通过DOMParser生成的元素有点问题
  1591. * @param {boolean} isComplete 是否是完整的
  1592. * @returns {HTMLElement}
  1593. * @example
  1594. * // 将字符串转为Element元素
  1595. * DOMUtils.parseHTML("<a href='xxxx'></a>")
  1596. * > <a href="xxxx"></a>
  1597. * @example
  1598. * // 使用DOMParser将字符串转为Element元素
  1599. * DOMUtils.parseHTML("<a href='xxxx'></a>",true)
  1600. * > <a href="xxxx"></a>
  1601. * @example
  1602. * // 由于需要转换的元素是多个元素,将字符串转为完整的Element元素
  1603. * DOMUtils.parseHTML("<a href='xxxx'></a><a href='xxxx'></a>",false, true)
  1604. * > <div><a href="xxxx"></a><a href='xxxx'></a></div>
  1605. * @example
  1606. * // 由于需要转换的元素是多个元素,使用DOMParser将字符串转为完整的Element元素
  1607. * DOMUtils.parseHTML("<a href='xxxx'></a><a href='xxxx'></a>",true, true)
  1608. * > #document
  1609. */
  1610. DOMUtils.parseHTML = function (html, useParser = false, isComplete = false) {
  1611. function parseHTMLByDOMParser() {
  1612. let parser = new DOMParser();
  1613. if (isComplete) {
  1614. return parser.parseFromString(html, "text/html");
  1615. } else {
  1616. return parser.parseFromString(html, "text/html").body.firstChild;
  1617. }
  1618. }
  1619. function parseHTMLByCreateDom() {
  1620. let tempDIV = document.createElement("div");
  1621. tempDIV.innerHTML = html;
  1622. if (isComplete) {
  1623. return tempDIV;
  1624. } else {
  1625. return tempDIV.firstChild;
  1626. }
  1627. }
  1628. if (useParser) {
  1629. return parseHTMLByDOMParser();
  1630. } else {
  1631. return parseHTMLByCreateDom();
  1632. }
  1633. };
  1634.  
  1635. /**
  1636. * 当鼠标移入或移出元素时触发事件
  1637. * @param {HTMLElement|string} element 当前元素
  1638. * @param {(event:Event)=>{}} handler 事件处理函数
  1639. * @example
  1640. * // 监听a.xx元素的移入或移出
  1641. * DOMUtils.hover(document.querySelector("a.xx"),()=>{
  1642. * console.log("移入/移除");
  1643. * })
  1644. * DOMUtils.hover("a.xx",()=>{
  1645. * console.log("移入/移除");
  1646. * })
  1647. */
  1648. DOMUtils.hover = function (element, handler) {
  1649. if (typeof element === "string") {
  1650. element = document.querySelector(element);
  1651. }
  1652. if (element == void 0) {
  1653. return;
  1654. }
  1655. DOMUtils.on(element, "mouseenter", null, handler);
  1656. DOMUtils.on(element, "mouseleave", null, handler);
  1657. };
  1658.  
  1659. /**
  1660. * 显示元素
  1661. * @param {HTMLElement|string} element 当前元素
  1662. * @example
  1663. * // 显示a.xx元素
  1664. * DOMUtils.show(document.querySelector("a.xx"))
  1665. * DOMUtils.show("a.xx")
  1666. */
  1667. DOMUtils.show = function (element) {
  1668. if (typeof element === "string") {
  1669. element = document.querySelector(element);
  1670. }
  1671. if (element == void 0) {
  1672. return;
  1673. }
  1674. element.style.display = "";
  1675. };
  1676.  
  1677. /**
  1678. * 隐藏元素
  1679. * @param {HTMLElement|string} element 当前元素
  1680. * @example
  1681. * // 隐藏a.xx元素
  1682. * DOMUtils.hide(document.querySelector("a.xx"))
  1683. * DOMUtils.hide("a.xx")
  1684. */
  1685. DOMUtils.hide = function (element) {
  1686. if (typeof element === "string") {
  1687. element = document.querySelector(element);
  1688. }
  1689. if (element == void 0) {
  1690. return;
  1691. }
  1692. element.style.display = "none";
  1693. };
  1694.  
  1695. /**
  1696. * 当按键松开时触发事件
  1697. * @param {HTMLElement|string} element 当前元素
  1698. * @param {Function} handler 事件处理函数
  1699. * @example
  1700. * // 监听a.xx元素的按键松开
  1701. * DOMUtils.keyup(document.querySelector("a.xx"),()=>{
  1702. * console.log("按键松开");
  1703. * })
  1704. * DOMUtils.keyup("a.xx",()=>{
  1705. * console.log("按键松开");
  1706. * })
  1707. */
  1708. DOMUtils.keyup = function (element, handler) {
  1709. if (typeof element === "string") {
  1710. element = document.querySelector(element);
  1711. }
  1712. if (element == void 0) {
  1713. return;
  1714. }
  1715. DOMUtils.on(element, "keyup", null, handler);
  1716. };
  1717.  
  1718. /**
  1719. * 当按键按下时触发事件
  1720. * @param {HTMLElement|string} element 当前元素
  1721. * @param {Function} handler 事件处理函数
  1722. * @example
  1723. * // 监听a.xx元素的按键按下
  1724. * DOMUtils.keydown(document.querySelector("a.xx"),()=>{
  1725. * console.log("按键按下");
  1726. * })
  1727. * DOMUtils.keydown("a.xx",()=>{
  1728. * console.log("按键按下");
  1729. * })
  1730. */
  1731. DOMUtils.keydown = function (element, handler) {
  1732. if (typeof element === "string") {
  1733. element = document.querySelector(element);
  1734. }
  1735. if (element == void 0) {
  1736. return;
  1737. }
  1738. DOMUtils.on(element, "keydown", null, handler);
  1739. };
  1740.  
  1741. /**
  1742. * 淡入元素
  1743. * @param {HTMLElement|string} element 当前元素
  1744. * @param {number} [duration=400] 动画持续时间(毫秒),默认400毫秒
  1745. * @param {Function} callback 动画结束的回调
  1746. * @example
  1747. * // 元素a.xx淡入
  1748. * DOMUtils.fadeIn(document.querySelector("a.xx"),2500,()=>{
  1749. * console.log("淡入完毕");
  1750. * })
  1751. * DOMUtils.fadeIn("a.xx",undefined,()=>{
  1752. * console.log("淡入完毕");
  1753. * })
  1754. */
  1755. DOMUtils.fadeIn = function (element, duration = 400, callback) {
  1756. if (typeof element === "string") {
  1757. element = document.querySelector(element);
  1758. }
  1759. if (element == void 0) {
  1760. return;
  1761. }
  1762. element.style.opacity = 0;
  1763. element.style.display = "";
  1764. let start = null;
  1765. let timer = null;
  1766. function step(timestamp) {
  1767. if (!start) start = timestamp;
  1768. let progress = timestamp - start;
  1769. element.style.opacity = Math.min(progress / duration, 1);
  1770. if (progress < duration) {
  1771. window.requestAnimationFrame(step);
  1772. } else {
  1773. if (callback && typeof callback === "function") {
  1774. callback();
  1775. }
  1776. window.cancelAnimationFrame(timer);
  1777. }
  1778. }
  1779. timer = window.requestAnimationFrame(step);
  1780. };
  1781.  
  1782. /**
  1783. * 淡出元素
  1784. * @param {HTMLElement|string} element 当前元素
  1785. * @param {number} [duration=400] 动画持续时间(毫秒),默认400毫秒
  1786. * @param {Function} callback 动画结束的回调
  1787. * @example
  1788. * // 元素a.xx淡出
  1789. * DOMUtils.fadeOut(document.querySelector("a.xx"),2500,()=>{
  1790. * console.log("淡出完毕");
  1791. * })
  1792. * DOMUtils.fadeOut("a.xx",undefined,()=>{
  1793. * console.log("淡出完毕");
  1794. * })
  1795. */
  1796. DOMUtils.fadeOut = function (element, duration = 400, callback) {
  1797. if (typeof element === "string") {
  1798. element = document.querySelector(element);
  1799. }
  1800. if (element == void 0) {
  1801. return;
  1802. }
  1803. element.style.opacity = 1;
  1804. let start = null;
  1805. let timer = null;
  1806. function step(timestamp) {
  1807. if (!start) start = timestamp;
  1808. let progress = timestamp - start;
  1809. element.style.opacity = Math.max(1 - progress / duration, 0);
  1810. if (progress < duration) {
  1811. window.requestAnimationFrame(step);
  1812. } else {
  1813. element.style.display = "none";
  1814. if (typeof callback === "function") {
  1815. callback();
  1816. }
  1817. window.cancelAnimationFrame(timer);
  1818. }
  1819. }
  1820. timer = window.requestAnimationFrame(step);
  1821. };
  1822.  
  1823. /**
  1824. * 切换元素的显示和隐藏状态
  1825. * @param {HTMLElement|string} element 当前元素
  1826. * @example
  1827. * // 如果元素a.xx当前是隐藏,则显示,如果是显示,则隐藏
  1828. * DOMUtils.toggle(document.querySelector("a.xx"))
  1829. * DOMUtils.toggle("a.xx")
  1830. */
  1831. DOMUtils.toggle = function (element) {
  1832. if (typeof element === "string") {
  1833. element = document.querySelector(element);
  1834. }
  1835. if (element == void 0) {
  1836. return;
  1837. }
  1838. if (getComputedStyle(element).getPropertyValue("display") === "none") {
  1839. DOMUtils.show(element);
  1840. } else {
  1841. DOMUtils.hide(element);
  1842. }
  1843. };
  1844. return DOMUtils;
  1845. });