DOMUtils

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

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

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/465772/1248101/DOMUtils.js

  1. /**
  2. * 自己常用的元素工具类
  3. * @copyright GPL-3.0-only
  4. * @author WhiteSev
  5. **/
  6. (function (global, factory) {
  7. typeof exports === "object" && typeof module !== "undefined"
  8. ? (module.exports = factory())
  9. : typeof define === "function" && define.amd
  10. ? define(factory)
  11. : ((global =
  12. typeof globalThis !== "undefined" ? globalThis : global || self),
  13. (global.DOMUtils = factory(global.DOMUtils)));
  14. })(this, function (oldDOMUtilsObj) {
  15. const DOMUtils = {};
  16. /**
  17. * @type {string} 元素工具类的版本
  18. */
  19. DOMUtils.version = "2023-9-10";
  20. /**
  21. * 获取或设置元素的属性值
  22. * @param {HTMLElement|string} element 目标元素
  23. * @param {string} attrName 属性名
  24. * @param {string} [attrValue] 属性值(可选)
  25. * @returns {?string} 如果传入了attrValue,则返回undefined;否则返回属性值
  26. * */
  27. DOMUtils.attr = function (element, attrName, attrValue) {
  28. if (typeof element === "string") {
  29. element = document.querySelector(element);
  30. }
  31. if (element == null) {
  32. return;
  33. }
  34. if (attrValue === undefined) {
  35. return element.getAttribute(attrName);
  36. } else {
  37. element.setAttribute(attrName, attrValue);
  38. }
  39. };
  40.  
  41. /**
  42. * 创建元素
  43. * @param {string} tagName 元素类型
  44. * @param {object} property 元素属性
  45. * @returns {Element}
  46. */
  47. DOMUtils.createElement = function (tagName, property) {
  48. let tempElement = document.createElement(tagName);
  49. if (typeof property === "string") {
  50. tempElement.innerHTML = property;
  51. return tempElement;
  52. }
  53. if (property == null) {
  54. return tempElement;
  55. }
  56. Object.keys(property).forEach((key) => {
  57. if (key in tempElement && typeof property[key] !== "object") {
  58. tempElement[key] = property[key];
  59. } else {
  60. tempElement.setAttribute(key, property[key]);
  61. }
  62. });
  63. return tempElement;
  64. };
  65.  
  66. /**
  67. * 获取或设置元素的样式属性值
  68. * @param {Element|string} element 目标元素
  69. * @param {string|object} property 样式属性名或包含多个属性名和属性值的对象
  70. * @param {string} [value] 样式属性值(可选)
  71. * @returns {?string} 如果传入了value,则返回undefined;否则返回样式属性值
  72. * */
  73. DOMUtils.css = function (element, property, value) {
  74. /**
  75. * 把纯数字没有px的加上
  76. */
  77. function handlePixe(propertyName, propertyValue) {
  78. let allowAddPixe = ["width", "height", "top", "left", "right", "bottom"];
  79. if (typeof propertyValue === "number") {
  80. propertyValue = propertyValue.toString();
  81. }
  82. if (
  83. typeof propertyValue === "string" &&
  84. allowAddPixe.includes(propertyName) &&
  85. propertyValue.match(/[0-9]$/gi)
  86. ) {
  87. propertyValue = propertyValue + "px";
  88. }
  89. return propertyValue;
  90. }
  91. if (typeof element === "string") {
  92. element = document.querySelector(element);
  93. }
  94. if (element == null) {
  95. return;
  96. }
  97. if (typeof property === "string") {
  98. if (value === undefined) {
  99. return element.style[property];
  100. } else {
  101. if (value === "string" && value.includes("!important")) {
  102. element.style.setProperty(property, value, "important");
  103. } else {
  104. value = handlePixe(property, value);
  105. element.style.setProperty(property, value);
  106. }
  107. }
  108. } else if (typeof property === "object") {
  109. for (let prop in property) {
  110. if (
  111. typeof property[prop] === "string" &&
  112. property[prop].includes("!important")
  113. ) {
  114. element.style.setProperty(prop, property[prop], "important");
  115. } else {
  116. property[prop] = handlePixe(prop, property[prop]);
  117. element.style.setProperty(prop, property[prop]);
  118. }
  119. }
  120. }
  121. };
  122. /**
  123. * 获取或设置元素的文本内容
  124. * @param {Element|element} element 目标元素
  125. * @param {string} [text] 文本内容(可选)
  126. * @returns {?string} 如果传入了text,则返回undefined;否则返回文本内容
  127. * */
  128. DOMUtils.text = function (element, text) {
  129. if (typeof element === "string") {
  130. element = document.querySelector(element);
  131. }
  132. if (element == null) {
  133. return;
  134. }
  135. if (text === undefined) {
  136. return element.textContent || element.innerText;
  137. } else {
  138. if ("textContent" in element) {
  139. element.textContent = text;
  140. } else if ("innerText" in element) {
  141. element.innerText = text;
  142. }
  143. }
  144. };
  145. /**
  146. * 获取或设置元素的HTML内容
  147. * @param {Element|string} element 目标元素
  148. * @param {string} [html] HTML内容(可选)
  149. * @returns {?string} 如果传入了html,则返回undefined;否则返回HTML内容
  150. * */
  151. DOMUtils.html = function (element, html) {
  152. if (typeof element === "string") {
  153. element = document.querySelector(element);
  154. }
  155. if (element == null) {
  156. return;
  157. }
  158. if (html == null) {
  159. return element.innerHTML;
  160. } else {
  161. if ("innerHTML" in element) {
  162. element.innerHTML = html;
  163. }
  164. }
  165. };
  166. /**
  167. * 绑定或触发元素的click事件
  168. * @param {Element|string} element 目标元素
  169. * @param {function} [handler] 事件处理函数(可选)
  170. * */
  171. DOMUtils.click = function (element, handler) {
  172. if (typeof element === "string") {
  173. element = document.querySelector(element);
  174. }
  175. if (element == null) {
  176. return;
  177. }
  178. if (handler === undefined) {
  179. DOMUtils.trigger(element, "click");
  180. } else {
  181. DOMUtils.on(element, "click", null, handler);
  182. }
  183. };
  184.  
  185. /**
  186. * 绑定或触发元素的blur事件
  187. * @param {Element|string} element 目标元素
  188. * @param {function} [handler] 事件处理函数(可选)
  189. * */
  190. DOMUtils.blur = function (element, handler) {
  191. if (typeof element === "string") {
  192. element = document.querySelector(element);
  193. }
  194. if (element == null) {
  195. return;
  196. }
  197. if (handler === undefined) {
  198. DOMUtils.trigger(element, "blur");
  199. } else {
  200. DOMUtils.on(element, "blur", null, handler);
  201. }
  202. };
  203. /**
  204. * 绑定或触发元素的focus事件
  205. * @param {Element|string} element 目标元素
  206. * @param {?function} [handler] 事件处理函数(可选)
  207. * */
  208. DOMUtils.focus = function (element, handler) {
  209. if (typeof element === "string") {
  210. element = document.querySelector(element);
  211. }
  212. if (element == null) {
  213. return;
  214. }
  215. if (handler === undefined) {
  216. DOMUtils.trigger(element, "focus");
  217. } else {
  218. DOMUtils.on(element, "focus", null, handler);
  219. }
  220. };
  221. /**
  222. * 获取或设置元素的value属性值
  223. * @param {Element|string} element 目标元素
  224. * @param {string} [value] value属性值(可选)
  225. * @returns {string|undefined} 如果传入了value,则返回undefined;否则返回value属性值
  226. * */
  227. DOMUtils.val = function (element, value) {
  228. if (typeof element === "string") {
  229. element = document.querySelector(element);
  230. }
  231. if (element == null) {
  232. return;
  233. }
  234. if (value == null) {
  235. if (
  236. element.localName === "input" &&
  237. (element.type === "checkbox" || element.type === "radio")
  238. ) {
  239. return element.checked;
  240. } else {
  241. return element.value;
  242. }
  243. } else {
  244. if (
  245. element.localName === "input" &&
  246. (element.type === "checkbox" || element.type === "radio")
  247. ) {
  248. element.checked = !!value;
  249. } else {
  250. element.value = value;
  251. }
  252. }
  253. };
  254. /**
  255. * 获取或设置元素的属性值
  256. * @param {Element|string} element 目标元素
  257. * @param {string} propName 属性名
  258. * @param {string} [propValue] 属性值(可选)
  259. * @returns {string|undefined} 如果传入了propValue,则返回undefined;否则返回属性值
  260. * */
  261. DOMUtils.prop = function (element, propName, propValue) {
  262. if (typeof element === "string") {
  263. element = document.querySelector(element);
  264. }
  265. if (element == null) {
  266. return;
  267. }
  268. if (propValue == null) {
  269. return element[propName];
  270. } else {
  271. element[propName] = propValue;
  272. }
  273. };
  274.  
  275. /**
  276. * 移除元素的属性
  277. * @param {Element|string} element 目标元素
  278. * @param {string} attrName 属性名
  279. * */
  280. DOMUtils.removeAttr = function (element, attrName) {
  281. if (typeof element === "string") {
  282. element = document.querySelector(element);
  283. }
  284. if (element == null) {
  285. return;
  286. }
  287. element.removeAttribute(attrName);
  288. };
  289.  
  290. /**
  291. * 移除元素class名
  292. * @param {Element|string} element 目标元素
  293. * @param {string} className class名
  294. * @returns {DOMUtils} 原型链
  295. */
  296. DOMUtils.removeClass = function (element, className) {
  297. if (typeof element === "string") {
  298. element = document.querySelector(element);
  299. }
  300. if (element == null) {
  301. return;
  302. }
  303. element.classList.remove(className);
  304. };
  305.  
  306. /**
  307. * 移除元素的属性
  308. * @param {Element|string} element 目标元素
  309. * @param {string} propName 属性名
  310. * */
  311. DOMUtils.removeProp = function (element, propName) {
  312. if (typeof element === "string") {
  313. element = document.querySelector(element);
  314. }
  315. if (element == null) {
  316. return;
  317. }
  318. delete element[propName];
  319. };
  320.  
  321. /**
  322. * 将一个元素替换为另一个元素
  323. * @param {Element|string} element 目标元素
  324. * @param {Element|string} newElement 新元素
  325. * @returns {DOMUtils} 原型链
  326. */
  327. DOMUtils.replaceWith = function (element, newElement) {
  328. if (typeof element === "string") {
  329. element = document.querySelector(element);
  330. }
  331. if (element == null) {
  332. return;
  333. }
  334. if (typeof newElement === "string") {
  335. newElement = DOMUtils.parseHTML(newElement, false, false);
  336. }
  337. if (element instanceof NodeList || element instanceof Array) {
  338. element.forEach((item) => {
  339. DOMUtils.replaceWith(item, newElement);
  340. });
  341. } else {
  342. element.parentElement.replaceChild(newElement, element);
  343. }
  344. };
  345.  
  346. /**
  347. * 给元素添加class
  348. * @param {Element|string} element 目标元素
  349. * @param {string} className class名
  350. * */
  351. DOMUtils.addClass = function (element, className) {
  352. if (typeof element === "string") {
  353. element = document.querySelector(element);
  354. }
  355. if (element == null) {
  356. return;
  357. }
  358. element.classList.add(className);
  359. };
  360. /**
  361. * 函数在元素内部末尾添加子元素或HTML字符串
  362. * @param {Element|string} element 目标元素
  363. * @param {object|string} content 子元素或HTML字符串
  364. * */
  365. DOMUtils.append = function (element, content) {
  366. if (typeof element === "string") {
  367. element = document.querySelector(element);
  368. }
  369. if (element == null) {
  370. return;
  371. }
  372. if (typeof content === "string") {
  373. element.insertAdjacentHTML("beforeend", content);
  374. } else {
  375. element.appendChild(content);
  376. }
  377. };
  378.  
  379. /**
  380. * 函数 在元素内部开头添加子元素或HTML字符串
  381. * @param {Element|string} element 目标元素
  382. * @param {object|string} content 子元素或HTML字符串
  383. * */
  384. DOMUtils.prepend = function (element, content) {
  385. if (typeof element === "string") {
  386. element = document.querySelector(element);
  387. }
  388. if (element == null) {
  389. return;
  390. }
  391. if (typeof content === "string") {
  392. element.insertAdjacentHTML("afterbegin", content);
  393. } else {
  394. element.insertBefore(content, element.firstChild);
  395. }
  396. };
  397. /**
  398. * 在元素后面添加兄弟元素或HTML字符串
  399. * @param {Element|string} element 目标元素
  400. * @param {object|string} content 兄弟元素或HTML字符串
  401. * */
  402. DOMUtils.after = function (element, content) {
  403. if (typeof element === "string") {
  404. element = document.querySelector(element);
  405. }
  406. if (element == null) {
  407. return;
  408. }
  409. if (typeof content === "string") {
  410. element.insertAdjacentHTML("afterend", content);
  411. } else {
  412. element.parentElement.insertBefore(content, element.nextSibling);
  413. }
  414. };
  415.  
  416. /**
  417. * 在元素前面添加兄弟元素或HTML字符串
  418. * @param {Element|string} element 目标元素
  419. * @param {object|string} content 兄弟元素或HTML字符串
  420. * */
  421. DOMUtils.before = function (element, content) {
  422. if (typeof element === "string") {
  423. element = document.querySelector(element);
  424. }
  425. if (element == null) {
  426. return;
  427. }
  428. if (typeof content === "string") {
  429. element.insertAdjacentHTML("beforebegin", content);
  430. } else {
  431. element.parentElement.insertBefore(content, element);
  432. }
  433. };
  434.  
  435. /**
  436. * 移除元素
  437. * @param {Element|string|NodeList} element 目标元素
  438. * */
  439. DOMUtils.remove = function (element) {
  440. if (typeof element === "string") {
  441. element = document.querySelectorAll(element);
  442. }
  443. if (element == null) {
  444. return;
  445. }
  446. if (element instanceof NodeList || element instanceof Array) {
  447. element.forEach(function (item) {
  448. DOMUtils.remove(item);
  449. });
  450. } else {
  451. element.remove();
  452. }
  453. };
  454. /**
  455. * 移除元素的所有子元素
  456. * @param {Element|string} element 目标元素
  457. * */
  458. DOMUtils.empty = function (element) {
  459. if (typeof element === "string") {
  460. element = document.querySelector(element);
  461. }
  462. if (element == null) {
  463. return;
  464. }
  465. while (element.firstChild) {
  466. element.removeChild(element.firstChild);
  467. }
  468. };
  469. /**
  470. * 绑定事件
  471. * @param {Element|string} element 需要绑定的元素
  472. * @param {string|Array} eventType 需要监听的事件
  473. * @param {HTMLElement?} selector 子元素选择器
  474. * @param {Function} callback 事件触发的回调函数
  475. * @param {Boolean} capture 表示事件是否在捕获阶段触发。默认为false,即在冒泡阶段触发
  476. * @param {Boolean} once 表示事件是否只触发一次。默认为false
  477. * @param {Boolean} passive 表示事件监听器是否不会调用preventDefault()。默认为false
  478. */
  479. DOMUtils.on = function (
  480. element,
  481. eventType,
  482. selector,
  483. callback,
  484. capture = false,
  485. once = false,
  486. passive = false
  487. ) {
  488. if (typeof element === "string") {
  489. element = document.querySelector(element);
  490. }
  491. if (element == null) {
  492. return;
  493. }
  494. let eventTypeList = [];
  495. if (Array.isArray(eventType)) {
  496. eventTypeList = eventType;
  497. } else if (typeof eventType === "string") {
  498. eventTypeList = eventType.split(" ");
  499. }
  500. if (typeof selector === "function") {
  501. /* 这是为没有selector的情况 */
  502. callback = selector;
  503. selector = null;
  504. }
  505. eventTypeList.forEach((_eventType_) => {
  506. if (selector) {
  507. element.addEventListener(
  508. _eventType_,
  509. function (event) {
  510. let target = event.target;
  511. if (target.matches(selector)) {
  512. callback.call(target, event);
  513. }
  514. },
  515. capture,
  516. once,
  517. passive
  518. );
  519. } else {
  520. element.addEventListener(
  521. _eventType_,
  522. function (event) {
  523. callback.call(event.target, event);
  524. },
  525. capture,
  526. once,
  527. passive
  528. );
  529. }
  530. });
  531.  
  532. if (callback && callback.delegate) {
  533. element.setAttribute("data-delegate", selector);
  534. }
  535.  
  536. let events = element.events || {};
  537. events[eventType] = events[eventType] || [];
  538. events[eventType].push({
  539. selector: selector,
  540. callback: callback,
  541. });
  542. element.events = events;
  543. };
  544. /**
  545. * 取消绑定事件
  546. * @param {Element|string} element 需要取消绑定的元素
  547. * @param {string|Array} eventType 需要取消监听的事件
  548. * @param {HTMLElement} selector 子元素选择器
  549. * @param {Function} callback 事件触发的回调函数
  550. * @param {Boolean} useCapture 表示事件是否在捕获阶段处理,它是一个可选参数,默认为false,表示在冒泡阶段处理事件。
  551. * 如果在添加事件监听器时指定了useCapture为true,则在移除事件监听器时也必须指定为true
  552. */
  553. DOMUtils.off = function (
  554. element,
  555. eventType,
  556. selector,
  557. callback,
  558. useCapture = false
  559. ) {
  560. if (typeof element === "string") {
  561. element = document.querySelector(element);
  562. }
  563. if (element == null) {
  564. return;
  565. }
  566. let events = element.events || {};
  567. let eventTypeList = [];
  568. if (!eventType) {
  569. for (let type in events) {
  570. eventTypeList = [...eventTypeList, type];
  571. }
  572. } else if (Array.isArray(eventType)) {
  573. eventTypeList = eventType;
  574. } else if (typeof eventType === "string") {
  575. eventTypeList = eventType.split(" ");
  576. }
  577. eventTypeList.forEach((_eventType_) => {
  578. let handlers = events[eventType] || [];
  579. for (let i = 0; i < handlers.length; i++) {
  580. if (
  581. (!selector || handlers[i].selector === selector) &&
  582. (!callback || handlers[i].callback === callback)
  583. ) {
  584. element.removeEventListener(
  585. _eventType_,
  586. handlers[i].callback,
  587. useCapture
  588. );
  589. handlers.splice(i--, 1);
  590. }
  591. }
  592. if (handlers.length === 0) {
  593. delete events[eventType];
  594. }
  595. });
  596. element.events = events;
  597. };
  598. /**
  599. * 主动触发事件
  600. * @param {Element|string} element 需要触发的元素
  601. * @param {String|Array} eventType 需要触发的事件
  602. */
  603. DOMUtils.trigger = function (element, eventType) {
  604. if (typeof element === "string") {
  605. element = document.querySelector(element);
  606. }
  607. if (element == null) {
  608. return;
  609. }
  610. let events = element.events || {};
  611. let eventTypeList = [];
  612. if (!eventType) {
  613. for (let type in events) {
  614. eventTypeList = [...eventTypeList, type];
  615. }
  616. } else if (Array.isArray(eventType)) {
  617. eventTypeList = eventType;
  618. } else if (typeof eventType === "string") {
  619. eventTypeList = eventType.split(" ");
  620. }
  621. eventTypeList.forEach((_eventType_) => {
  622. let event = new Event(_eventType_);
  623. element.dispatchEvent(event);
  624. });
  625. };
  626. /**
  627. * 设置或返回被选元素相对于文档的偏移坐标
  628. * @param {Element|string} element
  629. * @returns {Object}
  630. */
  631. DOMUtils.offset = function (element) {
  632. if (typeof element === "string") {
  633. element = document.querySelector(element);
  634. }
  635. if (element == null) {
  636. return;
  637. }
  638. let rect = element.getBoundingClientRect();
  639. let win = window;
  640. return {
  641. top: rect.top + win.scrollY,
  642. left: rect.left + win.scrollX,
  643. };
  644. };
  645. /**
  646. * 获取元素的宽度
  647. * @param {Element|string} element 要获取宽度的元素
  648. * @returns {Number} 元素的宽度,单位为像素
  649. */
  650. DOMUtils.width = function (element) {
  651. if (element == window) {
  652. return window.document.documentElement.clientWidth;
  653. }
  654. if (typeof element === "string") {
  655. element = document.querySelector(element);
  656. }
  657. if (element == null) {
  658. return;
  659. }
  660. if (element.nodeType === 9) {
  661. /* 文档节点 */
  662. return Math.max(
  663. element.body.scrollWidth,
  664. element.documentElement.scrollWidth,
  665. element.body.offsetWidth,
  666. element.documentElement.offsetWidth,
  667. element.documentElement.clientWidth
  668. );
  669. }
  670. let oldCSS_display = element.style.display;
  671. let oldCSS_visibility = element.style.visibility;
  672. let oldCSS_position = element.style.position;
  673. element.style.display = "block";
  674. element.style.visibility = "hidden";
  675. element.style.position = "absolute";
  676. let view = element.ownerDocument.defaultView;
  677. if (!view || !view.opener) {
  678. view = window;
  679. }
  680. let styles = view.getComputedStyle(element);
  681. let elementWidth =
  682. element.clientWidth -
  683. parseFloat(styles.paddingLeft) -
  684. parseFloat(styles.paddingRight);
  685. element.style.display = oldCSS_display;
  686. element.style.visibility = oldCSS_visibility;
  687. element.style.position = oldCSS_position;
  688. return elementWidth;
  689. };
  690. /**
  691. * 获取元素的高度
  692. * @param {Element|string} element 要获取高度的元素
  693. * @returns {Number} 元素的高度,单位为像素
  694. */
  695. DOMUtils.height = function (element) {
  696. if (element == window) {
  697. return window.document.documentElement.clientHeight;
  698. }
  699. if (typeof element === "string") {
  700. element = document.querySelector(element);
  701. }
  702. if (element == null) {
  703. return;
  704. }
  705. if (element.nodeType === 9) {
  706. /* 文档节点 */
  707. return Math.max(
  708. element.body.scrollHeight,
  709. element.documentElement.scrollHeight,
  710. element.body.offsetHeight,
  711. element.documentElement.offsetHeight,
  712. element.documentElement.clientHeight
  713. );
  714. }
  715. let oldCSS_display = element.style.display;
  716. let oldCSS_visibility = element.style.visibility;
  717. let oldCSS_position = element.style.position;
  718. element.style.display = "block";
  719. element.style.visibility = "hidden";
  720. element.style.position = "absolute";
  721. let view = element.ownerDocument.defaultView;
  722. if (!view || !view.opener) {
  723. view = window;
  724. }
  725. let styles = view.getComputedStyle(element);
  726. let elementHeight =
  727. element.clientHeight -
  728. parseFloat(styles.paddingTop) -
  729. parseFloat(styles.paddingBottom);
  730. element.style.display = oldCSS_display;
  731. element.style.visibility = oldCSS_visibility;
  732. element.style.position = oldCSS_position;
  733. return elementHeight;
  734. };
  735. /**
  736. * 获取元素的外部宽度(包括边框和外边距)
  737. * @param {Element|string} element 要获取外部宽度的元素
  738. * @returns {Number} 元素的外部宽度,单位为像素
  739. */
  740. DOMUtils.outerWidth = function (element) {
  741. if (element == window) {
  742. return window.innerWidth;
  743. }
  744. if (typeof element === "string") {
  745. element = document.querySelector(element);
  746. }
  747. if (element == null) {
  748. return;
  749. }
  750. let style = getComputedStyle(element, null);
  751. return (
  752. element.offsetWidth +
  753. parseFloat(style.marginLeft) +
  754. parseFloat(style.marginRight)
  755. );
  756. };
  757. /**
  758. * 获取元素的外部高度(包括边框和外边距)
  759. * @param {Element|string} element 要获取外部高度的元素
  760. * @returns {Number} 元素的外部高度,单位为像素
  761. */
  762. DOMUtils.outerHeight = function (element) {
  763. if (element == window) {
  764. return window.innerHeight;
  765. }
  766. if (typeof element === "string") {
  767. element = document.querySelector(element);
  768. }
  769. if (element == null) {
  770. return;
  771. }
  772. let style = getComputedStyle(element, null);
  773. return (
  774. element.offsetHeight +
  775. parseFloat(style.marginTop) +
  776. parseFloat(style.marginBottom)
  777. );
  778. };
  779.  
  780. /**
  781. * 等待文档加载完成后执行指定的函数
  782. * @param {Function} callback 需要执行的函数
  783. */
  784. DOMUtils.ready = function (callback) {
  785. function completed() {
  786. document.removeEventListener("DOMContentLoaded", completed);
  787. window.removeEventListener("load", completed);
  788. callback();
  789. }
  790. if (
  791. document.readyState === "complete" ||
  792. (document.readyState !== "loading" && !document.documentElement.doScroll)
  793. ) {
  794. window.setTimeout(callback);
  795. } else {
  796. /* 监听DOMContentLoaded事件 */
  797. document.addEventListener("DOMContentLoaded", completed);
  798. /* 监听load事件 */
  799. window.addEventListener("load", completed);
  800. }
  801. };
  802.  
  803. /**
  804. * 在一定时间内改变元素的样式属性,实现动画效果
  805. * @param {Element|string} element 需要进行动画的元素
  806. * @param {Object} styles 动画结束时元素的样式属性
  807. * @param {Number} [duration=1000] 动画持续时间,单位为毫秒
  808. * @param {Function} [callback=null] 动画结束后执行的函数
  809. */
  810. DOMUtils.animate = function (
  811. element,
  812. styles,
  813. duration = 1000,
  814. callback = null
  815. ) {
  816. if (typeof element === "string") {
  817. element = document.querySelector(element);
  818. }
  819. if (element == null) {
  820. return;
  821. }
  822. if (typeof duration !== "number" || duration <= 0) {
  823. throw new TypeError("duration must be a positive number");
  824. }
  825. if (typeof callback !== "function" && callback !== null) {
  826. throw new TypeError("callback must be a function or null");
  827. }
  828. if (typeof styles !== "object" || styles === null) {
  829. throw new TypeError("styles must be an object");
  830. }
  831. if (Object.keys(styles).length === 0) {
  832. throw new Error("styles must contain at least one property");
  833. }
  834. let start = performance.now();
  835. let from = {};
  836. let to = {};
  837. for (let prop in styles) {
  838. from[prop] = element.style[prop] || getComputedStyle(element)[prop];
  839. to[prop] = styles[prop];
  840. }
  841. let timer = setInterval(function () {
  842. let timePassed = performance.now() - start;
  843. let progress = timePassed / duration;
  844. if (progress > 1) {
  845. progress = 1;
  846. }
  847. for (let prop in styles) {
  848. element.style[prop] =
  849. from[prop] + (to[prop] - from[prop]) * progress + "px";
  850. }
  851. if (progress === 1) {
  852. clearInterval(timer);
  853. if (callback) {
  854. callback();
  855. }
  856. }
  857. }, 10);
  858. };
  859.  
  860. /**
  861. * 将一个元素包裹在指定的HTML元素中
  862. * @param {Element|string} element 要包裹的元素
  863. * @param {string} wrapperHTML 要包裹的HTML元素的字符串表示形式
  864. */
  865. DOMUtils.wrap = function (element, wrapperHTML) {
  866. if (typeof element === "string") {
  867. element = document.querySelector(element);
  868. }
  869. if (element == null) {
  870. return;
  871. }
  872. // 创建一个新的div元素,并将wrapperHTML作为其innerHTML
  873. let wrapper = document.createElement("div");
  874. wrapper.innerHTML = wrapperHTML;
  875.  
  876. // 将要包裹的元素插入到wrapper中
  877. element.parentElement.insertBefore(wrapper, element);
  878.  
  879. // 将要包裹的元素移动到wrapper中
  880. wrapper.insertBefore(element, wrapper.firstChild);
  881. };
  882. /**
  883. * 获取当前元素的前一个兄弟元素
  884. * @param {Element|string} element 当前元素
  885. * @returns {Element} 前一个兄弟元素
  886. */
  887. DOMUtils.prev = function (element) {
  888. if (typeof element === "string") {
  889. element = document.querySelector(element);
  890. }
  891. if (element == null) {
  892. return;
  893. }
  894. return element.previousElementSibling;
  895. };
  896.  
  897. /**
  898. * 获取当前元素的后一个兄弟元素
  899. * @param {Element|string} element 当前元素
  900. * @returns {Element} 后一个兄弟元素
  901. */
  902. DOMUtils.next = function (element) {
  903. if (typeof element === "string") {
  904. element = document.querySelector(element);
  905. }
  906. if (element == null) {
  907. return;
  908. }
  909. return element.nextElementSibling;
  910. };
  911.  
  912. /**
  913. * 释放原有的DOMUtils控制权
  914. * @returns
  915. */
  916. DOMUtils.noConflict = function () {
  917. if (window.DOMUtils) {
  918. delete window.DOMUtils;
  919. }
  920. if (oldDOMUtilsObj) {
  921. window.DOMUtils = oldDOMUtilsObj;
  922. }
  923. return DOMUtils;
  924. };
  925.  
  926. /**
  927. * 获取当前元素的所有兄弟元素
  928. * @param {Element|string} element 当前元素
  929. * @returns {Array} 所有兄弟元素
  930. */
  931. DOMUtils.siblings = function (element) {
  932. if (typeof element === "string") {
  933. element = document.querySelector(element);
  934. }
  935. if (element == null) {
  936. return;
  937. }
  938. return Array.from(element.parentElement.children).filter(
  939. (child) => child !== element
  940. );
  941. };
  942.  
  943. /**
  944. * 获取当前元素的父元素
  945. * @param {Element|NodeList|string} element 当前元素
  946. * @returns {Element|Array} 父元素
  947. */
  948. DOMUtils.parent = function (element) {
  949. if (typeof element === "string") {
  950. element = document.querySelector(element);
  951. }
  952. if (element == null) {
  953. return;
  954. }
  955. if (element instanceof NodeList || element instanceof Array) {
  956. let resultArray = [];
  957. element.forEach((item) => {
  958. resultArray = resultArray.concat(this.parent(item));
  959. });
  960. return resultArray;
  961. } else {
  962. return element.parentElement;
  963. }
  964. };
  965.  
  966. /**
  967. * 将字符串转为Element元素
  968. * @param {string} html
  969. * @param {boolean} useParser 是否使用DOMParser来生成元素,有些时候通过DOMParser生成的元素有点问题
  970. * @param {boolean} isComplete 是否是完整的
  971. * @returns {Element}
  972. */
  973. DOMUtils.parseHTML = function (html, useParser = false, isComplete = false) {
  974. function parseHTMLByDOMParser() {
  975. let parser = new DOMParser();
  976. if (isComplete) {
  977. return parser.parseFromString(html, "text/html");
  978. } else {
  979. return parser.parseFromString(html, "text/html").body.firstChild;
  980. }
  981. }
  982. function parseHTMLByCreateDom() {
  983. let tempDIV = document.createElement("div");
  984. tempDIV.innerHTML = html;
  985. if (isComplete) {
  986. return tempDIV;
  987. } else {
  988. return tempDIV.firstChild;
  989. }
  990. }
  991. if (useParser) {
  992. return parseHTMLByDOMParser();
  993. } else {
  994. return parseHTMLByCreateDom();
  995. }
  996. };
  997.  
  998. /**
  999. * 当鼠标移入或移出元素时触发事件
  1000. * @param {Element|string} element 当前元素
  1001. * @param {Function} handler 事件处理函数
  1002. */
  1003. DOMUtils.hover = function (element, handler) {
  1004. if (typeof element === "string") {
  1005. element = document.querySelector(element);
  1006. }
  1007. if (element == null) {
  1008. return;
  1009. }
  1010. DOMUtils.on(element, "mouseenter", null, handler);
  1011. DOMUtils.on(element, "mouseleave", null, handler);
  1012. };
  1013.  
  1014. /**
  1015. * 显示元素
  1016. * @param {Element|string} element 当前元素
  1017. */
  1018. DOMUtils.show = function (element) {
  1019. if (typeof element === "string") {
  1020. element = document.querySelector(element);
  1021. }
  1022. if (element == null) {
  1023. return;
  1024. }
  1025. element.style.display = "";
  1026. };
  1027.  
  1028. /**
  1029. * 隐藏元素
  1030. * @param {Element|string} element 当前元素
  1031. */
  1032. DOMUtils.hide = function (element) {
  1033. if (typeof element === "string") {
  1034. element = document.querySelector(element);
  1035. }
  1036. if (element == null) {
  1037. return;
  1038. }
  1039. element.style.display = "none";
  1040. };
  1041.  
  1042. /**
  1043. * 当按键松开时触发事件
  1044. * @param {Element|string} element 当前元素
  1045. * @param {Function} handler 事件处理函数
  1046. */
  1047. DOMUtils.keyup = function (element, handler) {
  1048. if (typeof element === "string") {
  1049. element = document.querySelector(element);
  1050. }
  1051. if (element == null) {
  1052. return;
  1053. }
  1054. DOMUtils.on(element, "keyup", null, handler);
  1055. };
  1056.  
  1057. /**
  1058. * 当按键按下时触发事件
  1059. * @param {Element|string} element 当前元素
  1060. * @param {Function} handler 事件处理函数
  1061. */
  1062. DOMUtils.keydown = function (element, handler) {
  1063. if (typeof element === "string") {
  1064. element = document.querySelector(element);
  1065. }
  1066. if (element == null) {
  1067. return;
  1068. }
  1069. DOMUtils.on(element, "keydown", null, handler);
  1070. };
  1071.  
  1072. /**
  1073. * 淡入元素
  1074. * @param {Element|string} element 当前元素
  1075. * @param {Number} duration 动画持续时间(毫秒)
  1076. * @param {Function} callback 动画结束的回调
  1077. */
  1078. DOMUtils.fadeIn = function (element, duration = 400, callback) {
  1079. if (typeof element === "string") {
  1080. element = document.querySelector(element);
  1081. }
  1082. if (element == null) {
  1083. return;
  1084. }
  1085. element.style.opacity = 0;
  1086. element.style.display = "";
  1087. let start = null;
  1088. let timer = null;
  1089. function step(timestamp) {
  1090. if (!start) start = timestamp;
  1091. let progress = timestamp - start;
  1092. element.style.opacity = Math.min(progress / duration, 1);
  1093. if (progress < duration) {
  1094. window.requestAnimationFrame(step);
  1095. } else {
  1096. if (callback && typeof callback === "function") {
  1097. callback();
  1098. }
  1099. window.cancelAnimationFrame(timer);
  1100. }
  1101. }
  1102. timer = window.requestAnimationFrame(step);
  1103. };
  1104.  
  1105. /**
  1106. * 淡出元素
  1107. * @param {Element|string} element 当前元素
  1108. * @param {Number} duration 动画持续时间(毫秒)
  1109. * @param {Function} callback 动画结束的回调
  1110. */
  1111. DOMUtils.fadeOut = function (element, duration = 400, callback) {
  1112. if (typeof element === "string") {
  1113. element = document.querySelector(element);
  1114. }
  1115. if (element == null) {
  1116. return;
  1117. }
  1118. element.style.opacity = 1;
  1119. let start = null;
  1120. let timer = null;
  1121. function step(timestamp) {
  1122. if (!start) start = timestamp;
  1123. let progress = timestamp - start;
  1124. element.style.opacity = Math.max(1 - progress / duration, 0);
  1125. if (progress < duration) {
  1126. window.requestAnimationFrame(step);
  1127. } else {
  1128. element.style.display = "none";
  1129. if (typeof callback === "function") {
  1130. callback();
  1131. }
  1132. window.cancelAnimationFrame(timer);
  1133. }
  1134. }
  1135. timer = window.requestAnimationFrame(step);
  1136. };
  1137.  
  1138. /**
  1139. * 为指定元素的子元素绑定事件
  1140. * @param {Element|string} element 当前元素
  1141. * @param {String} selector 子元素选择器
  1142. * @param {String} type 事件类型
  1143. * @param {Function} handler 事件处理函数
  1144. */
  1145. DOMUtils.delegate = function (element, selector, type, handler) {
  1146. if (typeof element === "string") {
  1147. element = document.querySelector(element);
  1148. }
  1149. if (element == null) {
  1150. return;
  1151. }
  1152. element.addEventListener(type, (event) => {
  1153. let target = event.target.closest(selector);
  1154. if (target && element.contains(target)) {
  1155. handler.call(target, event);
  1156. }
  1157. });
  1158. };
  1159.  
  1160. /**
  1161. * 切换元素的显示和隐藏状态
  1162. * @param {Element|string} element 当前元素
  1163. */
  1164. DOMUtils.toggle = function (element) {
  1165. if (typeof element === "string") {
  1166. element = document.querySelector(element);
  1167. }
  1168. if (element == null) {
  1169. return;
  1170. }
  1171. if (getComputedStyle(element).getPropertyValue("display") === "none") {
  1172. DOMUtils.show(element);
  1173. } else {
  1174. DOMUtils.hide(element);
  1175. }
  1176. };
  1177. return DOMUtils;
  1178. });

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址