DOMUtils

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

当前为 2024-07-24 提交的版本,查看 最新版本

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

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

QingJ © 2025

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