ElementGetter2.0

async requre element

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

  1. // ==UserScript==
  2. // @name ElementGetter
  3. // @author cxxjackie
  4. // @version 2.0.1
  5. // @supportURL https://bbs.tampermonkey.net.cn/thread-2726-1-1.html
  6. // ==/UserScript==
  7.  
  8. var elmGetter = function() {
  9. const win = window.unsafeWindow || document.defaultView || window;
  10. const doc = win.document;
  11. const listeners = new WeakMap();
  12. let mode = 'css';
  13. let $;
  14. const elProto = win.Element.prototype;
  15. const matches = elProto.matches || elProto.matchesSelector || elProto.webkitMatchesSelector ||
  16. elProto.mozMatchesSelector || elProto.oMatchesSelector;
  17. const MutationObs = win.MutationObserver || win.WebkitMutationObserver || win.MozMutationObserver;
  18. function addObserver(target, callback) {
  19. const observer = new MutationObs(mutations => {
  20. for (const mutation of mutations) {
  21. if (mutation.type === 'attributes') {
  22. callback(mutation.target, 'attr');
  23. if (observer.canceled) return;
  24. }
  25. for (const node of mutation.addedNodes) {
  26. if (node instanceof Element) callback(node, 'insert');
  27. if (observer.canceled) return;
  28. }
  29. }
  30. });
  31. observer.canceled = false;
  32. observer.observe(target, {childList: true, subtree: true, attributes: true, attributeOldValue: true});
  33. return () => {
  34. observer.canceled = true;
  35. observer.disconnect();
  36. };
  37. }
  38. function addFilter(target, filter) {
  39. let listener = listeners.get(target);
  40. if (!listener) {
  41. listener = {
  42. filters: new Set(),
  43. remove: addObserver(target, (el, reason) => listener.filters.forEach(f => f(el, reason)))
  44. };
  45. listeners.set(target, listener);
  46. }
  47. listener.filters.add(filter);
  48. }
  49. function removeFilter(target, filter) {
  50. const listener = listeners.get(target);
  51. if (!listener) return;
  52. listener.filters.delete(filter);
  53. if (!listener.filters.size) {
  54. listener.remove();
  55. listeners.delete(target);
  56. }
  57. }
  58. function query(selector, parent, root, curMode, reason) {
  59. switch (curMode) {
  60. case 'css': {
  61. if (reason === 'attr') return matches.call(parent, selector) ? parent : null;
  62. const checkParent = parent !== root && matches.call(parent, selector);
  63. return checkParent ? parent : parent.querySelector(selector);
  64. }
  65. case 'jquery': {
  66. if (reason === 'attr') return $(parent).is(selector) ? $(parent) : null;
  67. const jNodes = $(parent !== root ? parent : []).add([...parent.querySelectorAll('*')]).filter(selector);
  68. return jNodes.length ? $(jNodes.get(0)) : null;
  69. }
  70. case 'xpath': {
  71. const ownerDoc = parent.ownerDocument || parent;
  72. selector += '/self::*';
  73. return ownerDoc.evaluate(selector, reason === 'attr' ? root : parent, null, 9, null).singleNodeValue;
  74. }
  75. }
  76. }
  77. function queryAll(selector, parent, root, curMode, reason) {
  78. switch (curMode) {
  79. case 'css': {
  80. if (reason === 'attr') return matches.call(parent, selector) ? [parent] : [];
  81. const checkParent = parent !== root && matches.call(parent, selector);
  82. const result = parent.querySelectorAll(selector);
  83. return checkParent ? [parent, ...result] : [...result];
  84. }
  85. case 'jquery': {
  86. if (reason === 'attr') return $(parent).is(selector) ? [$(parent)] : [];
  87. const jNodes = $(parent !== root ? parent : []).add([...parent.querySelectorAll('*')]).filter(selector);
  88. return $.map(jNodes, el => $(el));
  89. }
  90. case 'xpath': {
  91. const ownerDoc = parent.ownerDocument || parent;
  92. selector += '/self::*';
  93. const xPathResult = ownerDoc.evaluate(selector, reason === 'attr' ? root : parent, null, 7, null);
  94. const result = [];
  95. for (let i = 0; i < xPathResult.snapshotLength; i++) {
  96. result.push(xPathResult.snapshotItem(i));
  97. }
  98. return result;
  99. }
  100. }
  101. }
  102. function isJquery(jq) {
  103. return jq && jq.fn && typeof jq.fn.jquery === 'string';
  104. }
  105. function getOne(selector, parent, timeout) {
  106. const curMode = mode;
  107. return new Promise(resolve => {
  108. const node = query(selector, parent, parent, curMode);
  109. if (node) return resolve(node);
  110. let timer;
  111. const filter = (el, reason) => {
  112. const node = query(selector, el, parent, curMode, reason);
  113. if (node) {
  114. removeFilter(parent, filter);
  115. timer && clearTimeout(timer);
  116. resolve(node);
  117. }
  118. };
  119. addFilter(parent, filter);
  120. if (timeout > 0) {
  121. timer = setTimeout(() => {
  122. removeFilter(parent, filter);
  123. resolve(null);
  124. }, timeout);
  125. }
  126. });
  127. }
  128. return {
  129. get currentSelector() {
  130. return mode;
  131. },
  132. get(selector, ...args) {
  133. let parent = typeof args[0] !== 'number' && args.shift() || doc;
  134. if (mode === 'jquery' && parent instanceof $) parent = parent.get(0);
  135. const timeout = args[0] || 0;
  136. if (Array.isArray(selector)) {
  137. return Promise.all(selector.map(s => getOne(s, parent, timeout)));
  138. }
  139. return getOne(selector, parent, timeout);
  140. },
  141. each(selector, ...args) {
  142. let parent = typeof args[0] !== 'function' && args.shift() || doc;
  143. if (mode === 'jquery' && parent instanceof $) parent = parent.get(0);
  144. const callback = args[0];
  145. const curMode = mode;
  146. const refs = new WeakSet();
  147. for (const node of queryAll(selector, parent, parent, curMode)) {
  148. refs.add(curMode === 'jquery' ? node.get(0) : node);
  149. if (callback(node, false) === false) return;
  150. }
  151. const filter = (el, reason) => {
  152. for (const node of queryAll(selector, el, parent, curMode, reason)) {
  153. const _el = curMode === 'jquery' ? node.get(0) : node;
  154. if (refs.has(_el)) break;
  155. refs.add(_el);
  156. if (callback(node, true) === false) {
  157. return removeFilter(parent, filter);
  158. }
  159. }
  160. };
  161. addFilter(parent, filter);
  162. },
  163. create(domString, ...args) {
  164. const returnList = typeof args[0] === 'boolean' && args.shift();
  165. const parent = args[0];
  166. const template = doc.createElement('template');
  167. template.innerHTML = domString;
  168. const node = template.content.firstElementChild;
  169. if (!node) return null;
  170. parent ? parent.appendChild(node) : node.remove();
  171. if (returnList) {
  172. const list = {};
  173. node.querySelectorAll('[id]').forEach(el => list[el.id] = el);
  174. list[0] = node;
  175. return list;
  176. }
  177. return node;
  178. },
  179. selector(desc) {
  180. switch (true) {
  181. case isJquery(desc):
  182. $ = desc;
  183. return mode = 'jquery';
  184. case !desc || typeof desc.toLowerCase !== 'function':
  185. return mode = 'css';
  186. case desc.toLowerCase() === 'jquery':
  187. for (const jq of [window.jQuery, window.$, win.jQuery, win.$]) {
  188. if (isJquery(jq)) {
  189. $ = jq;
  190. break;
  191. }
  192. }
  193. return mode = $ ? 'jquery' : 'css';
  194. case desc.toLowerCase() === 'xpath':
  195. return mode = 'xpath';
  196. default:
  197. return mode = 'css';
  198. }
  199. }
  200. };
  201. }();

QingJ © 2025

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