dom util

dom manipulation util

此腳本不應該直接安裝,它是一個供其他腳本使用的函式庫。欲使用本函式庫,請在腳本 metadata 寫上: // @require https://update.gf.qytechs.cn/scripts/499616/1635544/dom%20util.js

  1. const HtmlSanitizer = {
  2. tempElement: document.createElement("div"),
  3. sanitize: function (/** @type {string} */ htmlString) {
  4. this.tempElement.innerText = htmlString;
  5. return this.tempElement.innerHTML;
  6. },
  7. };
  8.  
  9. // Feature detection for Trusted Types
  10. let trustedHTMLPolicy;
  11. if (window.trustedTypes && window.trustedTypes.createPolicy) {
  12. trustedHTMLPolicy = window.trustedTypes.createPolicy('mmHtmlPolicy', {
  13. createHTML: (input) => input // Add sanitization logic here if necessary
  14. });
  15. }
  16.  
  17. class HtmlString extends String {
  18. /**@type {HTMLElement|null} */
  19. element = null;
  20.  
  21. /**@param {string} value */
  22. constructor(value) {
  23. super(value);
  24. }
  25.  
  26. /**@returns {HTMLElement} */
  27. asElement() {
  28. if (this.element !== null) {
  29. return this.element;
  30. }
  31.  
  32. const temp = document.createElement("div");
  33. // Use Trusted Types if available, otherwise fall back to direct assignment
  34. if (trustedHTMLPolicy) {
  35. temp.innerHTML = trustedHTMLPolicy.createHTML(this.valueOf());
  36. } else {
  37. temp.innerHTML = this.valueOf();
  38. }
  39. if (temp.childElementCount > 1) {
  40. throw new Error("html template does not accept more than 1 element");
  41. }
  42.  
  43. this.element = /**@type {HTMLElement} */ (temp.firstElementChild);
  44. return /**@type {HTMLElement} */ (this.element);
  45. }
  46. }
  47.  
  48. /**
  49. * @param {string} selector
  50. * @param {HTMLElement|Document} rootElement
  51. * @returns {HTMLElement|null}
  52. */
  53. function $findElm(selector, rootElement = document) {
  54. return /**@type {HTMLElement|null} */ (rootElement.querySelector(selector));
  55. }
  56.  
  57. /**
  58. * @param {string} selector
  59. * @param {HTMLElement|Document} rootElement
  60. * @returns {HTMLElement}
  61. */
  62. function $findElmStrictly(selector, rootElement = document) {
  63. const element = /**@type {HTMLElement|null} */ (rootElement.querySelector(selector));
  64. if (element === null) {
  65. throw new Error(`Element with selector '${selector}' not found`);
  66. }
  67.  
  68. return element;
  69. }
  70.  
  71. /**
  72. * @param {string} selector
  73. * @returns {NodeListOf<HTMLElement>}
  74. */
  75. function $findAll(selector) {
  76. return /**@type {NodeListOf<HTMLElement>} */ (document.querySelectorAll(selector));
  77. }
  78.  
  79. /**@typedef {string|HtmlString|number|boolean} TInterpolatedValue */
  80.  
  81. /**
  82. * safe html interpolation
  83. * @param {TemplateStringsArray} literalValues
  84. * @param {TInterpolatedValue[]|TInterpolatedValue[][]} interpolatedValues
  85. * @returns {HtmlString}
  86. */
  87. function html(literalValues, ...interpolatedValues) {
  88. let result = "";
  89.  
  90. interpolatedValues.forEach((currentInterpolatedVal, idx) => {
  91. let literalVal = literalValues[idx];
  92. let interpolatedVal = "";
  93. if (Array.isArray(currentInterpolatedVal)) {
  94. interpolatedVal = currentInterpolatedVal.join("\n");
  95. } else if (typeof currentInterpolatedVal !== "boolean") {
  96. interpolatedVal = currentInterpolatedVal.toString();
  97. }
  98.  
  99. const isSanitize = !literalVal.endsWith("$");
  100. if (isSanitize) {
  101. result += literalVal;
  102. result += HtmlSanitizer.sanitize(interpolatedVal);
  103. } else {
  104. literalVal = literalVal.slice(0, -1);
  105.  
  106. result += literalVal;
  107. result += interpolatedVal;
  108. }
  109. });
  110.  
  111. result += literalValues.slice(-1);
  112. return new HtmlString(result);
  113. }
  114.  
  115. /**
  116. * wait for element to be added to the DOM
  117. * @param {string} selector
  118. * @param {number} timeout
  119. * @param {(element: HTMLElement) => void} callback
  120. * @returns {CallableFunction | null} callback to stop observing
  121. */
  122. function waitForElement(selector, timeout, callback) {
  123. let matchingElement = /**@type {HTMLElement|null} */ (document.querySelector(selector));
  124. if (matchingElement) {
  125. callback(matchingElement);
  126. return null;
  127. }
  128.  
  129. const observer = new MutationObserver((mutations) => {
  130. for (let mutation of mutations) {
  131. if (!mutation.addedNodes) continue;
  132.  
  133. for (let node of mutation.addedNodes) {
  134. if (node.matches && node.matches(selector)) {
  135. callback(node);
  136. observer.disconnect();
  137. clearTimeout(timeoutId);
  138. return;
  139. }
  140. if (node.querySelector) {
  141. matchingElement = /**@type {HTMLElement|null} */ (node.querySelector(selector));
  142. if (matchingElement !== null) {
  143. callback(matchingElement);
  144. observer.disconnect();
  145. clearTimeout(timeoutId);
  146. return;
  147. }
  148. }
  149. }
  150. }
  151. });
  152.  
  153. observer.observe(document.documentElement, {
  154. childList: true,
  155. subtree: true,
  156. attributes: false,
  157. characterData: false,
  158. });
  159.  
  160. const timeoutId = setTimeout(() => {
  161. observer.disconnect();
  162. console.log(`Timeout reached: Element "${selector}" not found`);
  163. }, timeout);
  164.  
  165. return () => {
  166. observer.disconnect();
  167. };
  168. }
  169.  
  170. /**
  171. * Smoothly scrolls the page to the given element with an optional offset above it.
  172. *
  173. * @param {HTMLElement} el - The target element to scroll to.
  174. * @param {number} [offset=20] - The number of pixels to offset above the element.
  175. * @returns {void}
  176. */
  177. function scrollToElementWithOffset(el, offset = 20) {
  178. const rect = el.getBoundingClientRect();
  179. const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
  180. const targetY = rect.top + scrollTop - offset;
  181.  
  182. window.scrollTo({
  183. top: targetY,
  184. behavior: 'smooth'
  185. });
  186. }
  187.  
  188. /**
  189. * Appends a <style> tag with class 'mm-styles' and the given CSS rules to the document head.
  190. *
  191. * @param {string} cssText - The CSS rules to insert into the style tag.
  192. * @returns {HTMLStyleElement} The created style element.
  193. */
  194. function addStyles(cssText) {
  195. const styleEl = document.createElement('style');
  196. styleEl.className = 'mm-styles';
  197. styleEl.textContent = cssText;
  198. document.head.appendChild(styleEl);
  199. return styleEl;
  200. }

QingJ © 2025

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