DOMUtils

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

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/465772/1614591/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. class WindowApi {
  8. /** 默认的配置 */
  9. defaultApi = {
  10. document: document,
  11. window: window,
  12. globalThis: globalThis,
  13. self: self,
  14. top: top,
  15. };
  16. /** 使用的配置 */
  17. api;
  18. constructor(option) {
  19. if (option) {
  20. if (option.globalThis == null) {
  21. option.globalThis = option.window;
  22. }
  23. if (option.self == null) {
  24. option.self = option.window;
  25. }
  26. }
  27. if (!option) {
  28. option = Object.assign({}, this.defaultApi);
  29. }
  30. // @ts-ignore
  31. this.api = Object.assign({}, option);
  32. }
  33. get document() {
  34. return this.api.document;
  35. }
  36. get window() {
  37. return this.api.window;
  38. }
  39. get globalThis() {
  40. return this.api.globalThis;
  41. }
  42. get self() {
  43. return this.api.self;
  44. }
  45. get top() {
  46. return this.api.top;
  47. }
  48. }
  49.  
  50. const createCache = (lastNumberWeakMap) => {
  51. return (collection, nextNumber) => {
  52. lastNumberWeakMap.set(collection, nextNumber);
  53. return nextNumber;
  54. };
  55. };
  56.  
  57. /*
  58. * The value of the constant Number.MAX_SAFE_INTEGER equals (2 ** 53 - 1) but it
  59. * is fairly new.
  60. */
  61. const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER === undefined ? 9007199254740991 : Number.MAX_SAFE_INTEGER;
  62. const TWO_TO_THE_POWER_OF_TWENTY_NINE = 536870912;
  63. const TWO_TO_THE_POWER_OF_THIRTY = TWO_TO_THE_POWER_OF_TWENTY_NINE * 2;
  64. const createGenerateUniqueNumber = (cache, lastNumberWeakMap) => {
  65. return (collection) => {
  66. const lastNumber = lastNumberWeakMap.get(collection);
  67. /*
  68. * Let's try the cheapest algorithm first. It might fail to produce a new
  69. * number, but it is so cheap that it is okay to take the risk. Just
  70. * increase the last number by one or reset it to 0 if we reached the upper
  71. * bound of SMIs (which stands for small integers). When the last number is
  72. * unknown it is assumed that the collection contains zero based consecutive
  73. * numbers.
  74. */
  75. let nextNumber = lastNumber === undefined ? collection.size : lastNumber < TWO_TO_THE_POWER_OF_THIRTY ? lastNumber + 1 : 0;
  76. if (!collection.has(nextNumber)) {
  77. return cache(collection, nextNumber);
  78. }
  79. /*
  80. * If there are less than half of 2 ** 30 numbers stored in the collection,
  81. * the chance to generate a new random number in the range from 0 to 2 ** 30
  82. * is at least 50%. It's benifitial to use only SMIs because they perform
  83. * much better in any environment based on V8.
  84. */
  85. if (collection.size < TWO_TO_THE_POWER_OF_TWENTY_NINE) {
  86. while (collection.has(nextNumber)) {
  87. nextNumber = Math.floor(Math.random() * TWO_TO_THE_POWER_OF_THIRTY);
  88. }
  89. return cache(collection, nextNumber);
  90. }
  91. // Quickly check if there is a theoretical chance to generate a new number.
  92. if (collection.size > MAX_SAFE_INTEGER) {
  93. throw new Error('Congratulations, you created a collection of unique numbers which uses all available integers!');
  94. }
  95. // Otherwise use the full scale of safely usable integers.
  96. while (collection.has(nextNumber)) {
  97. nextNumber = Math.floor(Math.random() * MAX_SAFE_INTEGER);
  98. }
  99. return cache(collection, nextNumber);
  100. };
  101. };
  102.  
  103. const LAST_NUMBER_WEAK_MAP = new WeakMap();
  104. const cache = createCache(LAST_NUMBER_WEAK_MAP);
  105. const generateUniqueNumber = createGenerateUniqueNumber(cache, LAST_NUMBER_WEAK_MAP);
  106.  
  107. const isMessagePort = (sender) => {
  108. return typeof sender.start === 'function';
  109. };
  110.  
  111. const PORT_MAP = new WeakMap();
  112.  
  113. const extendBrokerImplementation = (partialBrokerImplementation) => ({
  114. ...partialBrokerImplementation,
  115. connect: ({ call }) => {
  116. return async () => {
  117. const { port1, port2 } = new MessageChannel();
  118. const portId = await call('connect', { port: port1 }, [port1]);
  119. PORT_MAP.set(port2, portId);
  120. return port2;
  121. };
  122. },
  123. disconnect: ({ call }) => {
  124. return async (port) => {
  125. const portId = PORT_MAP.get(port);
  126. if (portId === undefined) {
  127. throw new Error('The given port is not connected.');
  128. }
  129. await call('disconnect', { portId });
  130. };
  131. },
  132. isSupported: ({ call }) => {
  133. return () => call('isSupported');
  134. }
  135. });
  136.  
  137. const ONGOING_REQUESTS = new WeakMap();
  138. const createOrGetOngoingRequests = (sender) => {
  139. if (ONGOING_REQUESTS.has(sender)) {
  140. // @todo TypeScript needs to be convinced that has() works as expected.
  141. return ONGOING_REQUESTS.get(sender);
  142. }
  143. const ongoingRequests = new Map();
  144. ONGOING_REQUESTS.set(sender, ongoingRequests);
  145. return ongoingRequests;
  146. };
  147. const createBroker = (brokerImplementation) => {
  148. const fullBrokerImplementation = extendBrokerImplementation(brokerImplementation);
  149. return (sender) => {
  150. const ongoingRequests = createOrGetOngoingRequests(sender);
  151. sender.addEventListener('message', (({ data: message }) => {
  152. const { id } = message;
  153. if (id !== null && ongoingRequests.has(id)) {
  154. const { reject, resolve } = ongoingRequests.get(id);
  155. ongoingRequests.delete(id);
  156. if (message.error === undefined) {
  157. resolve(message.result);
  158. }
  159. else {
  160. reject(new Error(message.error.message));
  161. }
  162. }
  163. }));
  164. if (isMessagePort(sender)) {
  165. sender.start();
  166. }
  167. const call = (method, params = null, transferables = []) => {
  168. return new Promise((resolve, reject) => {
  169. const id = generateUniqueNumber(ongoingRequests);
  170. ongoingRequests.set(id, { reject, resolve });
  171. if (params === null) {
  172. sender.postMessage({ id, method }, transferables);
  173. }
  174. else {
  175. sender.postMessage({ id, method, params }, transferables);
  176. }
  177. });
  178. };
  179. const notify = (method, params, transferables = []) => {
  180. sender.postMessage({ id: null, method, params }, transferables);
  181. };
  182. let functions = {};
  183. for (const [key, handler] of Object.entries(fullBrokerImplementation)) {
  184. functions = { ...functions, [key]: handler({ call, notify }) };
  185. }
  186. return { ...functions };
  187. };
  188. };
  189.  
  190. // Prefilling the Maps with a function indexed by zero is necessary to be compliant with the specification.
  191. const scheduledIntervalsState = new Map([[0, null]]); // tslint:disable-line no-empty
  192. const scheduledTimeoutsState = new Map([[0, null]]); // tslint:disable-line no-empty
  193. const wrap = createBroker({
  194. clearInterval: ({ call }) => {
  195. return (timerId) => {
  196. if (typeof scheduledIntervalsState.get(timerId) === 'symbol') {
  197. scheduledIntervalsState.set(timerId, null);
  198. call('clear', { timerId, timerType: 'interval' }).then(() => {
  199. scheduledIntervalsState.delete(timerId);
  200. });
  201. }
  202. };
  203. },
  204. clearTimeout: ({ call }) => {
  205. return (timerId) => {
  206. if (typeof scheduledTimeoutsState.get(timerId) === 'symbol') {
  207. scheduledTimeoutsState.set(timerId, null);
  208. call('clear', { timerId, timerType: 'timeout' }).then(() => {
  209. scheduledTimeoutsState.delete(timerId);
  210. });
  211. }
  212. };
  213. },
  214. setInterval: ({ call }) => {
  215. return (func, delay = 0, ...args) => {
  216. const symbol = Symbol();
  217. const timerId = generateUniqueNumber(scheduledIntervalsState);
  218. scheduledIntervalsState.set(timerId, symbol);
  219. const schedule = () => call('set', {
  220. delay,
  221. now: performance.timeOrigin + performance.now(),
  222. timerId,
  223. timerType: 'interval'
  224. }).then(() => {
  225. const state = scheduledIntervalsState.get(timerId);
  226. if (state === undefined) {
  227. throw new Error('The timer is in an undefined state.');
  228. }
  229. if (state === symbol) {
  230. func(...args);
  231. // Doublecheck if the interval should still be rescheduled because it could have been cleared inside of func().
  232. if (scheduledIntervalsState.get(timerId) === symbol) {
  233. schedule();
  234. }
  235. }
  236. });
  237. schedule();
  238. return timerId;
  239. };
  240. },
  241. setTimeout: ({ call }) => {
  242. return (func, delay = 0, ...args) => {
  243. const symbol = Symbol();
  244. const timerId = generateUniqueNumber(scheduledTimeoutsState);
  245. scheduledTimeoutsState.set(timerId, symbol);
  246. call('set', {
  247. delay,
  248. now: performance.timeOrigin + performance.now(),
  249. timerId,
  250. timerType: 'timeout'
  251. }).then(() => {
  252. const state = scheduledTimeoutsState.get(timerId);
  253. if (state === undefined) {
  254. throw new Error('The timer is in an undefined state.');
  255. }
  256. if (state === symbol) {
  257. // A timeout can be savely deleted because it is only called once.
  258. scheduledTimeoutsState.delete(timerId);
  259. func(...args);
  260. }
  261. });
  262. return timerId;
  263. };
  264. }
  265. });
  266. const load = (url) => {
  267. const worker = new Worker(url);
  268. return wrap(worker);
  269. };
  270.  
  271. const createLoadOrReturnBroker = (loadBroker, worker) => {
  272. let broker = null;
  273. return () => {
  274. if (broker !== null) {
  275. return broker;
  276. }
  277. const blob = new Blob([worker], { type: 'application/javascript; charset=utf-8' });
  278. const url = URL.createObjectURL(blob);
  279. broker = loadBroker(url);
  280. // Bug #1: Edge up until v18 didn't like the URL to be revoked directly.
  281. setTimeout(() => URL.revokeObjectURL(url));
  282. return broker;
  283. };
  284. };
  285.  
  286. // This is the minified and stringified code of the worker-timers-worker package.
  287. const worker = `(()=>{var e={455:function(e,t){!function(e){"use strict";var t=function(e){return function(t){var r=e(t);return t.add(r),r}},r=function(e){return function(t,r){return e.set(t,r),r}},n=void 0===Number.MAX_SAFE_INTEGER?9007199254740991:Number.MAX_SAFE_INTEGER,o=536870912,s=2*o,a=function(e,t){return function(r){var a=t.get(r),i=void 0===a?r.size:a<s?a+1:0;if(!r.has(i))return e(r,i);if(r.size<o){for(;r.has(i);)i=Math.floor(Math.random()*s);return e(r,i)}if(r.size>n)throw new Error("Congratulations, you created a collection of unique numbers which uses all available integers!");for(;r.has(i);)i=Math.floor(Math.random()*n);return e(r,i)}},i=new WeakMap,u=r(i),c=a(u,i),d=t(c);e.addUniqueNumber=d,e.generateUniqueNumber=c}(t)}},t={};function r(n){var o=t[n];if(void 0!==o)return o.exports;var s=t[n]={exports:{}};return e[n].call(s.exports,s,s.exports,r),s.exports}(()=>{"use strict";const e=-32603,t=-32602,n=-32601,o=(e,t)=>Object.assign(new Error(e),{status:t}),s=t=>o('The handler of the method called "'.concat(t,'" returned an unexpected result.'),e),a=(t,r)=>async({data:{id:a,method:i,params:u}})=>{const c=r[i];try{if(void 0===c)throw(e=>o('The requested method called "'.concat(e,'" is not supported.'),n))(i);const r=void 0===u?c():c(u);if(void 0===r)throw(t=>o('The handler of the method called "'.concat(t,'" returned no required result.'),e))(i);const d=r instanceof Promise?await r:r;if(null===a){if(void 0!==d.result)throw s(i)}else{if(void 0===d.result)throw s(i);const{result:e,transferables:r=[]}=d;t.postMessage({id:a,result:e},r)}}catch(e){const{message:r,status:n=-32603}=e;t.postMessage({error:{code:n,message:r},id:a})}};var i=r(455);const u=new Map,c=(e,r,n)=>({...r,connect:({port:t})=>{t.start();const n=e(t,r),o=(0,i.generateUniqueNumber)(u);return u.set(o,(()=>{n(),t.close(),u.delete(o)})),{result:o}},disconnect:({portId:e})=>{const r=u.get(e);if(void 0===r)throw(e=>o('The specified parameter called "portId" with the given value "'.concat(e,'" does not identify a port connected to this worker.'),t))(e);return r(),{result:null}},isSupported:async()=>{if(await new Promise((e=>{const t=new ArrayBuffer(0),{port1:r,port2:n}=new MessageChannel;r.onmessage=({data:t})=>e(null!==t),n.postMessage(t,[t])}))){const e=n();return{result:e instanceof Promise?await e:e}}return{result:!1}}}),d=(e,t,r=()=>!0)=>{const n=c(d,t,r),o=a(e,n);return e.addEventListener("message",o),()=>e.removeEventListener("message",o)},l=e=>t=>{const r=e.get(t);if(void 0===r)return Promise.resolve(!1);const[n,o]=r;return clearTimeout(n),e.delete(t),o(!1),Promise.resolve(!0)},f=(e,t,r)=>(n,o,s)=>{const{expected:a,remainingDelay:i}=e(n,o);return new Promise((e=>{t.set(s,[setTimeout(r,i,a,t,e,s),e])}))},m=(e,t)=>{const r=performance.now(),n=e+t-r-performance.timeOrigin;return{expected:r+n,remainingDelay:n}},p=(e,t,r,n)=>{const o=e-performance.now();o>0?t.set(n,[setTimeout(p,o,e,t,r,n),r]):(t.delete(n),r(!0))},h=new Map,v=l(h),w=new Map,g=l(w),M=f(m,h,p),y=f(m,w,p);d(self,{clear:async({timerId:e,timerType:t})=>({result:await("interval"===t?v(e):g(e))}),set:async({delay:e,now:t,timerId:r,timerType:n})=>({result:await("interval"===n?M:y)(e,t,r)})})})()})();`; // tslint:disable-line:max-line-length
  288.  
  289. const loadOrReturnBroker = createLoadOrReturnBroker(load, worker);
  290. const clearInterval = (timerId) => loadOrReturnBroker().clearInterval(timerId);
  291. const clearTimeout = (timerId) => loadOrReturnBroker().clearTimeout(timerId);
  292. const setInterval = (...args) => loadOrReturnBroker().setInterval(...args);
  293. const setTimeout$1 = (...args) => loadOrReturnBroker().setTimeout(...args);
  294.  
  295. /** 通用工具类 */
  296. const DOMUtilsCommonUtils = {
  297. windowApi: new WindowApi({
  298. document: document,
  299. window: window,
  300. top: top,
  301. }),
  302. /**
  303. * 判断元素是否已显示或已连接
  304. * @param element
  305. */
  306. isShow(element) {
  307. return Boolean(element.getClientRects().length);
  308. },
  309. /**
  310. * 获取安全的html
  311. */
  312. getSafeHTML(text) {
  313. // @ts-ignore
  314. if (globalThis.trustedTypes) {
  315. // @ts-ignore
  316. const policy = globalThis.trustedTypes.createPolicy("safe-innerHTML", {
  317. createHTML: (html) => html,
  318. });
  319. return policy.createHTML(text);
  320. }
  321. else {
  322. return text;
  323. }
  324. },
  325. /**
  326. * 在CSP策略下设置innerHTML
  327. * @param $el 元素
  328. * @param text 文本
  329. */
  330. setSafeHTML($el, text) {
  331. // 创建 TrustedHTML 策略(需 CSP 允许)
  332. $el.innerHTML = this.getSafeHTML(text);
  333. },
  334. /**
  335. * 用于显示元素并获取它的高度宽度等其它属性
  336. * @param element
  337. */
  338. showElement(element) {
  339. let dupNode = element.cloneNode(true);
  340. dupNode.setAttribute("style", "visibility: hidden !important;display:block !important;");
  341. this.windowApi.document.documentElement.appendChild(dupNode);
  342. return {
  343. /**
  344. * 恢复修改的style
  345. */
  346. recovery() {
  347. dupNode.remove();
  348. },
  349. };
  350. },
  351. /**
  352. * 获取元素上的Float格式的属性px
  353. * @param element
  354. * @param styleName style名
  355. */
  356. getStyleValue(element, styleName) {
  357. let view = null;
  358. let styles = null;
  359. if (element instanceof CSSStyleDeclaration) {
  360. /* 直接就获取了style属性 */
  361. styles = element;
  362. }
  363. else {
  364. view = element.ownerDocument.defaultView;
  365. if (!view || !view.opener) {
  366. view = window;
  367. }
  368. styles = view.getComputedStyle(element);
  369. }
  370. let value = parseFloat(styles[styleName]);
  371. if (isNaN(value)) {
  372. return 0;
  373. }
  374. else {
  375. return value;
  376. }
  377. },
  378. /**
  379. * 判断是否是window,例如window、self、globalThis
  380. * @param target
  381. */
  382. isWin(target) {
  383. if (typeof target !== "object") {
  384. return false;
  385. }
  386. if (target instanceof Node) {
  387. return false;
  388. }
  389. if (target === globalThis) {
  390. return true;
  391. }
  392. if (target === window) {
  393. return true;
  394. }
  395. if (target === self) {
  396. return true;
  397. }
  398. if (target === globalThis) {
  399. return true;
  400. }
  401. if (target === window) {
  402. return true;
  403. }
  404. if (target === self) {
  405. return true;
  406. }
  407. if (typeof unsafeWindow !== "undefined" && target === unsafeWindow) {
  408. return true;
  409. }
  410. if (target?.Math?.toString() !== "[object Math]") {
  411. return false;
  412. }
  413. return true;
  414. },
  415. /**
  416. * 删除对象上的属性
  417. * @param target
  418. * @param propName
  419. */
  420. delete(target, propName) {
  421. if (typeof Reflect === "object" && Reflect.deleteProperty) {
  422. Reflect.deleteProperty(target, propName);
  423. }
  424. else {
  425. delete target[propName];
  426. }
  427. },
  428. /**
  429. * 自动使用 Worker 执行 setTimeout
  430. */
  431. setTimeout(callback, timeout = 0) {
  432. try {
  433. return setTimeout$1(callback, timeout);
  434. }
  435. catch (error) {
  436. return globalThis.setTimeout(callback, timeout);
  437. }
  438. },
  439. /**
  440. * 配合 .setTimeout 使用
  441. */
  442. clearTimeout(timeId) {
  443. try {
  444. if (timeId != null) {
  445. clearTimeout(timeId);
  446. }
  447. }
  448. catch (error) {
  449. }
  450. finally {
  451. globalThis.clearTimeout(timeId);
  452. }
  453. },
  454. /**
  455. * 自动使用 Worker 执行 setInterval
  456. */
  457. setInterval(callback, timeout = 0) {
  458. try {
  459. return setInterval(callback, timeout);
  460. }
  461. catch (error) {
  462. return globalThis.setInterval(callback, timeout);
  463. }
  464. },
  465. /**
  466. * 配合 .setInterval 使用
  467. */
  468. clearInterval(timeId) {
  469. try {
  470. if (timeId != null) {
  471. clearInterval(timeId);
  472. }
  473. }
  474. catch (error) {
  475. }
  476. finally {
  477. globalThis.clearInterval(timeId);
  478. }
  479. },
  480. /**
  481. * 判断是否是元素列表
  482. * @param $ele
  483. */
  484. isNodeList($ele) {
  485. return Array.isArray($ele) || $ele instanceof NodeList;
  486. },
  487. };
  488.  
  489. /* 数据 */
  490. const DOMUtilsData = {
  491. /** .on添加在元素存储的事件 */
  492. SymbolEvents: Symbol("events_" + (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1)),
  493. };
  494.  
  495. const OriginPrototype = {
  496. Object: {
  497. defineProperty: Object.defineProperty,
  498. },
  499. };
  500.  
  501. class DOMUtilsEvent {
  502. windowApi;
  503. constructor(windowApiOption) {
  504. this.windowApi = new WindowApi(windowApiOption);
  505. }
  506. on(element, eventType, selector, callback, option) {
  507. /**
  508. * 获取option配置
  509. * @param args
  510. * @param startIndex
  511. * @param option
  512. */
  513. function getOption(args, startIndex, option) {
  514. let currentParam = args[startIndex];
  515. if (typeof currentParam === "boolean") {
  516. option.capture = currentParam;
  517. if (typeof args[startIndex + 1] === "boolean") {
  518. option.once = args[startIndex + 1];
  519. }
  520. if (typeof args[startIndex + 2] === "boolean") {
  521. option.passive = args[startIndex + 2];
  522. }
  523. }
  524. else if (typeof currentParam === "object" &&
  525. ("capture" in currentParam ||
  526. "once" in currentParam ||
  527. "passive" in currentParam ||
  528. "isComposedPath" in currentParam)) {
  529. option.capture = currentParam.capture;
  530. option.once = currentParam.once;
  531. option.passive = currentParam.passive;
  532. option.isComposedPath = currentParam.isComposedPath;
  533. }
  534. return option;
  535. }
  536. let DOMUtilsContext = this;
  537. let args = arguments;
  538. if (typeof element === "string") {
  539. element = DOMUtilsContext.selectorAll(element);
  540. }
  541. if (element == null) {
  542. return;
  543. }
  544. let elementList = [];
  545. if (element instanceof NodeList || Array.isArray(element)) {
  546. element = element;
  547. elementList = [...element];
  548. }
  549. else {
  550. elementList.push(element);
  551. }
  552. // 事件名
  553. let eventTypeList = [];
  554. if (Array.isArray(eventType)) {
  555. eventTypeList = eventTypeList.concat(eventType.filter((eventTypeItem) => typeof eventTypeItem === "string" && eventTypeItem.toString() !== ""));
  556. }
  557. else if (typeof eventType === "string") {
  558. eventTypeList = eventTypeList.concat(eventType.split(" ").filter((eventTypeItem) => eventTypeItem !== ""));
  559. }
  560. // 子元素选择器
  561. let selectorList = [];
  562. if (Array.isArray(selector)) {
  563. selectorList = selectorList.concat(selector.filter((selectorItem) => typeof selectorItem === "string" && selectorItem.toString() !== ""));
  564. }
  565. else if (typeof selector === "string") {
  566. selectorList.push(selector);
  567. }
  568. // 事件回调
  569. let listenerCallBack = callback;
  570. // 事件配置
  571. let listenerOption = {
  572. capture: false,
  573. once: false,
  574. passive: false,
  575. isComposedPath: false,
  576. };
  577. if (typeof selector === "function") {
  578. // 这是为没有selector的情况
  579. // 那么它就是callback
  580. listenerCallBack = selector;
  581. listenerOption = getOption(args, 3, listenerOption);
  582. }
  583. else {
  584. // 这是存在selector的情况
  585. listenerOption = getOption(args, 4, listenerOption);
  586. }
  587. /**
  588. * 如果是once,那么删除该监听和元素上的事件和监听
  589. */
  590. function checkOptionOnceToRemoveEventListener() {
  591. if (listenerOption.once) {
  592. DOMUtilsContext.off(element, eventType, selector, callback, option);
  593. }
  594. }
  595. elementList.forEach((elementItem) => {
  596. /**
  597. * 事件回调
  598. * @param event
  599. */
  600. function domUtilsEventCallBack(event) {
  601. if (selectorList.length) {
  602. /* 存在子元素选择器 */
  603. // 这时候的this和target都是子元素选择器的元素
  604. let eventTarget = listenerOption.isComposedPath
  605. ? event.composedPath()[0]
  606. : event.target;
  607. let totalParent = elementItem;
  608. if (DOMUtilsCommonUtils.isWin(totalParent)) {
  609. if (totalParent ===
  610. DOMUtilsContext.windowApi.document) {
  611. totalParent = DOMUtilsContext.windowApi.document.documentElement;
  612. }
  613. }
  614. let findValue = selectorList.find((selectorItem) => {
  615. // 判断目标元素是否匹配选择器
  616. if (DOMUtilsContext.matches(eventTarget, selectorItem)) {
  617. /* 当前目标可以被selector所匹配到 */
  618. return true;
  619. }
  620. /* 在上层与主元素之间寻找可以被selector所匹配到的 */
  621. let $closestMatches = DOMUtilsContext.closest(eventTarget, selectorItem);
  622. if ($closestMatches && totalParent?.contains($closestMatches)) {
  623. eventTarget = $closestMatches;
  624. return true;
  625. }
  626. return false;
  627. });
  628. if (findValue) {
  629. // 这里尝试使用defineProperty修改event的target值
  630. try {
  631. OriginPrototype.Object.defineProperty(event, "target", {
  632. get() {
  633. return eventTarget;
  634. },
  635. });
  636. }
  637. catch (error) { }
  638. listenerCallBack.call(eventTarget, event, eventTarget);
  639. checkOptionOnceToRemoveEventListener();
  640. }
  641. }
  642. else {
  643. // 这时候的this指向监听的元素
  644. listenerCallBack.call(elementItem, event);
  645. checkOptionOnceToRemoveEventListener();
  646. }
  647. }
  648. /* 遍历事件名设置元素事件 */
  649. eventTypeList.forEach((eventName) => {
  650. elementItem.addEventListener(eventName, domUtilsEventCallBack, listenerOption);
  651. /* 获取对象上的事件 */
  652. let elementEvents = Reflect.get(elementItem, DOMUtilsData.SymbolEvents) || {};
  653. /* 初始化对象上的xx事件 */
  654. elementEvents[eventName] = elementEvents[eventName] || [];
  655. elementEvents[eventName].push({
  656. selector: selectorList,
  657. option: listenerOption,
  658. callback: domUtilsEventCallBack,
  659. originCallBack: listenerCallBack,
  660. });
  661. /* 覆盖事件 */
  662. Reflect.set(elementItem, DOMUtilsData.SymbolEvents, elementEvents);
  663. });
  664. });
  665. }
  666. off(element, eventType, selector, callback, option, filter) {
  667. /**
  668. * 获取option配置
  669. * @param args1
  670. * @param startIndex
  671. * @param option
  672. */
  673. function getOption(args1, startIndex, option) {
  674. let currentParam = args1[startIndex];
  675. if (typeof currentParam === "boolean") {
  676. option.capture = currentParam;
  677. }
  678. else if (typeof currentParam === "object" &&
  679. "capture" in currentParam) {
  680. option.capture = currentParam.capture;
  681. }
  682. return option;
  683. }
  684. let DOMUtilsContext = this;
  685. let args = arguments;
  686. if (typeof element === "string") {
  687. element = DOMUtilsContext.selectorAll(element);
  688. }
  689. if (element == null) {
  690. return;
  691. }
  692. let elementList = [];
  693. if (element instanceof NodeList || Array.isArray(element)) {
  694. element = element;
  695. elementList = [...element];
  696. }
  697. else {
  698. elementList.push(element);
  699. }
  700. let eventTypeList = [];
  701. if (Array.isArray(eventType)) {
  702. eventTypeList = eventTypeList.concat(eventType.filter((eventTypeItem) => typeof eventTypeItem === "string" && eventTypeItem.toString() !== ""));
  703. }
  704. else if (typeof eventType === "string") {
  705. eventTypeList = eventTypeList.concat(eventType.split(" ").filter((eventTypeItem) => eventTypeItem !== ""));
  706. }
  707. // 子元素选择器
  708. let selectorList = [];
  709. if (Array.isArray(selector)) {
  710. selectorList = selectorList.concat(selector.filter((selectorItem) => typeof selectorItem === "string" && selectorItem.toString() !== ""));
  711. }
  712. else if (typeof selector === "string") {
  713. selectorList.push(selector);
  714. }
  715. /**
  716. * 事件的回调函数
  717. */
  718. let listenerCallBack = callback;
  719. /**
  720. * 事件的配置
  721. */
  722. let listenerOption = {
  723. capture: false,
  724. };
  725. if (typeof selector === "function") {
  726. // 这是为没有selector的情况
  727. // 那么它就是callback
  728. listenerCallBack = selector;
  729. listenerOption = getOption(args, 3, listenerOption);
  730. }
  731. else {
  732. // 这是存在selector的情况
  733. listenerOption = getOption(args, 4, listenerOption);
  734. }
  735. // 是否移除所有事件
  736. let isRemoveAll = false;
  737. if (args.length === 2) {
  738. // 目标函数、事件名
  739. isRemoveAll = true;
  740. }
  741. else if ((args.length === 3 && typeof args[2] === "string") ||
  742. Array.isArray(args[2])) {
  743. // 目标函数、事件名、子元素选择器
  744. isRemoveAll = true;
  745. }
  746. elementList.forEach((elementItem) => {
  747. /* 获取对象上的事件 */
  748. let elementEvents = Reflect.get(elementItem, DOMUtilsData.SymbolEvents) || {};
  749. eventTypeList.forEach((eventName) => {
  750. let handlers = elementEvents[eventName] || [];
  751. if (typeof filter === "function") {
  752. handlers = handlers.filter(filter);
  753. }
  754. for (let index = 0; index < handlers.length; index++) {
  755. let handler = handlers[index];
  756. let flag = true;
  757. if (flag &&
  758. listenerCallBack &&
  759. handler.originCallBack !== listenerCallBack) {
  760. // callback不同
  761. flag = false;
  762. }
  763. if (flag && selectorList.length && Array.isArray(handler.selector)) {
  764. if (JSON.stringify(handler.selector) !== JSON.stringify(selectorList)) {
  765. // 子元素选择器不同
  766. flag = false;
  767. }
  768. }
  769. if (flag && listenerOption.capture !== handler.option.capture) {
  770. // 事件的配置项不同
  771. flag = false;
  772. }
  773. if (flag || isRemoveAll) {
  774. elementItem.removeEventListener(eventName, handler.callback, handler.option);
  775. handlers.splice(index--, 1);
  776. }
  777. }
  778. if (handlers.length === 0) {
  779. /* 如果没有任意的handler,那么删除该属性 */
  780. DOMUtilsCommonUtils.delete(elementEvents, eventType);
  781. }
  782. });
  783. Reflect.set(elementItem, DOMUtilsData.SymbolEvents, elementEvents);
  784. });
  785. }
  786. /**
  787. * 取消绑定所有的事件
  788. * @param element 需要取消绑定的元素|元素数组
  789. * @param eventType (可选)需要取消监听的事件
  790. */
  791. offAll(element, eventType) {
  792. let DOMUtilsContext = this;
  793. if (typeof element === "string") {
  794. element = DOMUtilsContext.selectorAll(element);
  795. }
  796. if (element == null) {
  797. return;
  798. }
  799. let elementList = [];
  800. if (element instanceof NodeList || Array.isArray(element)) {
  801. elementList = [...element];
  802. }
  803. else {
  804. elementList.push(element);
  805. }
  806. let eventTypeList = [];
  807. if (Array.isArray(eventType)) {
  808. eventTypeList = eventTypeList.concat(eventType);
  809. }
  810. else if (typeof eventType === "string") {
  811. eventTypeList = eventTypeList.concat(eventType.split(" "));
  812. }
  813. elementList.forEach((elementItem) => {
  814. Object.getOwnPropertySymbols(elementItem).forEach((symbolEvents) => {
  815. if (!symbolEvents.toString().startsWith("Symbol(events_")) {
  816. return;
  817. }
  818. let elementEvents = elementItem[symbolEvents] || {};
  819. let iterEventNameList = eventTypeList.length
  820. ? eventTypeList
  821. : Object.keys(elementEvents);
  822. iterEventNameList.forEach((eventName) => {
  823. let handlers = elementEvents[eventName];
  824. if (!handlers) {
  825. return;
  826. }
  827. for (const handler of handlers) {
  828. elementItem.removeEventListener(eventName, handler.callback, {
  829. capture: handler["option"]["capture"],
  830. });
  831. }
  832. let events = Reflect.get(elementItem, symbolEvents);
  833. DOMUtilsCommonUtils.delete(events, eventName);
  834. });
  835. });
  836. });
  837. }
  838. /**
  839. * 等待文档加载完成后执行指定的函数
  840. * @param callback 需要执行的函数
  841. * @example
  842. * DOMUtils.ready(function(){
  843. * console.log("文档加载完毕")
  844. * })
  845. */
  846. ready(callback) {
  847. if (typeof callback !== "function") {
  848. return;
  849. }
  850. let DOMUtilsContext = this;
  851. /**
  852. * 检测文档是否加载完毕
  853. */
  854. function checkDOMReadyState() {
  855. try {
  856. if (DOMUtilsContext.windowApi.document.readyState === "complete" ||
  857. (DOMUtilsContext.windowApi.document.readyState !== "loading" &&
  858. !DOMUtilsContext.windowApi.document.documentElement
  859. .doScroll)) {
  860. return true;
  861. }
  862. else {
  863. return false;
  864. }
  865. }
  866. catch (error) {
  867. return false;
  868. }
  869. }
  870. /**
  871. * 成功加载完毕后触发的回调函数
  872. */
  873. function completed() {
  874. removeDomReadyListener();
  875. callback();
  876. }
  877. let targetList = [
  878. {
  879. target: DOMUtilsContext.windowApi.document,
  880. eventType: "DOMContentLoaded",
  881. callback: completed,
  882. },
  883. {
  884. target: DOMUtilsContext.windowApi.window,
  885. eventType: "load",
  886. callback: completed,
  887. },
  888. ];
  889. /**
  890. * 添加监听
  891. */
  892. function addDomReadyListener() {
  893. for (let index = 0; index < targetList.length; index++) {
  894. let item = targetList[index];
  895. item.target.addEventListener(item.eventType, item.callback);
  896. }
  897. }
  898. /**
  899. * 移除监听
  900. */
  901. function removeDomReadyListener() {
  902. for (let index = 0; index < targetList.length; index++) {
  903. let item = targetList[index];
  904. item.target.removeEventListener(item.eventType, item.callback);
  905. }
  906. }
  907. if (checkDOMReadyState()) {
  908. /* 检查document状态 */
  909. DOMUtilsCommonUtils.setTimeout(callback);
  910. }
  911. else {
  912. /* 添加监听 */
  913. addDomReadyListener();
  914. }
  915. }
  916. /**
  917. * 主动触发事件
  918. * @param element 需要触发的元素|元素数组|window
  919. * @param eventType 需要触发的事件
  920. * @param details 赋予触发的Event的额外属性,如果是Event类型,那么将自动代替默认new的Event对象
  921. * @param useDispatchToTriggerEvent 是否使用dispatchEvent来触发事件,默认true
  922. * @example
  923. * // 触发元素a.xx的click事件
  924. * DOMUtils.trigger(document.querySelector("a.xx"),"click")
  925. * DOMUtils.trigger("a.xx","click")
  926. * // 触发元素a.xx的click、tap、hover事件
  927. * DOMUtils.trigger(document.querySelector("a.xx"),"click tap hover")
  928. * DOMUtils.trigger("a.xx",["click","tap","hover"])
  929. */
  930. trigger(element, eventType, details, useDispatchToTriggerEvent = true) {
  931. let DOMUtilsContext = this;
  932. if (typeof element === "string") {
  933. element = DOMUtilsContext.selectorAll(element);
  934. }
  935. if (element == null) {
  936. return;
  937. }
  938. let elementList = [];
  939. if (element instanceof NodeList || Array.isArray(element)) {
  940. element = element;
  941. elementList = [...element];
  942. }
  943. else {
  944. elementList = [element];
  945. }
  946. let eventTypeList = [];
  947. if (Array.isArray(eventType)) {
  948. eventTypeList = eventType;
  949. }
  950. else if (typeof eventType === "string") {
  951. eventTypeList = eventType.split(" ");
  952. }
  953. elementList.forEach((elementItem) => {
  954. /* 获取对象上的事件 */
  955. let events = elementItem[DOMUtilsData.SymbolEvents] || {};
  956. eventTypeList.forEach((_eventType_) => {
  957. let event = null;
  958. if (details && details instanceof Event) {
  959. event = details;
  960. }
  961. else {
  962. event = new Event(_eventType_);
  963. if (details) {
  964. Object.keys(details).forEach((keyName) => {
  965. event[keyName] = details[keyName];
  966. });
  967. }
  968. }
  969. if (useDispatchToTriggerEvent == false && _eventType_ in events) {
  970. events[_eventType_].forEach((eventsItem) => {
  971. eventsItem.callback(event);
  972. });
  973. }
  974. else {
  975. elementItem.dispatchEvent(event);
  976. }
  977. });
  978. });
  979. }
  980. /**
  981. * 绑定或触发元素的click事件
  982. * @param element 目标元素
  983. * @param handler (可选)事件处理函数
  984. * @param details (可选)赋予触发的Event的额外属性
  985. * @param useDispatchToTriggerEvent (可选)是否使用dispatchEvent来触发事件,默认true
  986. * @example
  987. * // 触发元素a.xx的click事件
  988. * DOMUtils.click(document.querySelector("a.xx"))
  989. * DOMUtils.click("a.xx")
  990. * DOMUtils.click("a.xx",function(){
  991. * console.log("触发click事件成功")
  992. * })
  993. * */
  994. click(element, handler, details, useDispatchToTriggerEvent) {
  995. let DOMUtilsContext = this;
  996. if (typeof element === "string") {
  997. element = DOMUtilsContext.selectorAll(element);
  998. }
  999. if (element == null) {
  1000. return;
  1001. }
  1002. if (DOMUtilsCommonUtils.isNodeList(element)) {
  1003. // 设置
  1004. element.forEach(($ele) => {
  1005. DOMUtilsContext.click($ele, handler, details, useDispatchToTriggerEvent);
  1006. });
  1007. return;
  1008. }
  1009. if (handler == null) {
  1010. DOMUtilsContext.trigger(element, "click", details, useDispatchToTriggerEvent);
  1011. }
  1012. else {
  1013. DOMUtilsContext.on(element, "click", null, handler);
  1014. }
  1015. }
  1016. /**
  1017. * 绑定或触发元素的blur事件
  1018. * @param element 目标元素
  1019. * @param handler (可选)事件处理函数
  1020. * @param details (可选)赋予触发的Event的额外属性
  1021. * @param useDispatchToTriggerEvent (可选)是否使用dispatchEvent来触发事件,默认true
  1022. * @example
  1023. * // 触发元素a.xx的blur事件
  1024. * DOMUtils.blur(document.querySelector("a.xx"))
  1025. * DOMUtils.blur("a.xx")
  1026. * DOMUtils.blur("a.xx",function(){
  1027. * console.log("触发blur事件成功")
  1028. * })
  1029. * */
  1030. blur(element, handler, details, useDispatchToTriggerEvent) {
  1031. let DOMUtilsContext = this;
  1032. if (typeof element === "string") {
  1033. element = DOMUtilsContext.selectorAll(element);
  1034. }
  1035. if (element == null) {
  1036. return;
  1037. }
  1038. if (DOMUtilsCommonUtils.isNodeList(element)) {
  1039. // 设置
  1040. element.forEach(($ele) => {
  1041. DOMUtilsContext.focus($ele, handler, details, useDispatchToTriggerEvent);
  1042. });
  1043. return;
  1044. }
  1045. if (handler === null) {
  1046. DOMUtilsContext.trigger(element, "blur", details, useDispatchToTriggerEvent);
  1047. }
  1048. else {
  1049. DOMUtilsContext.on(element, "blur", null, handler);
  1050. }
  1051. }
  1052. /**
  1053. * 绑定或触发元素的focus事件
  1054. * @param element 目标元素
  1055. * @param handler (可选)事件处理函数
  1056. * @param details (可选)赋予触发的Event的额外属性
  1057. * @param useDispatchToTriggerEvent (可选)是否使用dispatchEvent来触发事件,默认true
  1058. * @example
  1059. * // 触发元素a.xx的focus事件
  1060. * DOMUtils.focus(document.querySelector("a.xx"))
  1061. * DOMUtils.focus("a.xx")
  1062. * DOMUtils.focus("a.xx",function(){
  1063. * console.log("触发focus事件成功")
  1064. * })
  1065. * */
  1066. focus(element, handler, details, useDispatchToTriggerEvent) {
  1067. let DOMUtilsContext = this;
  1068. if (typeof element === "string") {
  1069. element = DOMUtilsContext.selectorAll(element);
  1070. }
  1071. if (element == null) {
  1072. return;
  1073. }
  1074. if (DOMUtilsCommonUtils.isNodeList(element)) {
  1075. // 设置
  1076. element.forEach(($ele) => {
  1077. DOMUtilsContext.focus($ele, handler, details, useDispatchToTriggerEvent);
  1078. });
  1079. return;
  1080. }
  1081. if (handler == null) {
  1082. DOMUtilsContext.trigger(element, "focus", details, useDispatchToTriggerEvent);
  1083. }
  1084. else {
  1085. DOMUtilsContext.on(element, "focus", null, handler);
  1086. }
  1087. }
  1088. /**
  1089. * 当鼠标移入或移出元素时触发事件
  1090. * @param element 当前元素
  1091. * @param handler 事件处理函数
  1092. * @param option 配置
  1093. * @example
  1094. * // 监听a.xx元素的移入或移出
  1095. * DOMUtils.hover(document.querySelector("a.xx"),()=>{
  1096. * console.log("移入/移除");
  1097. * })
  1098. * DOMUtils.hover("a.xx",()=>{
  1099. * console.log("移入/移除");
  1100. * })
  1101. */
  1102. hover(element, handler, option) {
  1103. let DOMUtilsContext = this;
  1104. if (typeof element === "string") {
  1105. element = DOMUtilsContext.selectorAll(element);
  1106. }
  1107. if (element == null) {
  1108. return;
  1109. }
  1110. if (DOMUtilsCommonUtils.isNodeList(element)) {
  1111. // 设置
  1112. element.forEach(($ele) => {
  1113. DOMUtilsContext.hover($ele, handler, option);
  1114. });
  1115. return;
  1116. }
  1117. DOMUtilsContext.on(element, "mouseenter", null, handler, option);
  1118. DOMUtilsContext.on(element, "mouseleave", null, handler, option);
  1119. }
  1120. /**
  1121. * 当按键松开时触发事件
  1122. * keydown - > keypress - > keyup
  1123. * @param element 当前元素
  1124. * @param handler 事件处理函数
  1125. * @param option 配置
  1126. * @example
  1127. * // 监听a.xx元素的按键松开
  1128. * DOMUtils.keyup(document.querySelector("a.xx"),()=>{
  1129. * console.log("按键松开");
  1130. * })
  1131. * DOMUtils.keyup("a.xx",()=>{
  1132. * console.log("按键松开");
  1133. * })
  1134. */
  1135. keyup(element, handler, option) {
  1136. let DOMUtilsContext = this;
  1137. if (element == null) {
  1138. return;
  1139. }
  1140. if (typeof element === "string") {
  1141. element = DOMUtilsContext.selectorAll(element);
  1142. }
  1143. if (DOMUtilsCommonUtils.isNodeList(element)) {
  1144. // 设置
  1145. element.forEach(($ele) => {
  1146. DOMUtilsContext.keyup($ele, handler, option);
  1147. });
  1148. return;
  1149. }
  1150. DOMUtilsContext.on(element, "keyup", null, handler, option);
  1151. }
  1152. /**
  1153. * 当按键按下时触发事件
  1154. * keydown - > keypress - > keyup
  1155. * @param element 目标
  1156. * @param handler 事件处理函数
  1157. * @param option 配置
  1158. * @example
  1159. * // 监听a.xx元素的按键按下
  1160. * DOMUtils.keydown(document.querySelector("a.xx"),()=>{
  1161. * console.log("按键按下");
  1162. * })
  1163. * DOMUtils.keydown("a.xx",()=>{
  1164. * console.log("按键按下");
  1165. * })
  1166. */
  1167. keydown(element, handler, option) {
  1168. let DOMUtilsContext = this;
  1169. if (element == null) {
  1170. return;
  1171. }
  1172. if (typeof element === "string") {
  1173. element = DOMUtilsContext.selectorAll(element);
  1174. }
  1175. if (DOMUtilsCommonUtils.isNodeList(element)) {
  1176. // 设置
  1177. element.forEach(($ele) => {
  1178. DOMUtilsContext.keydown($ele, handler, option);
  1179. });
  1180. return;
  1181. }
  1182. DOMUtilsContext.on(element, "keydown", null, handler, option);
  1183. }
  1184. /**
  1185. * 当按键按下时触发事件
  1186. * keydown - > keypress - > keyup
  1187. * @param element 目标
  1188. * @param handler 事件处理函数
  1189. * @param option 配置
  1190. * @example
  1191. * // 监听a.xx元素的按键按下
  1192. * DOMUtils.keypress(document.querySelector("a.xx"),()=>{
  1193. * console.log("按键按下");
  1194. * })
  1195. * DOMUtils.keypress("a.xx",()=>{
  1196. * console.log("按键按下");
  1197. * })
  1198. */
  1199. keypress(element, handler, option) {
  1200. let DOMUtilsContext = this;
  1201. if (element == null) {
  1202. return;
  1203. }
  1204. if (typeof element === "string") {
  1205. element = DOMUtilsContext.selectorAll(element);
  1206. }
  1207. if (DOMUtilsCommonUtils.isNodeList(element)) {
  1208. // 设置
  1209. element.forEach(($ele) => {
  1210. DOMUtilsContext.keypress($ele, handler, option);
  1211. });
  1212. return;
  1213. }
  1214. DOMUtilsContext.on(element, "keypress", null, handler, option);
  1215. }
  1216. /**
  1217. * 监听某个元素键盘按键事件或window全局按键事件
  1218. * 按下有值的键时触发,按下Ctrl\Alt\Shift\Meta是无值键。按下先触发keydown事件,再触发keypress事件。
  1219. * @param element 需要监听的对象,可以是全局Window或者某个元素
  1220. * @param eventName 事件名,默认keypress
  1221. * @param callback 自己定义的回调事件,参数1为当前的key,参数2为组合按键,数组类型,包含ctrl、shift、alt和meta(win键或mac的cmd键)
  1222. * @param options 监听事件的配置
  1223. * @example
  1224. Utils.listenKeyboard(window,(keyName,keyValue,otherKey,event)=>{
  1225. if(keyName === "Enter"){
  1226. console.log("回车按键的值是:"+keyValue)
  1227. }
  1228. if(otherKey.indexOf("ctrl") && keyName === "Enter" ){
  1229. console.log("Ctrl和回车键");
  1230. }
  1231. })
  1232. * @example
  1233. 字母和数字键的键码值(keyCode)
  1234. 按键 键码 按键 键码 按键 键码 按键 键码
  1235. A 65 J 74 S 83 1 49
  1236. B 66 K 75 T 84 2 50
  1237. C 67 L 76 U 85 3 51
  1238. D 68 M 77 V 86 4 52
  1239. E 69 N 78 W 87 5 53
  1240. F 70 O 79 X 88 6 54
  1241. G 71 P 80 Y 89 7 55
  1242. H 72 Q 81 Z 90 8 56
  1243. I 73 R 82 0 48 9 57
  1244. 数字键盘上的键的键码值(keyCode)
  1245. 功能键键码值(keyCode)
  1246. 按键 键码 按键 键码 按键 键码 按键 键码
  1247. 0 96 8 104 F1 112 F7 118
  1248. 1 97 9 105 F2 113 F8 119
  1249. 2 98 * 106 F3 114 F9 120
  1250. 3 99 + 107 F4 115 F10 121
  1251. 4 100 Enter 108 F5 116 F11 122
  1252. 5 101 - 109 F6 117 F12 123
  1253. 6 102 . 110
  1254. 7 103 / 111
  1255. 控制键键码值(keyCode)
  1256. 按键 键码 按键 键码 按键 键码 按键 键码
  1257. BackSpace 8 Esc 27 → 39 -_ 189
  1258. Tab 9 Spacebar 32 ↓ 40 .> 190
  1259. Clear 12 Page Up 33 Insert 45 /? 191
  1260. Enter 13 Page Down 34 Delete 46 `~ 192
  1261. Shift 16 End 35 Num Lock 144 [{ 219
  1262. Control 17 Home 36 ;: 186 \| 220
  1263. Alt 18 ← 37 =+ 187 ]} 221
  1264. Cape Lock 20 ↑ 38 ,< 188 '" 222
  1265. 多媒体键码值(keyCode)
  1266. 按键 键码
  1267. 音量加 175
  1268. 音量减 174
  1269. 停止 179
  1270. 静音 173
  1271. 浏览器 172
  1272. 邮件 180
  1273. 搜索 170
  1274. 收藏 171
  1275. **/
  1276. listenKeyboard(element, eventName = "keypress", callback, options) {
  1277. let DOMUtilsContext = this;
  1278. if (typeof element === "string") {
  1279. element = DOMUtilsContext.selectorAll(element);
  1280. }
  1281. let keyboardEventCallBack = function (event) {
  1282. /** 键名 */
  1283. let keyName = event.key || event.code;
  1284. /** 键值 */
  1285. let keyValue = event.charCode || event.keyCode || event.which;
  1286. /** 组合键列表 */
  1287. let otherCodeList = [];
  1288. if (event.ctrlKey) {
  1289. otherCodeList.push("ctrl");
  1290. }
  1291. if (event.altKey) {
  1292. otherCodeList.push("alt");
  1293. }
  1294. if (event.metaKey) {
  1295. otherCodeList.push("meta");
  1296. }
  1297. if (event.shiftKey) {
  1298. otherCodeList.push("shift");
  1299. }
  1300. if (typeof callback === "function") {
  1301. callback(keyName, keyValue, otherCodeList, event);
  1302. }
  1303. };
  1304. DOMUtilsContext.on(element, eventName, keyboardEventCallBack, options);
  1305. return {
  1306. removeListen: () => {
  1307. DOMUtilsContext.off(element, eventName, keyboardEventCallBack, options);
  1308. },
  1309. };
  1310. }
  1311. selector(selector, parent) {
  1312. return this.selectorAll(selector, parent)[0];
  1313. }
  1314. selectorAll(selector, parent) {
  1315. const context = this;
  1316. parent = parent || context.windowApi.document;
  1317. selector = selector.trim();
  1318. if (selector.match(/[^\s]{1}:empty$/gi)) {
  1319. // empty 语法
  1320. selector = selector.replace(/:empty$/gi, "");
  1321. return Array.from(parent.querySelectorAll(selector)).filter(($ele) => {
  1322. return $ele?.innerHTML?.trim() === "";
  1323. });
  1324. }
  1325. else if (selector.match(/[^\s]{1}:contains\("(.*)"\)$/i) ||
  1326. selector.match(/[^\s]{1}:contains\('(.*)'\)$/i)) {
  1327. // contains 语法
  1328. let textMatch = selector.match(/:contains\(("|')(.*)("|')\)$/i);
  1329. let text = textMatch[2];
  1330. selector = selector.replace(/:contains\(("|')(.*)("|')\)$/gi, "");
  1331. return Array.from(parent.querySelectorAll(selector)).filter(($ele) => {
  1332. // @ts-ignore
  1333. return ($ele?.textContent || $ele?.innerText)?.includes(text);
  1334. });
  1335. }
  1336. else if (selector.match(/[^\s]{1}:regexp\("(.*)"\)$/i) ||
  1337. selector.match(/[^\s]{1}:regexp\('(.*)'\)$/i)) {
  1338. // regexp 语法
  1339. let textMatch = selector.match(/:regexp\(("|')(.*)("|')\)$/i);
  1340. let pattern = textMatch[2];
  1341. let flagMatch = pattern.match(/("|'),[\s]*("|')([igm]{0,3})$/i);
  1342. let flags = "";
  1343. if (flagMatch) {
  1344. pattern = pattern.replace(/("|'),[\s]*("|')([igm]{0,3})$/gi, "");
  1345. flags = flagMatch[3];
  1346. }
  1347. let regexp = new RegExp(pattern, flags);
  1348. selector = selector.replace(/:regexp\(("|')(.*)("|')\)$/gi, "");
  1349. return Array.from(parent.querySelectorAll(selector)).filter(($ele) => {
  1350. // @ts-ignore
  1351. return Boolean(($ele?.textContent || $ele?.innerText)?.match(regexp));
  1352. });
  1353. }
  1354. else {
  1355. // 普通语法
  1356. return Array.from(parent.querySelectorAll(selector));
  1357. }
  1358. }
  1359. /**
  1360. * 匹配元素,可使用以下的额外语法
  1361. *
  1362. * + :contains([text]) 作用: 找到包含指定文本内容的指定元素
  1363. * + :empty 作用:找到既没有文本内容也没有子元素的指定元素
  1364. * + :regexp([text]) 作用: 找到符合正则表达式的内容的指定元素
  1365. * @param $el 元素
  1366. * @param selector 选择器
  1367. * @example
  1368. * DOMUtils.matches("div:contains('测试')")
  1369. * > true
  1370. * @example
  1371. * DOMUtils.matches("div:empty")
  1372. * > true
  1373. * @example
  1374. * DOMUtils.matches("div:regexp('^xxxx$')")
  1375. * > true
  1376. * @example
  1377. * DOMUtils.matches("div:regexp(/^xxx/ig)")
  1378. * > false
  1379. */
  1380. matches($el, selector) {
  1381. selector = selector.trim();
  1382. if ($el == null) {
  1383. return false;
  1384. }
  1385. if (selector.match(/[^\s]{1}:empty$/gi)) {
  1386. // empty 语法
  1387. selector = selector.replace(/:empty$/gi, "");
  1388. return $el.matches(selector) && $el?.innerHTML?.trim() === "";
  1389. }
  1390. else if (selector.match(/[^\s]{1}:contains\("(.*)"\)$/i) ||
  1391. selector.match(/[^\s]{1}:contains\('(.*)'\)$/i)) {
  1392. // contains 语法
  1393. let textMatch = selector.match(/:contains\(("|')(.*)("|')\)$/i);
  1394. let text = textMatch[2];
  1395. selector = selector.replace(/:contains\(("|')(.*)("|')\)$/gi, "");
  1396. // @ts-ignore
  1397. let content = $el?.textContent || $el?.innerText;
  1398. if (typeof content !== "string") {
  1399. content = "";
  1400. }
  1401. return $el.matches(selector) && content?.includes(text);
  1402. }
  1403. else if (selector.match(/[^\s]{1}:regexp\("(.*)"\)$/i) ||
  1404. selector.match(/[^\s]{1}:regexp\('(.*)'\)$/i)) {
  1405. // regexp 语法
  1406. let textMatch = selector.match(/:regexp\(("|')(.*)("|')\)$/i);
  1407. let pattern = textMatch[2];
  1408. let flagMatch = pattern.match(/("|'),[\s]*("|')([igm]{0,3})$/i);
  1409. let flags = "";
  1410. if (flagMatch) {
  1411. pattern = pattern.replace(/("|'),[\s]*("|')([igm]{0,3})$/gi, "");
  1412. flags = flagMatch[3];
  1413. }
  1414. let regexp = new RegExp(pattern, flags);
  1415. selector = selector.replace(/:regexp\(("|')(.*)("|')\)$/gi, "");
  1416. // @ts-ignore
  1417. let content = $el?.textContent || $el?.innerText;
  1418. if (typeof content !== "string") {
  1419. content = "";
  1420. }
  1421. return $el.matches(selector) && Boolean(content?.match(regexp));
  1422. }
  1423. else {
  1424. // 普通语法
  1425. return $el.matches(selector);
  1426. }
  1427. }
  1428. closest($el, selector) {
  1429. selector = selector.trim();
  1430. if (selector.match(/[^\s]{1}:empty$/gi)) {
  1431. // empty 语法
  1432. selector = selector.replace(/:empty$/gi, "");
  1433. let $closest = $el?.closest(selector);
  1434. if ($closest && $closest?.innerHTML?.trim() === "") {
  1435. return $closest;
  1436. }
  1437. return null;
  1438. }
  1439. else if (selector.match(/[^\s]{1}:contains\("(.*)"\)$/i) ||
  1440. selector.match(/[^\s]{1}:contains\('(.*)'\)$/i)) {
  1441. // contains 语法
  1442. let textMatch = selector.match(/:contains\(("|')(.*)("|')\)$/i);
  1443. let text = textMatch[2];
  1444. selector = selector.replace(/:contains\(("|')(.*)("|')\)$/gi, "");
  1445. let $closest = $el?.closest(selector);
  1446. if ($closest) {
  1447. // @ts-ignore
  1448. let content = $el?.textContent || $el?.innerText;
  1449. if (typeof content === "string" && content.includes(text)) {
  1450. return $closest;
  1451. }
  1452. }
  1453. return null;
  1454. }
  1455. else if (selector.match(/[^\s]{1}:regexp\("(.*)"\)$/i) ||
  1456. selector.match(/[^\s]{1}:regexp\('(.*)'\)$/i)) {
  1457. // regexp 语法
  1458. let textMatch = selector.match(/:regexp\(("|')(.*)("|')\)$/i);
  1459. let pattern = textMatch[2];
  1460. let flagMatch = pattern.match(/("|'),[\s]*("|')([igm]{0,3})$/i);
  1461. let flags = "";
  1462. if (flagMatch) {
  1463. pattern = pattern.replace(/("|'),[\s]*("|')([igm]{0,3})$/gi, "");
  1464. flags = flagMatch[3];
  1465. }
  1466. let regexp = new RegExp(pattern, flags);
  1467. selector = selector.replace(/:regexp\(("|')(.*)("|')\)$/gi, "");
  1468. let $closest = $el?.closest(selector);
  1469. if ($closest) {
  1470. // @ts-ignore
  1471. let content = $el?.textContent || $el?.innerText;
  1472. if (typeof content === "string" && content.match(regexp)) {
  1473. return $closest;
  1474. }
  1475. }
  1476. return null;
  1477. }
  1478. else {
  1479. // 普通语法
  1480. let $closest = $el?.closest(selector);
  1481. return $closest;
  1482. }
  1483. }
  1484. }
  1485.  
  1486. class DOMUtils extends DOMUtilsEvent {
  1487. constructor(option) {
  1488. super(option);
  1489. }
  1490. /** 版本号 */
  1491. version = "2025.6.26";
  1492. attr(element, attrName, attrValue) {
  1493. let DOMUtilsContext = this;
  1494. if (typeof element === "string") {
  1495. element = DOMUtilsContext.selectorAll(element);
  1496. }
  1497. if (element == null) {
  1498. return;
  1499. }
  1500. if (DOMUtilsCommonUtils.isNodeList(element)) {
  1501. if (attrValue == null) {
  1502. // 获取属性
  1503. return DOMUtilsContext.attr(element[0], attrName, attrValue);
  1504. }
  1505. else {
  1506. // 设置属性
  1507. element.forEach(($ele) => {
  1508. DOMUtilsContext.attr($ele, attrName, attrValue);
  1509. });
  1510. return;
  1511. }
  1512. }
  1513. if (attrValue == null) {
  1514. return element.getAttribute(attrName);
  1515. }
  1516. else {
  1517. element.setAttribute(attrName, attrValue);
  1518. }
  1519. }
  1520. /**
  1521. * 创建元素
  1522. * @param tagName 标签名
  1523. * @param property 属性
  1524. * @param attributes 元素上的自定义属性
  1525. * @example
  1526. * // 创建一个DIV元素,且属性class为xxx
  1527. * DOMUtils.createElement("div",undefined,{ class:"xxx" });
  1528. * > <div class="xxx"></div>
  1529. * @example
  1530. * // 创建一个DIV元素
  1531. * DOMUtils.createElement("div");
  1532. * > <div></div>
  1533. * @example
  1534. * // 创建一个DIV元素
  1535. * DOMUtils.createElement("div","测试");
  1536. * > <div>测试</div>
  1537. */
  1538. createElement(
  1539. /** 元素名 */
  1540. tagName,
  1541. /** 属性 */
  1542. property,
  1543. /** 自定义属性 */
  1544. attributes) {
  1545. let DOMUtilsContext = this;
  1546. let tempElement = DOMUtilsContext.windowApi.document.createElement(tagName);
  1547. if (typeof property === "string") {
  1548. DOMUtilsContext.html(tempElement, property);
  1549. return tempElement;
  1550. }
  1551. if (property == null) {
  1552. property = {};
  1553. }
  1554. if (attributes == null) {
  1555. attributes = {};
  1556. }
  1557. Object.keys(property).forEach((key) => {
  1558. let value = property[key];
  1559. if (key === "innerHTML") {
  1560. DOMUtilsContext.html(tempElement, value);
  1561. return;
  1562. }
  1563. tempElement[key] = value;
  1564. });
  1565. Object.keys(attributes).forEach((key) => {
  1566. let value = attributes[key];
  1567. if (typeof value === "object") {
  1568. /* object转字符串 */
  1569. value = JSON.stringify(value);
  1570. }
  1571. else if (typeof value === "function") {
  1572. /* function转字符串 */
  1573. value = value.toString();
  1574. }
  1575. tempElement.setAttribute(key, value);
  1576. });
  1577. return tempElement;
  1578. }
  1579. css(element, property, value) {
  1580. let DOMUtilsContext = this;
  1581. /**
  1582. * 把纯数字没有px的加上
  1583. */
  1584. function handlePixe(propertyName, propertyValue) {
  1585. let allowAddPixe = [
  1586. "width",
  1587. "height",
  1588. "top",
  1589. "left",
  1590. "right",
  1591. "bottom",
  1592. "font-size",
  1593. ];
  1594. if (typeof propertyValue === "number") {
  1595. propertyValue = propertyValue.toString();
  1596. }
  1597. if (typeof propertyValue === "string" &&
  1598. allowAddPixe.includes(propertyName) &&
  1599. propertyValue.match(/[0-9]$/gi)) {
  1600. propertyValue = propertyValue + "px";
  1601. }
  1602. return propertyValue;
  1603. }
  1604. if (typeof element === "string") {
  1605. element = DOMUtilsContext.selectorAll(element);
  1606. }
  1607. if (element == null) {
  1608. return;
  1609. }
  1610. if (DOMUtilsCommonUtils.isNodeList(element)) {
  1611. if (typeof property === "string") {
  1612. if (value == null) {
  1613. // 获取属性
  1614. return DOMUtilsContext.css(element[0], property);
  1615. }
  1616. else {
  1617. // 设置属性
  1618. element.forEach(($ele) => {
  1619. DOMUtilsContext.css($ele, property);
  1620. });
  1621. return;
  1622. }
  1623. }
  1624. else if (typeof property === "object") {
  1625. // 设置属性
  1626. element.forEach(($ele) => {
  1627. DOMUtilsContext.css($ele, property);
  1628. });
  1629. return;
  1630. }
  1631. else ;
  1632. return;
  1633. }
  1634. let setStyleProperty = (propertyName, propertyValue) => {
  1635. if (typeof propertyValue === "string" &&
  1636. propertyValue.trim().endsWith("!important")) {
  1637. propertyValue = propertyValue
  1638. .trim()
  1639. .replace(/!important$/gi, "")
  1640. .trim();
  1641. element.style.setProperty(propertyName, propertyValue, "important");
  1642. }
  1643. else {
  1644. propertyValue = handlePixe(propertyName, propertyValue);
  1645. element.style.setProperty(propertyName, propertyValue);
  1646. }
  1647. };
  1648. if (typeof property === "string") {
  1649. if (value == null) {
  1650. return DOMUtilsContext.windowApi.globalThis
  1651. .getComputedStyle(element)
  1652. .getPropertyValue(property);
  1653. }
  1654. else {
  1655. setStyleProperty(property, value);
  1656. }
  1657. }
  1658. else if (typeof property === "object") {
  1659. for (let prop in property) {
  1660. let value = property[prop];
  1661. setStyleProperty(prop, value);
  1662. }
  1663. }
  1664. else {
  1665. // 其他情况
  1666. throw new TypeError("property must be string or object");
  1667. }
  1668. }
  1669. text(element, text) {
  1670. let DOMUtilsContext = this;
  1671. if (typeof element === "string") {
  1672. element = DOMUtilsContext.selectorAll(element);
  1673. }
  1674. if (element == null) {
  1675. return;
  1676. }
  1677. if (DOMUtilsCommonUtils.isNodeList(element)) {
  1678. if (text == null) {
  1679. // 获取
  1680. return DOMUtilsContext.text(element[0]);
  1681. }
  1682. else {
  1683. // 设置
  1684. element.forEach(($ele) => {
  1685. DOMUtilsContext.text($ele, text);
  1686. });
  1687. }
  1688. return;
  1689. }
  1690. if (text == null) {
  1691. return element.textContent || element.innerText;
  1692. }
  1693. else {
  1694. if (text instanceof Node) {
  1695. text = text.textContent || text.innerText;
  1696. }
  1697. if ("textContent" in element) {
  1698. element.textContent = text;
  1699. }
  1700. else if ("innerText" in element) {
  1701. element.innerText = text;
  1702. }
  1703. }
  1704. }
  1705. html(element, html) {
  1706. let DOMUtilsContext = this;
  1707. if (typeof element === "string") {
  1708. element = DOMUtilsContext.selectorAll(element);
  1709. }
  1710. if (element == null) {
  1711. return;
  1712. }
  1713. if (DOMUtilsCommonUtils.isNodeList(element)) {
  1714. if (html == null) {
  1715. // 获取
  1716. return DOMUtilsContext.html(element[0]);
  1717. }
  1718. else {
  1719. // 设置
  1720. element.forEach(($ele) => {
  1721. DOMUtilsContext.html($ele, html);
  1722. });
  1723. }
  1724. return;
  1725. }
  1726. if (html == null) {
  1727. // 获取
  1728. return element.innerHTML;
  1729. }
  1730. else {
  1731. // 设置
  1732. if (html instanceof Element) {
  1733. html = html.innerHTML;
  1734. }
  1735. if ("innerHTML" in element) {
  1736. DOMUtilsCommonUtils.setSafeHTML(element, html);
  1737. }
  1738. }
  1739. }
  1740. /**
  1741. * 获取移动元素的transform偏移
  1742. */
  1743. getTransform(element, isShow = false) {
  1744. let DOMUtilsContext = this;
  1745. let transform_left = 0;
  1746. let transform_top = 0;
  1747. if (!(isShow || (!isShow && DOMUtilsCommonUtils.isShow(element)))) {
  1748. /* 未显示 */
  1749. let { recovery } = DOMUtilsCommonUtils.showElement(element);
  1750. let transformInfo = DOMUtilsContext.getTransform(element, true);
  1751. recovery();
  1752. return transformInfo;
  1753. }
  1754. let elementTransform = DOMUtilsContext.windowApi.globalThis.getComputedStyle(element).transform;
  1755. if (elementTransform != null &&
  1756. elementTransform !== "none" &&
  1757. elementTransform !== "") {
  1758. let elementTransformSplit = elementTransform
  1759. .match(/\((.+)\)/)?.[1]
  1760. .split(",");
  1761. if (elementTransformSplit) {
  1762. transform_left = Math.abs(parseInt(elementTransformSplit[4]));
  1763. transform_top = Math.abs(parseInt(elementTransformSplit[5]));
  1764. }
  1765. else {
  1766. transform_left = 0;
  1767. transform_top = 0;
  1768. }
  1769. }
  1770. return {
  1771. transformLeft: transform_left,
  1772. transformTop: transform_top,
  1773. };
  1774. }
  1775. val(element, value) {
  1776. let DOMUtilsContext = this;
  1777. if (typeof element === "string") {
  1778. element = DOMUtilsContext.selectorAll(element);
  1779. }
  1780. if (element == null) {
  1781. return;
  1782. }
  1783. if (DOMUtilsCommonUtils.isNodeList(element)) {
  1784. if (value == null) {
  1785. // 获取
  1786. return DOMUtilsContext.val(element[0]);
  1787. }
  1788. else {
  1789. // 设置
  1790. element.forEach(($ele) => {
  1791. DOMUtilsContext.val($ele, value);
  1792. });
  1793. }
  1794. return;
  1795. }
  1796. if (value == null) {
  1797. // 获取
  1798. if (element.localName === "input" &&
  1799. (element.type === "checkbox" || element.type === "radio")) {
  1800. return element.checked;
  1801. }
  1802. else {
  1803. return element.value;
  1804. }
  1805. }
  1806. else {
  1807. // 设置
  1808. if (element.localName === "input" &&
  1809. (element.type === "checkbox" || element.type === "radio")) {
  1810. element.checked = !!value;
  1811. }
  1812. else {
  1813. element.value = value;
  1814. }
  1815. }
  1816. }
  1817. prop(element, propName, propValue) {
  1818. let DOMUtilsContext = this;
  1819. if (typeof element === "string") {
  1820. element = DOMUtilsContext.selectorAll(element);
  1821. }
  1822. if (element == null) {
  1823. return;
  1824. }
  1825. if (DOMUtilsCommonUtils.isNodeList(element)) {
  1826. if (propValue == null) {
  1827. // 获取
  1828. return DOMUtilsContext.prop(element[0], propName);
  1829. }
  1830. else {
  1831. // 设置
  1832. element.forEach(($ele) => {
  1833. DOMUtilsContext.prop($ele, propName, propValue);
  1834. });
  1835. }
  1836. return;
  1837. }
  1838. if (propValue == null) {
  1839. return Reflect.get(element, propName);
  1840. }
  1841. else {
  1842. if (element instanceof Element && propName === "innerHTML") {
  1843. DOMUtilsContext.html(element, propValue);
  1844. }
  1845. else {
  1846. Reflect.set(element, propName, propValue);
  1847. }
  1848. }
  1849. }
  1850. /**
  1851. * 移除元素的属性
  1852. * @param element 目标元素
  1853. * @param attrName 属性名
  1854. * @example
  1855. * // 移除元素a.xx的属性data-value
  1856. * DOMUtils.removeAttr(document.querySelector("a.xx"),"data-value")
  1857. * DOMUtils.removeAttr("a.xx","data-value")
  1858. * */
  1859. removeAttr(element, attrName) {
  1860. let DOMUtilsContext = this;
  1861. if (typeof element === "string") {
  1862. element = DOMUtilsContext.selectorAll(element);
  1863. }
  1864. if (element == null) {
  1865. return;
  1866. }
  1867. if (DOMUtilsCommonUtils.isNodeList(element)) {
  1868. // 设置
  1869. element.forEach(($ele) => {
  1870. DOMUtilsContext.removeAttr($ele, attrName);
  1871. });
  1872. return;
  1873. }
  1874. element.removeAttribute(attrName);
  1875. }
  1876. /**
  1877. * 移除元素class名
  1878. * @param element 目标元素
  1879. * @param className 类名
  1880. * @example
  1881. * // 移除元素a.xx的className为xx
  1882. * DOMUtils.removeClass(document.querySelector("a.xx"),"xx")
  1883. * DOMUtils.removeClass("a.xx","xx")
  1884. */
  1885. removeClass(element, className) {
  1886. let DOMUtilsContext = this;
  1887. if (typeof element === "string") {
  1888. element = DOMUtilsContext.selectorAll(element);
  1889. }
  1890. if (element == null) {
  1891. return;
  1892. }
  1893. if (DOMUtilsCommonUtils.isNodeList(element)) {
  1894. // 设置
  1895. element.forEach(($ele) => {
  1896. DOMUtilsContext.removeClass($ele, className);
  1897. });
  1898. return;
  1899. }
  1900. if (className == null) {
  1901. // 清空全部className
  1902. element.className = "";
  1903. }
  1904. else {
  1905. if (!Array.isArray(className)) {
  1906. className = className.split(" ");
  1907. }
  1908. className.forEach((itemClassName) => {
  1909. element.classList.remove(itemClassName);
  1910. });
  1911. }
  1912. }
  1913. /**
  1914. * 移除元素的属性
  1915. * @param element 目标元素
  1916. * @param propName 属性名
  1917. * @example
  1918. * // 移除元素a.xx的href属性
  1919. * DOMUtils.removeProp(document.querySelector("a.xx"),"href")
  1920. * DOMUtils.removeProp("a.xx","href")
  1921. * */
  1922. removeProp(element, propName) {
  1923. let DOMUtilsContext = this;
  1924. if (typeof element === "string") {
  1925. element = DOMUtilsContext.selectorAll(element);
  1926. }
  1927. if (element == null) {
  1928. return;
  1929. }
  1930. if (DOMUtilsCommonUtils.isNodeList(element)) {
  1931. // 设置
  1932. element.forEach(($ele) => {
  1933. DOMUtilsContext.removeProp($ele, propName);
  1934. });
  1935. return;
  1936. }
  1937. DOMUtilsCommonUtils.delete(element, propName);
  1938. }
  1939. /**
  1940. * 将一个元素替换为另一个元素
  1941. * @param element 目标元素
  1942. * @param newElement 新元素
  1943. * @example
  1944. * // 替换元素a.xx为b.xx
  1945. * DOMUtils.replaceWith(document.querySelector("a.xx"),document.querySelector("b.xx"))
  1946. * DOMUtils.replaceWith("a.xx",'<b class="xx"></b>')
  1947. */
  1948. replaceWith(element, newElement) {
  1949. let DOMUtilsContext = this;
  1950. if (typeof element === "string") {
  1951. element = DOMUtilsContext.selectorAll(element);
  1952. }
  1953. if (element == null) {
  1954. return;
  1955. }
  1956. if (DOMUtilsCommonUtils.isNodeList(element)) {
  1957. // 设置
  1958. element.forEach(($ele) => {
  1959. DOMUtilsContext.replaceWith($ele, newElement);
  1960. });
  1961. return;
  1962. }
  1963. if (typeof newElement === "string") {
  1964. newElement = DOMUtilsContext.parseHTML(newElement, false, false);
  1965. }
  1966. element.parentElement.replaceChild(newElement, element);
  1967. }
  1968. /**
  1969. * 给元素添加class
  1970. * @param element 目标元素
  1971. * @param className class名
  1972. * @example
  1973. * // 元素a.xx的className添加_vue_
  1974. * DOMUtils.addClass(document.querySelector("a.xx"),"_vue_")
  1975. * DOMUtils.addClass("a.xx","_vue_")
  1976. * */
  1977. addClass(element, className) {
  1978. let DOMUtilsContext = this;
  1979. if (typeof element === "string") {
  1980. element = DOMUtilsContext.selectorAll(element);
  1981. }
  1982. if (element == null) {
  1983. return;
  1984. }
  1985. if (DOMUtilsCommonUtils.isNodeList(element)) {
  1986. // 设置
  1987. element.forEach(($ele) => {
  1988. DOMUtilsContext.addClass($ele, className);
  1989. });
  1990. return;
  1991. }
  1992. if (!Array.isArray(className)) {
  1993. className = className.split(" ");
  1994. }
  1995. className.forEach((itemClassName) => {
  1996. if (itemClassName.trim() == "") {
  1997. return;
  1998. }
  1999. element.classList.add(itemClassName);
  2000. });
  2001. }
  2002. /**
  2003. * 判断元素是否存在className
  2004. * @param element
  2005. * @param className
  2006. */
  2007. hasClass(element, className) {
  2008. let DOMUtilsContext = this;
  2009. if (typeof element === "string") {
  2010. element = DOMUtilsContext.selectorAll(element);
  2011. }
  2012. if (element == null) {
  2013. return false;
  2014. }
  2015. if (DOMUtilsCommonUtils.isNodeList(element)) {
  2016. let flag = true;
  2017. for (let index = 0; index < element.length; index++) {
  2018. const $ele = element[index];
  2019. flag = flag && DOMUtilsContext.hasClass($ele, className);
  2020. }
  2021. return flag;
  2022. }
  2023. if (!element?.classList) {
  2024. return false;
  2025. }
  2026. if (!Array.isArray(className)) {
  2027. className = className.split(" ");
  2028. }
  2029. for (let index = 0; index < className.length; index++) {
  2030. const item = className[index].trim();
  2031. if (!element.classList.contains(item)) {
  2032. return false;
  2033. }
  2034. }
  2035. return true;
  2036. }
  2037. /**
  2038. * 函数在元素内部末尾添加子元素或HTML字符串
  2039. * @param element 目标元素
  2040. * @param content 子元素或HTML字符串
  2041. * @example
  2042. * // 元素a.xx的内部末尾添加一个元素
  2043. * DOMUtils.append(document.querySelector("a.xx"),document.querySelector("b.xx"))
  2044. * DOMUtils.append("a.xx","'<b class="xx"></b>")
  2045. * */
  2046. append(element, content) {
  2047. let DOMUtilsContext = this;
  2048. if (typeof element === "string") {
  2049. element = DOMUtilsContext.selectorAll(element);
  2050. }
  2051. if (element == null) {
  2052. return;
  2053. }
  2054. if (DOMUtilsCommonUtils.isNodeList(element)) {
  2055. // 设置
  2056. element.forEach(($ele) => {
  2057. DOMUtilsContext.append($ele, content);
  2058. });
  2059. return;
  2060. }
  2061. function elementAppendChild(ele, text) {
  2062. if (typeof content === "string") {
  2063. ele.insertAdjacentHTML("beforeend", DOMUtilsCommonUtils.getSafeHTML(text));
  2064. }
  2065. else {
  2066. ele.appendChild(text);
  2067. }
  2068. }
  2069. if (Array.isArray(content) || content instanceof NodeList) {
  2070. /* 数组 */
  2071. let fragment = DOMUtilsContext.windowApi.document.createDocumentFragment();
  2072. content.forEach((ele) => {
  2073. if (typeof ele === "string") {
  2074. ele = DOMUtilsContext.parseHTML(ele, true, false);
  2075. }
  2076. fragment.appendChild(ele);
  2077. });
  2078. element.appendChild(fragment);
  2079. }
  2080. else {
  2081. elementAppendChild(element, content);
  2082. }
  2083. }
  2084. /**
  2085. * 函数 在元素内部开头添加子元素或HTML字符串
  2086. * @param element 目标元素
  2087. * @param content 子元素或HTML字符串
  2088. * @example
  2089. * // 元素a.xx内部开头添加一个元素
  2090. * DOMUtils.prepend(document.querySelector("a.xx"),document.querySelector("b.xx"))
  2091. * DOMUtils.prepend("a.xx","'<b class="xx"></b>")
  2092. * */
  2093. prepend(element, content) {
  2094. let DOMUtilsContext = this;
  2095. if (typeof element === "string") {
  2096. element = DOMUtilsContext.selectorAll(element);
  2097. }
  2098. if (element == null) {
  2099. return;
  2100. }
  2101. if (DOMUtilsCommonUtils.isNodeList(element)) {
  2102. // 设置
  2103. element.forEach(($ele) => {
  2104. DOMUtilsContext.prepend($ele, content);
  2105. });
  2106. return;
  2107. }
  2108. if (typeof content === "string") {
  2109. element.insertAdjacentHTML("afterbegin", DOMUtilsCommonUtils.getSafeHTML(content));
  2110. }
  2111. else {
  2112. let $firstChild = element.firstChild;
  2113. if ($firstChild == null) {
  2114. element.prepend(content);
  2115. }
  2116. else {
  2117. element.insertBefore(content, element.firstChild);
  2118. }
  2119. }
  2120. }
  2121. /**
  2122. * 在元素后面添加兄弟元素或HTML字符串
  2123. * @param element 目标元素
  2124. * @param content 兄弟元素或HTML字符串
  2125. * @example
  2126. * // 元素a.xx后面添加一个元素
  2127. * DOMUtils.after(document.querySelector("a.xx"),document.querySelector("b.xx"))
  2128. * DOMUtils.after("a.xx","'<b class="xx"></b>")
  2129. * */
  2130. after(element, content) {
  2131. let DOMUtilsContext = this;
  2132. if (typeof element === "string") {
  2133. element = DOMUtilsContext.selectorAll(element);
  2134. }
  2135. if (element == null) {
  2136. return;
  2137. }
  2138. if (DOMUtilsCommonUtils.isNodeList(element)) {
  2139. // 设置
  2140. element.forEach(($ele) => {
  2141. DOMUtilsContext.after($ele, content);
  2142. });
  2143. return;
  2144. }
  2145. if (typeof content === "string") {
  2146. element.insertAdjacentHTML("afterend", DOMUtilsCommonUtils.getSafeHTML(content));
  2147. }
  2148. else {
  2149. let $parent = element.parentElement;
  2150. let $nextSlibling = element.nextSibling;
  2151. if (!$parent || $nextSlibling) {
  2152. // 任意一个不行
  2153. element.after(content);
  2154. }
  2155. else {
  2156. element.parentElement.insertBefore(content, element.nextSibling);
  2157. }
  2158. }
  2159. }
  2160. /**
  2161. * 在元素前面添加兄弟元素或HTML字符串
  2162. * @param element 目标元素
  2163. * @param content 兄弟元素或HTML字符串
  2164. * @example
  2165. * // 元素a.xx前面添加一个元素
  2166. * DOMUtils.before(document.querySelector("a.xx"),document.querySelector("b.xx"))
  2167. * DOMUtils.before("a.xx","'<b class="xx"></b>")
  2168. * */
  2169. before(element, content) {
  2170. let DOMUtilsContext = this;
  2171. if (typeof element === "string") {
  2172. element = DOMUtilsContext.selectorAll(element);
  2173. }
  2174. if (element == null) {
  2175. return;
  2176. }
  2177. if (DOMUtilsCommonUtils.isNodeList(element)) {
  2178. // 设置
  2179. element.forEach(($ele) => {
  2180. DOMUtilsContext.before($ele, content);
  2181. });
  2182. return;
  2183. }
  2184. if (typeof content === "string") {
  2185. element.insertAdjacentHTML("beforebegin", DOMUtilsCommonUtils.getSafeHTML(content));
  2186. }
  2187. else {
  2188. let $parent = element.parentElement;
  2189. if (!$parent) {
  2190. element.before(content);
  2191. }
  2192. else {
  2193. $parent.insertBefore(content, element);
  2194. }
  2195. }
  2196. }
  2197. /**
  2198. * 移除元素
  2199. * @param element 目标元素
  2200. * @example
  2201. * // 元素a.xx前面添加一个元素
  2202. * DOMUtils.remove(document.querySelector("a.xx"))
  2203. * DOMUtils.remove(document.querySelectorAll("a.xx"))
  2204. * DOMUtils.remove("a.xx")
  2205. * */
  2206. remove(element) {
  2207. let DOMUtilsContext = this;
  2208. if (typeof element === "string") {
  2209. element = DOMUtilsContext.selectorAll(element);
  2210. }
  2211. if (element == null) {
  2212. return;
  2213. }
  2214. if (DOMUtilsCommonUtils.isNodeList(element)) {
  2215. element.forEach(($ele) => {
  2216. DOMUtilsContext.remove($ele);
  2217. });
  2218. return;
  2219. }
  2220. element.remove();
  2221. }
  2222. /**
  2223. * 移除元素的所有子元素
  2224. * @param element 目标元素
  2225. * @example
  2226. * // 移除元素a.xx元素的所有子元素
  2227. * DOMUtils.empty(document.querySelector("a.xx"))
  2228. * DOMUtils.empty("a.xx")
  2229. * */
  2230. empty(element) {
  2231. let DOMUtilsContext = this;
  2232. if (typeof element === "string") {
  2233. element = DOMUtilsContext.selectorAll(element);
  2234. }
  2235. if (element == null) {
  2236. return;
  2237. }
  2238. if (DOMUtilsCommonUtils.isNodeList(element)) {
  2239. // 设置
  2240. element.forEach(($ele) => {
  2241. DOMUtilsContext.empty($ele);
  2242. });
  2243. return;
  2244. }
  2245. DOMUtilsContext.html(element, "");
  2246. }
  2247. /**
  2248. * 获取元素相对于文档的偏移坐标(加上文档的滚动条)
  2249. * @param element 目标元素
  2250. * @example
  2251. * // 获取元素a.xx的对于文档的偏移坐标
  2252. * DOMUtils.offset(document.querySelector("a.xx"))
  2253. * DOMUtils.offset("a.xx")
  2254. * > 0
  2255. */
  2256. offset(element) {
  2257. let DOMUtilsContext = this;
  2258. if (typeof element === "string") {
  2259. element = DOMUtilsContext.selector(element);
  2260. }
  2261. if (element == null) {
  2262. return;
  2263. }
  2264. let rect = element.getBoundingClientRect();
  2265. return {
  2266. /** y轴偏移 */
  2267. top: rect.top + DOMUtilsContext.windowApi.globalThis.scrollY,
  2268. /** x轴偏移 */
  2269. left: rect.left + DOMUtilsContext.windowApi.globalThis.scrollX,
  2270. };
  2271. }
  2272. width(element, isShow = false) {
  2273. let DOMUtilsContext = this;
  2274. if (typeof element === "string") {
  2275. element = DOMUtilsContext.selector(element);
  2276. }
  2277. if (element == null) {
  2278. // @ts-ignore
  2279. return;
  2280. }
  2281. if (DOMUtilsCommonUtils.isWin(element)) {
  2282. return DOMUtilsContext.windowApi.window.document.documentElement
  2283. .clientWidth;
  2284. }
  2285. if (element.nodeType === 9) {
  2286. /* Document文档节点 */
  2287. element = element;
  2288. return Math.max(element.body.scrollWidth, element.documentElement.scrollWidth, element.body.offsetWidth, element.documentElement.offsetWidth, element.documentElement.clientWidth);
  2289. }
  2290. if (isShow ||
  2291. (!isShow && DOMUtilsCommonUtils.isShow(element))) {
  2292. /* 已显示 */
  2293. /* 不从style中获取对应的宽度,因为可能使用了class定义了width !important */
  2294. element = element;
  2295. /* 如果element.style.width为空 则从css里面获取是否定义了width信息如果定义了 则读取css里面定义的宽度width */
  2296. if (parseFloat(DOMUtilsCommonUtils.getStyleValue(element, "width").toString()) > 0) {
  2297. return parseFloat(DOMUtilsCommonUtils.getStyleValue(element, "width").toString());
  2298. }
  2299. /* 如果从css里获取到的值不是大于0 可能是auto 则通过offsetWidth来进行计算 */
  2300. if (element.offsetWidth > 0) {
  2301. let borderLeftWidth = DOMUtilsCommonUtils.getStyleValue(element, "borderLeftWidth");
  2302. let borderRightWidth = DOMUtilsCommonUtils.getStyleValue(element, "borderRightWidth");
  2303. let paddingLeft = DOMUtilsCommonUtils.getStyleValue(element, "paddingLeft");
  2304. let paddingRight = DOMUtilsCommonUtils.getStyleValue(element, "paddingRight");
  2305. let backHeight = parseFloat(element.offsetWidth.toString()) -
  2306. parseFloat(borderLeftWidth.toString()) -
  2307. parseFloat(borderRightWidth.toString()) -
  2308. parseFloat(paddingLeft.toString()) -
  2309. parseFloat(paddingRight.toString());
  2310. return parseFloat(backHeight.toString());
  2311. }
  2312. return 0;
  2313. }
  2314. else {
  2315. /* 未显示 */
  2316. element = element;
  2317. let { recovery } = DOMUtilsCommonUtils.showElement(element);
  2318. let width = DOMUtilsContext.width(element, true);
  2319. recovery();
  2320. return width;
  2321. }
  2322. }
  2323. height(element, isShow = false) {
  2324. let DOMUtilsContext = this;
  2325. if (DOMUtilsCommonUtils.isWin(element)) {
  2326. return DOMUtilsContext.windowApi.window.document.documentElement
  2327. .clientHeight;
  2328. }
  2329. if (typeof element === "string") {
  2330. element = DOMUtilsContext.selector(element);
  2331. }
  2332. if (element == null) {
  2333. // @ts-ignore
  2334. return;
  2335. }
  2336. if (element.nodeType === 9) {
  2337. element = element;
  2338. /* Document文档节点 */
  2339. return Math.max(element.body.scrollHeight, element.documentElement.scrollHeight, element.body.offsetHeight, element.documentElement.offsetHeight, element.documentElement.clientHeight);
  2340. }
  2341. if (isShow ||
  2342. (!isShow && DOMUtilsCommonUtils.isShow(element))) {
  2343. element = element;
  2344. /* 已显示 */
  2345. /* 从style中获取对应的高度,因为可能使用了class定义了width !important */
  2346. /* 如果element.style.height为空 则从css里面获取是否定义了height信息如果定义了 则读取css里面定义的高度height */
  2347. if (parseFloat(DOMUtilsCommonUtils.getStyleValue(element, "height").toString()) > 0) {
  2348. return parseFloat(DOMUtilsCommonUtils.getStyleValue(element, "height").toString());
  2349. }
  2350. /* 如果从css里获取到的值不是大于0 可能是auto 则通过offsetHeight来进行计算 */
  2351. if (element.offsetHeight > 0) {
  2352. let borderTopWidth = DOMUtilsCommonUtils.getStyleValue(element, "borderTopWidth");
  2353. let borderBottomWidth = DOMUtilsCommonUtils.getStyleValue(element, "borderBottomWidth");
  2354. let paddingTop = DOMUtilsCommonUtils.getStyleValue(element, "paddingTop");
  2355. let paddingBottom = DOMUtilsCommonUtils.getStyleValue(element, "paddingBottom");
  2356. let backHeight = parseFloat(element.offsetHeight.toString()) -
  2357. parseFloat(borderTopWidth.toString()) -
  2358. parseFloat(borderBottomWidth.toString()) -
  2359. parseFloat(paddingTop.toString()) -
  2360. parseFloat(paddingBottom.toString());
  2361. return parseFloat(backHeight.toString());
  2362. }
  2363. return 0;
  2364. }
  2365. else {
  2366. /* 未显示 */
  2367. element = element;
  2368. let { recovery } = DOMUtilsCommonUtils.showElement(element);
  2369. let height = DOMUtilsContext.height(element, true);
  2370. recovery();
  2371. return height;
  2372. }
  2373. }
  2374. outerWidth(element, isShow = false) {
  2375. let DOMUtilsContext = this;
  2376. if (DOMUtilsCommonUtils.isWin(element)) {
  2377. return DOMUtilsContext.windowApi.window.innerWidth;
  2378. }
  2379. if (typeof element === "string") {
  2380. element = DOMUtilsContext.selector(element);
  2381. }
  2382. if (element == null) {
  2383. // @ts-ignore
  2384. return;
  2385. }
  2386. element = element;
  2387. if (isShow || (!isShow && DOMUtilsCommonUtils.isShow(element))) {
  2388. let style = DOMUtilsContext.windowApi.globalThis.getComputedStyle(element, null);
  2389. let marginLeft = DOMUtilsCommonUtils.getStyleValue(style, "marginLeft");
  2390. let marginRight = DOMUtilsCommonUtils.getStyleValue(style, "marginRight");
  2391. return element.offsetWidth + marginLeft + marginRight;
  2392. }
  2393. else {
  2394. let { recovery } = DOMUtilsCommonUtils.showElement(element);
  2395. let outerWidth = DOMUtilsContext.outerWidth(element, true);
  2396. recovery();
  2397. return outerWidth;
  2398. }
  2399. }
  2400. outerHeight(element, isShow = false) {
  2401. let DOMUtilsContext = this;
  2402. if (DOMUtilsCommonUtils.isWin(element)) {
  2403. return DOMUtilsContext.windowApi.window.innerHeight;
  2404. }
  2405. if (typeof element === "string") {
  2406. element = DOMUtilsContext.selector(element);
  2407. }
  2408. if (element == null) {
  2409. // @ts-ignore
  2410. return;
  2411. }
  2412. element = element;
  2413. if (isShow || (!isShow && DOMUtilsCommonUtils.isShow(element))) {
  2414. let style = DOMUtilsContext.windowApi.globalThis.getComputedStyle(element, null);
  2415. let marginTop = DOMUtilsCommonUtils.getStyleValue(style, "marginTop");
  2416. let marginBottom = DOMUtilsCommonUtils.getStyleValue(style, "marginBottom");
  2417. return element.offsetHeight + marginTop + marginBottom;
  2418. }
  2419. else {
  2420. let { recovery } = DOMUtilsCommonUtils.showElement(element);
  2421. let outerHeight = DOMUtilsContext.outerHeight(element, true);
  2422. recovery();
  2423. return outerHeight;
  2424. }
  2425. }
  2426. /**
  2427. * 在一定时间内改变元素的样式属性,实现动画效果
  2428. * @param element 需要进行动画的元素
  2429. * @param styles 动画结束时元素的样式属性
  2430. * @param duration 动画持续时间,单位为毫秒
  2431. * @param callback 动画结束后执行的函数
  2432. * @example
  2433. * // 监听元素a.xx的从显示变为隐藏
  2434. * DOMUtils.animate(document.querySelector("a.xx"),{ top:100},1000,function(){
  2435. * console.log("已往上位移100px")
  2436. * })
  2437. */
  2438. animate(element, styles, duration = 1000, callback = null) {
  2439. let DOMUtilsContext = this;
  2440. if (typeof element === "string") {
  2441. element = DOMUtilsContext.selectorAll(element);
  2442. }
  2443. if (element == null) {
  2444. return;
  2445. }
  2446. if (DOMUtilsCommonUtils.isNodeList(element)) {
  2447. // 设置
  2448. element.forEach(($ele) => {
  2449. DOMUtilsContext.animate($ele, styles, duration, callback);
  2450. });
  2451. return;
  2452. }
  2453. if (typeof duration !== "number" || duration <= 0) {
  2454. throw new TypeError("duration must be a positive number");
  2455. }
  2456. if (typeof callback !== "function" && callback !== void 0) {
  2457. throw new TypeError("callback must be a function or null");
  2458. }
  2459. if (typeof styles !== "object" || styles === void 0) {
  2460. throw new TypeError("styles must be an object");
  2461. }
  2462. if (Object.keys(styles).length === 0) {
  2463. throw new Error("styles must contain at least one property");
  2464. }
  2465. let start = performance.now();
  2466. let from = {};
  2467. let to = {};
  2468. for (let prop in styles) {
  2469. from[prop] =
  2470. element.style[prop] ||
  2471. DOMUtilsContext.windowApi.globalThis.getComputedStyle(element)[prop];
  2472. to[prop] = styles[prop];
  2473. }
  2474. let timer = DOMUtilsCommonUtils.setInterval(function () {
  2475. let timePassed = performance.now() - start;
  2476. let progress = timePassed / duration;
  2477. if (progress > 1) {
  2478. progress = 1;
  2479. }
  2480. for (let prop in styles) {
  2481. element.style[prop] =
  2482. from[prop] + (to[prop] - from[prop]) * progress + "px";
  2483. }
  2484. if (progress === 1) {
  2485. DOMUtilsCommonUtils.clearInterval(timer);
  2486. if (callback) {
  2487. callback();
  2488. }
  2489. }
  2490. }, 10);
  2491. }
  2492. /**
  2493. * 将一个元素包裹在指定的HTML元素中
  2494. * @param element 要包裹的元素
  2495. * @param wrapperHTML 要包裹的HTML元素的字符串表示形式
  2496. * @example
  2497. * // 将a.xx元素外面包裹一层div
  2498. * DOMUtils.wrap(document.querySelector("a.xx"),"<div></div>")
  2499. */
  2500. wrap(element, wrapperHTML) {
  2501. let DOMUtilsContext = this;
  2502. if (typeof element === "string") {
  2503. element = DOMUtilsContext.selectorAll(element);
  2504. }
  2505. if (element == null) {
  2506. return;
  2507. }
  2508. if (DOMUtilsCommonUtils.isNodeList(element)) {
  2509. // 设置
  2510. element.forEach(($ele) => {
  2511. DOMUtilsContext.wrap($ele, wrapperHTML);
  2512. });
  2513. return;
  2514. }
  2515. element = element;
  2516. // 创建一个新的div元素,并将wrapperHTML作为其innerHTML
  2517. let wrapper = DOMUtilsContext.windowApi.document.createElement("div");
  2518. DOMUtilsContext.html(wrapper, wrapperHTML);
  2519. let wrapperFirstChild = wrapper.firstChild;
  2520. // 将要包裹的元素插入目标元素前面
  2521. let parentElement = element.parentElement;
  2522. parentElement.insertBefore(wrapperFirstChild, element);
  2523. // 将要包裹的元素移动到wrapper中
  2524. wrapperFirstChild.appendChild(element);
  2525. }
  2526. prev(element) {
  2527. let DOMUtilsContext = this;
  2528. if (typeof element === "string") {
  2529. element = DOMUtilsContext.selector(element);
  2530. }
  2531. if (element == null) {
  2532. return;
  2533. }
  2534. return element.previousElementSibling;
  2535. }
  2536. next(element) {
  2537. let DOMUtilsContext = this;
  2538. if (typeof element === "string") {
  2539. element = DOMUtilsContext.selector(element);
  2540. }
  2541. if (element == null) {
  2542. return;
  2543. }
  2544. return element.nextElementSibling;
  2545. }
  2546. /**
  2547. * 取消挂载在window下的DOMUtils并返回DOMUtils
  2548. * @example
  2549. * let DOMUtils = window.DOMUtils.noConflict()
  2550. */
  2551. noConflict() {
  2552. let DOMUtilsContext = this;
  2553. if (DOMUtilsContext.windowApi.window.DOMUtils) {
  2554. DOMUtilsCommonUtils.delete(window, "DOMUtils");
  2555. }
  2556. DOMUtilsContext.windowApi.window.DOMUtils = this;
  2557. return this;
  2558. }
  2559. siblings(element) {
  2560. let DOMUtilsContext = this;
  2561. if (typeof element === "string") {
  2562. element = DOMUtilsContext.selector(element);
  2563. }
  2564. if (element == null) {
  2565. return;
  2566. }
  2567. return Array.from(element.parentElement
  2568. .children).filter((child) => child !== element);
  2569. }
  2570. /**
  2571. * 获取当前元素的父元素
  2572. * @param element 当前元素
  2573. * @returns 父元素
  2574. * @example
  2575. * // 获取a.xx元素的父元素
  2576. * DOMUtils.parent(document.querySelector("a.xx"))
  2577. * DOMUtils.parent("a.xx")
  2578. * > <div ...>....</div>
  2579. */
  2580. parent(element) {
  2581. let DOMUtilsContext = this;
  2582. if (typeof element === "string") {
  2583. element = DOMUtilsContext.selector(element);
  2584. }
  2585. if (element == null) {
  2586. return;
  2587. }
  2588. if (DOMUtilsCommonUtils.isNodeList(element)) {
  2589. let resultArray = [];
  2590. element.forEach(($ele) => {
  2591. resultArray.push(DOMUtilsContext.parent($ele));
  2592. });
  2593. return resultArray;
  2594. }
  2595. else {
  2596. return element.parentElement;
  2597. }
  2598. }
  2599. parseHTML(html, useParser = false, isComplete = false) {
  2600. let DOMUtilsContext = this;
  2601. // 去除html前后的空格
  2602. html = html.trim();
  2603. function parseHTMLByDOMParser() {
  2604. let parser = new DOMParser();
  2605. if (isComplete) {
  2606. return parser.parseFromString(html, "text/html");
  2607. }
  2608. else {
  2609. return parser.parseFromString(html, "text/html").body.firstChild;
  2610. }
  2611. }
  2612. function parseHTMLByCreateDom() {
  2613. let tempDIV = DOMUtilsContext.windowApi.document.createElement("div");
  2614. DOMUtilsContext.html(tempDIV, html);
  2615. if (isComplete) {
  2616. return tempDIV;
  2617. }
  2618. else {
  2619. return tempDIV.firstChild;
  2620. }
  2621. }
  2622. if (useParser) {
  2623. return parseHTMLByDOMParser();
  2624. }
  2625. else {
  2626. return parseHTMLByCreateDom();
  2627. }
  2628. }
  2629. /**
  2630. * 序列化表单元素
  2631. * @param $form 表单元素
  2632. * @example
  2633. * DOMUtils.serialize(document.querySelector("form"))
  2634. * > xxx=xxx&aaa=
  2635. */
  2636. serialize($form) {
  2637. const elements = $form.elements;
  2638. let serializedArray = [];
  2639. for (let i = 0; i < elements.length; i++) {
  2640. const element = elements[i];
  2641. if (element.name &&
  2642. !element.disabled &&
  2643. (element.checked ||
  2644. [
  2645. "text",
  2646. "hidden",
  2647. "password",
  2648. "textarea",
  2649. "select-one",
  2650. "select-multiple",
  2651. ].includes(element.type))) {
  2652. if (element.type === "select-multiple") {
  2653. for (let j = 0; j < element.options.length; j++) {
  2654. if (element.options[j].selected) {
  2655. serializedArray.push({
  2656. name: element.name,
  2657. value: element.options[j].value,
  2658. });
  2659. }
  2660. }
  2661. }
  2662. else {
  2663. serializedArray.push({ name: element.name, value: element.value });
  2664. }
  2665. }
  2666. }
  2667. return serializedArray
  2668. .map((item) => `${encodeURIComponent(item.name)}=${encodeURIComponent(item.value)}`)
  2669. .join("&");
  2670. }
  2671. /**
  2672. * 显示元素
  2673. * @param target 当前元素
  2674. * @param checkVisiblie 是否检测元素是否显示
  2675. * + true (默认)如果检测到还未显示,则强制使用display: unset !important;
  2676. * + false 不检测,直接设置display属性为空
  2677. * @example
  2678. * // 显示a.xx元素
  2679. * DOMUtils.show(document.querySelector("a.xx"))
  2680. * DOMUtils.show(document.querySelectorAll("a.xx"))
  2681. * DOMUtils.show("a.xx")
  2682. */
  2683. show(target, checkVisiblie = true) {
  2684. let DOMUtilsContext = this;
  2685. if (target == null) {
  2686. return;
  2687. }
  2688. if (typeof target === "string") {
  2689. target = DOMUtilsContext.selectorAll(target);
  2690. }
  2691. if (target instanceof NodeList || target instanceof Array) {
  2692. target = target;
  2693. for (const element of target) {
  2694. DOMUtilsContext.show(element, checkVisiblie);
  2695. }
  2696. }
  2697. else {
  2698. target = target;
  2699. target.style.display = "";
  2700. if (checkVisiblie) {
  2701. if (!DOMUtilsCommonUtils.isShow(target)) {
  2702. /* 仍然是不显示,尝试使用强覆盖 */
  2703. target.style.setProperty("display", "unset", "important");
  2704. }
  2705. }
  2706. }
  2707. }
  2708. /**
  2709. * 隐藏元素
  2710. * @param target 当前元素
  2711. * @param checkVisiblie 是否检测元素是否显示
  2712. * + true (默认)如果检测到显示,则强制使用display: none !important;
  2713. * + false 不检测,直接设置display属性为none
  2714. * @example
  2715. * // 隐藏a.xx元素
  2716. * DOMUtils.hide(document.querySelector("a.xx"))
  2717. * DOMUtils.hide(document.querySelectorAll("a.xx"))
  2718. * DOMUtils.hide("a.xx")
  2719. */
  2720. hide(target, checkVisiblie = true) {
  2721. let DOMUtilsContext = this;
  2722. if (target == null) {
  2723. return;
  2724. }
  2725. if (typeof target === "string") {
  2726. target = DOMUtilsContext.selectorAll(target);
  2727. }
  2728. if (target instanceof NodeList || target instanceof Array) {
  2729. target = target;
  2730. for (const element of target) {
  2731. DOMUtilsContext.hide(element, checkVisiblie);
  2732. }
  2733. }
  2734. else {
  2735. target = target;
  2736. target.style.display = "none";
  2737. if (checkVisiblie) {
  2738. if (DOMUtilsCommonUtils.isShow(target)) {
  2739. /* 仍然是显示,尝试使用强覆盖 */
  2740. target.style.setProperty("display", "none", "important");
  2741. }
  2742. }
  2743. }
  2744. }
  2745. /**
  2746. * 淡入元素
  2747. * @param element 当前元素
  2748. * @param duration 动画持续时间(毫秒),默认400毫秒
  2749. * @param callback 动画结束的回调
  2750. * @example
  2751. * // 元素a.xx淡入
  2752. * DOMUtils.fadeIn(document.querySelector("a.xx"),2500,()=>{
  2753. * console.log("淡入完毕");
  2754. * })
  2755. * DOMUtils.fadeIn("a.xx",undefined,()=>{
  2756. * console.log("淡入完毕");
  2757. * })
  2758. */
  2759. fadeIn(element, duration = 400, callback) {
  2760. if (element == null) {
  2761. return;
  2762. }
  2763. let DOMUtilsContext = this;
  2764. if (typeof element === "string") {
  2765. element = DOMUtilsContext.selectorAll(element);
  2766. }
  2767. if (DOMUtilsCommonUtils.isNodeList(element)) {
  2768. // 设置
  2769. element.forEach(($ele) => {
  2770. DOMUtilsContext.fadeIn($ele, duration, callback);
  2771. });
  2772. return;
  2773. }
  2774. element.style.opacity = "0";
  2775. element.style.display = "";
  2776. let start = null;
  2777. let timer = null;
  2778. function step(timestamp) {
  2779. if (!start)
  2780. start = timestamp;
  2781. let progress = timestamp - start;
  2782. element = element;
  2783. element.style.opacity = Math.min(progress / duration, 1).toString();
  2784. if (progress < duration) {
  2785. DOMUtilsContext.windowApi.window.requestAnimationFrame(step);
  2786. }
  2787. else {
  2788. if (callback && typeof callback === "function") {
  2789. callback();
  2790. }
  2791. DOMUtilsContext.windowApi.window.cancelAnimationFrame(timer);
  2792. }
  2793. }
  2794. timer = DOMUtilsContext.windowApi.window.requestAnimationFrame(step);
  2795. }
  2796. /**
  2797. * 淡出元素
  2798. * @param element 当前元素
  2799. * @param duration 动画持续时间(毫秒),默认400毫秒
  2800. * @param callback 动画结束的回调
  2801. * @example
  2802. * // 元素a.xx淡出
  2803. * DOMUtils.fadeOut(document.querySelector("a.xx"),2500,()=>{
  2804. * console.log("淡出完毕");
  2805. * })
  2806. * DOMUtils.fadeOut("a.xx",undefined,()=>{
  2807. * console.log("淡出完毕");
  2808. * })
  2809. */
  2810. fadeOut(element, duration = 400, callback) {
  2811. let DOMUtilsContext = this;
  2812. if (element == null) {
  2813. return;
  2814. }
  2815. if (typeof element === "string") {
  2816. element = DOMUtilsContext.selectorAll(element);
  2817. }
  2818. if (DOMUtilsCommonUtils.isNodeList(element)) {
  2819. // 设置
  2820. element.forEach(($ele) => {
  2821. DOMUtilsContext.fadeOut($ele, duration, callback);
  2822. });
  2823. return;
  2824. }
  2825. element.style.opacity = "1";
  2826. let start = null;
  2827. let timer = null;
  2828. function step(timestamp) {
  2829. if (!start)
  2830. start = timestamp;
  2831. let progress = timestamp - start;
  2832. element = element;
  2833. element.style.opacity = Math.max(1 - progress / duration, 0).toString();
  2834. if (progress < duration) {
  2835. DOMUtilsContext.windowApi.window.requestAnimationFrame(step);
  2836. }
  2837. else {
  2838. element.style.display = "none";
  2839. if (typeof callback === "function") {
  2840. callback();
  2841. }
  2842. DOMUtilsContext.windowApi.window.cancelAnimationFrame(timer);
  2843. }
  2844. }
  2845. timer = DOMUtilsContext.windowApi.window.requestAnimationFrame(step);
  2846. }
  2847. /**
  2848. * 切换元素的显示和隐藏状态
  2849. * @param element 当前元素
  2850. * @param checkVisiblie 是否检测元素是否显示
  2851. * @example
  2852. * // 如果元素a.xx当前是隐藏,则显示,如果是显示,则隐藏
  2853. * DOMUtils.toggle(document.querySelector("a.xx"))
  2854. * DOMUtils.toggle("a.xx")
  2855. */
  2856. toggle(element, checkVisiblie) {
  2857. let DOMUtilsContext = this;
  2858. if (typeof element === "string") {
  2859. element = DOMUtilsContext.selectorAll(element);
  2860. }
  2861. if (element == null) {
  2862. return;
  2863. }
  2864. if (DOMUtilsCommonUtils.isNodeList(element)) {
  2865. // 设置
  2866. element.forEach(($ele) => {
  2867. DOMUtilsContext.toggle($ele);
  2868. });
  2869. return;
  2870. }
  2871. if (DOMUtilsContext.windowApi.globalThis
  2872. .getComputedStyle(element)
  2873. .getPropertyValue("display") === "none") {
  2874. DOMUtilsContext.show(element, checkVisiblie);
  2875. }
  2876. else {
  2877. DOMUtilsContext.hide(element, checkVisiblie);
  2878. }
  2879. }
  2880. /**
  2881. * 创建一个新的DOMUtils实例
  2882. * @param option
  2883. * @returns
  2884. */
  2885. createDOMUtils(option) {
  2886. return new DOMUtils(option);
  2887. }
  2888. /**
  2889. * 获取文字的位置信息
  2890. * @param $input 输入框
  2891. * @param selectionStart 起始位置
  2892. * @param selectionEnd 结束位置
  2893. * @example
  2894. * DOMUtils.getTextBoundingRect(document.querySelector("input"));
  2895. */
  2896. getTextBoundingRect($input, selectionStart, selectionEnd) {
  2897. let DOMUtilsContext = this;
  2898. // Basic parameter validation
  2899. if (!$input || !("value" in $input))
  2900. return $input;
  2901. if (selectionStart == null) {
  2902. selectionStart = $input.selectionStart || 0;
  2903. }
  2904. if (selectionEnd == null) {
  2905. selectionEnd = $input.selectionEnd || 0;
  2906. }
  2907. if (typeof selectionStart == "string")
  2908. selectionStart = parseFloat(selectionStart);
  2909. if (typeof selectionStart != "number" || isNaN(selectionStart)) {
  2910. selectionStart = 0;
  2911. }
  2912. if (selectionStart < 0)
  2913. selectionStart = 0;
  2914. else
  2915. selectionStart = Math.min($input.value.length, selectionStart);
  2916. if (typeof selectionEnd == "string")
  2917. selectionEnd = parseFloat(selectionEnd);
  2918. if (typeof selectionEnd != "number" ||
  2919. isNaN(selectionEnd) ||
  2920. selectionEnd < selectionStart) {
  2921. selectionEnd = selectionStart;
  2922. }
  2923. if (selectionEnd < 0)
  2924. selectionEnd = 0;
  2925. else
  2926. selectionEnd = Math.min($input.value.length, selectionEnd);
  2927. // If available (thus IE), use the createTextRange method
  2928. // @ts-ignore
  2929. if (typeof $input.createTextRange == "function") {
  2930. let range = $input.createTextRange();
  2931. range.collapse(true);
  2932. range.moveStart("character", selectionStart);
  2933. range.moveEnd("character", selectionEnd - selectionStart);
  2934. return range.getBoundingClientRect();
  2935. }
  2936. // createTextRange is not supported, create a fake text range
  2937. let offset = getInputOffset(), topPos = offset.top, leftPos = offset.left, width = getInputCSS("width", true), height = getInputCSS("height", true);
  2938. // Styles to simulate a node in an input field
  2939. let cssDefaultStyles = "white-space:pre;padding:0;margin:0;", listOfModifiers = [
  2940. "direction",
  2941. "font-family",
  2942. "font-size",
  2943. "font-size-adjust",
  2944. "font-variant",
  2945. "font-weight",
  2946. "font-style",
  2947. "letter-spacing",
  2948. "line-height",
  2949. "text-align",
  2950. "text-indent",
  2951. "text-transform",
  2952. "word-wrap",
  2953. "word-spacing",
  2954. ];
  2955. topPos += getInputCSS("padding-top", true);
  2956. topPos += getInputCSS("border-top-width", true);
  2957. leftPos += getInputCSS("padding-left", true);
  2958. leftPos += getInputCSS("border-left-width", true);
  2959. leftPos += 1; //Seems to be necessary
  2960. for (let index = 0; index < listOfModifiers.length; index++) {
  2961. let property = listOfModifiers[index];
  2962. // @ts-ignore
  2963. cssDefaultStyles += property + ":" + getInputCSS(property) + ";";
  2964. }
  2965. // End of CSS variable checks
  2966. // 不能为空,不然获取不到高度
  2967. let text = $input.value || "G", textLen = text.length, fakeClone = DOMUtilsContext.windowApi.document.createElement("div");
  2968. if (selectionStart > 0)
  2969. appendPart(0, selectionStart);
  2970. var fakeRange = appendPart(selectionStart, selectionEnd);
  2971. if (textLen > selectionEnd)
  2972. appendPart(selectionEnd, textLen);
  2973. // Styles to inherit the font styles of the element
  2974. fakeClone.style.cssText = cssDefaultStyles;
  2975. // Styles to position the text node at the desired position
  2976. fakeClone.style.position = "absolute";
  2977. fakeClone.style.top = topPos + "px";
  2978. fakeClone.style.left = leftPos + "px";
  2979. fakeClone.style.width = width + "px";
  2980. fakeClone.style.height = height + "px";
  2981. DOMUtilsContext.windowApi.document.body.appendChild(fakeClone);
  2982. var returnValue = fakeRange.getBoundingClientRect(); //Get rect
  2983. fakeClone?.parentNode?.removeChild(fakeClone); //Remove temp
  2984. return returnValue;
  2985. // Local functions for readability of the previous code
  2986. /**
  2987. *
  2988. * @param start
  2989. * @param end
  2990. * @returns
  2991. */
  2992. function appendPart(start, end) {
  2993. var span = DOMUtilsContext.windowApi.document.createElement("span");
  2994. span.style.cssText = cssDefaultStyles; //Force styles to prevent unexpected results
  2995. span.textContent = text.substring(start, end);
  2996. fakeClone.appendChild(span);
  2997. return span;
  2998. }
  2999. // Computing offset position
  3000. function getInputOffset() {
  3001. let body = DOMUtilsContext.windowApi.document.body, win = DOMUtilsContext.windowApi.document.defaultView, docElem = DOMUtilsContext.windowApi.document.documentElement, $box = DOMUtilsContext.windowApi.document.createElement("div");
  3002. $box.style.paddingLeft = $box.style.width = "1px";
  3003. body.appendChild($box);
  3004. var isBoxModel = $box.offsetWidth == 2;
  3005. body.removeChild($box);
  3006. let $boxRect = $input.getBoundingClientRect();
  3007. var clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0, scrollTop = win.pageYOffset ||
  3008. (isBoxModel && docElem.scrollTop) ||
  3009. body.scrollTop, scrollLeft = win.pageXOffset ||
  3010. (isBoxModel && docElem.scrollLeft) ||
  3011. body.scrollLeft;
  3012. return {
  3013. top: $boxRect.top + scrollTop - clientTop,
  3014. left: $boxRect.left + scrollLeft - clientLeft,
  3015. };
  3016. }
  3017. /**
  3018. *
  3019. * @param prop
  3020. * @param isNumber
  3021. * @returns
  3022. */
  3023. function getInputCSS(prop, isNumber) {
  3024. var val = DOMUtilsContext.windowApi.document
  3025. .defaultView.getComputedStyle($input, null)
  3026. .getPropertyValue(prop);
  3027. return isNumber ? parseFloat(val) : val;
  3028. }
  3029. }
  3030. }
  3031. let domUtils = new DOMUtils();
  3032.  
  3033. return domUtils;
  3034.  
  3035. }));
  3036. //# sourceMappingURL=index.umd.js.map

QingJ © 2025

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