DOMUtils

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

当前为 2023-09-20 提交的版本,查看 最新版本

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

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