UserscriptAPIDom

https://gitee.com/liangjiancang/userscript/tree/master/lib/UserscriptAPI

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

  1. /**
  2. * UserscriptAPIDom
  3. *
  4. * 依赖于 `UserscriptAPI`。
  5. * @version 1.2.4.20230314
  6. * @author Laster2800
  7. * @see {@link https://gitee.com/liangjiancang/userscript/tree/master/lib/UserscriptAPI UserscriptAPI}
  8. */
  9. class UserscriptAPIDom {
  10. /**
  11. * @param {UserscriptAPI} api `UserscriptAPI`
  12. */
  13. constructor(api) {
  14. this.api = api
  15. }
  16.  
  17. /**
  18. * 查找符合条件的祖先元素
  19. * @param {HTMLElement} target 目标元素
  20. * @param {(el: HTMLElement) => boolean} condition 条件
  21. * @param {boolean} [positioned] 以 `offsetParent` 而非 `parentElement` 上溯
  22. * @returns {HTMLElement} 符合条件的祖先元素
  23. */
  24. findAncestor(target, condition, positioned) {
  25. const p = positioned ? 'offsetParent' : 'parentElement'
  26. let element = target[p]
  27. while (element && !condition(element)) {
  28. element = element[p]
  29. }
  30. return element
  31. }
  32.  
  33. /**
  34. * 设定元素位置,默认设定为绝对居中
  35. *
  36. * 要求该元素此时可见且尺寸为确定值(一般要求为块状元素)。
  37. * @param {HTMLElement} target 目标元素
  38. * @param {Object} [options] 选项
  39. * @param {string} [options.position='fixed'] 定位方式
  40. * @param {string} [options.top='50%'] `style.top`
  41. * @param {string} [options.left='50%'] `style.left`
  42. */
  43. setPosition(target, options) {
  44. options = {
  45. position: 'fixed',
  46. top: '50%',
  47. left: '50%',
  48. ...options,
  49. }
  50. target.style.position = options.position
  51. const style = window.getComputedStyle(target)
  52. const top = (Number.parseFloat(style.height) + Number.parseFloat(style.paddingTop) + Number.parseFloat(style.paddingBottom)) / 2
  53. const left = (Number.parseFloat(style.width) + Number.parseFloat(style.paddingLeft) + Number.parseFloat(style.paddingRight)) / 2
  54. target.style.top = `calc(${options.top} - ${top}px)`
  55. target.style.left = `calc(${options.left} - ${left}px)`
  56. }
  57.  
  58. /**
  59. * @typedef FadeTargetElement
  60. * @property {string} [fadeInDisplay] 渐显开始后的 `display` 样式。若没有设定:
  61. * * 若当前 `display` 与 `fadeOutDisplay` 不同,默认值为当前 `display`。
  62. * * 若当前 `display` 与 `fadeOutDisplay` 相同,默认值为 `block`。
  63. * @property {string} [fadeOutDisplay='none'] 渐隐开始后的 `display` 样式
  64. * @property {number} [fadeInTime] 渐显时间;缺省时,元素的 `transition-duration` 必须与 `api.options.fadeTime` 一致
  65. * @property {number} [fadeOutTime] 渐隐时间;缺省时,元素的 `transition-duration` 必须与 `api.options.fadeTime` 一致
  66. * @property {string} [fadeInFunction='ease-in-out'] 渐显效果,应为符合 `transition-timing-function` 的有效值
  67. * @property {string} [fadeOutFunction='ease-in-out'] 渐隐效果,应为符合 `transition-timing-function` 的有效值
  68. * @property {boolean} [fadeInNoInteractive] 渐显期间是否禁止交互
  69. * @property {boolean} [fadeOutNoInteractive] 渐隐期间是否禁止交互
  70. */
  71. /**
  72. * 处理 HTML 元素的渐显和渐隐
  73. * @param {boolean} inOut 渐显/渐隐
  74. * @param {HTMLElement & FadeTargetElement} target HTML 元素
  75. * @param {() => void} [callback] 渐显/渐隐完成的回调函数
  76. */
  77. fade(inOut, target, callback) {
  78. const { api } = this
  79. let transitionChanged = false
  80. const fadeId = Date.now() // 等同于当前时间戳,其意义在于保证对于同一元素,后执行的操作必将覆盖前的操作
  81. const fadeOutDisplay = target.fadeOutDisplay ?? 'none'
  82. target._fadeId = fadeId
  83. if (inOut) { // 渐显
  84. let displayChanged = false
  85. if (typeof target.fadeInTime === 'number' || target.fadeInFunction) {
  86. target.style.transition = `opacity ${target.fadeInTime ?? api.options.fadeTime}ms ${target.fadeInFunction ?? 'ease-in-out'}`
  87. transitionChanged = true
  88. }
  89. if (target.fadeInNoInteractive) {
  90. target.style.pointerEvents = 'none'
  91. }
  92. const originalDisplay = window.getComputedStyle(target).display
  93. let { fadeInDisplay } = target
  94. if (!fadeInDisplay) {
  95. fadeInDisplay = (originalDisplay !== fadeOutDisplay) ? originalDisplay : 'block'
  96. }
  97. if (originalDisplay !== fadeInDisplay) {
  98. target.style.display = fadeInDisplay
  99. displayChanged = true
  100. }
  101. setTimeout(() => {
  102. let success = false
  103. if (target._fadeId <= fadeId) {
  104. target.style.opacity = '1'
  105. success = true
  106. }
  107. setTimeout(() => {
  108. callback?.(success)
  109. if (target._fadeId <= fadeId) {
  110. if (transitionChanged) {
  111. target.style.transition = ''
  112. }
  113. if (target.fadeInNoInteractive) {
  114. target.style.pointerEvents = ''
  115. }
  116. }
  117. }, target.fadeInTime ?? api.options.fadeTime)
  118. }, displayChanged ? 10 : 0) // 此处的 10ms 是为了保证修改 display 后在浏览器上真正生效;按 HTML5 定义,浏览器需保证 display 在修改后 4ms 内生效,但实际上大部分浏览器貌似做不到,等个 10ms 再修改 opacity
  119. } else { // 渐隐
  120. if (typeof target.fadeOutTime === 'number' || target.fadeOutFunction) {
  121. target.style.transition = `opacity ${target.fadeOutTime ?? api.options.fadeTime}ms ${target.fadeOutFunction ?? 'ease-in-out'}`
  122. transitionChanged = true
  123. }
  124. if (target.fadeOutNoInteractive) {
  125. target.style.pointerEvents = 'none'
  126. }
  127. target.style.opacity = '0'
  128. setTimeout(() => {
  129. let success = false
  130. if (target._fadeId <= fadeId) {
  131. target.style.display = fadeOutDisplay
  132. success = true
  133. }
  134. callback?.(success)
  135. if (success) {
  136. if (transitionChanged) {
  137. target.style.transition = ''
  138. }
  139. if (target.fadeOutNoInteractive) {
  140. target.style.pointerEvents = ''
  141. }
  142. }
  143. }, target.fadeOutTime ?? api.options.fadeTime)
  144. }
  145. }
  146. }
  147.  
  148. /* global UserscriptAPI */
  149. // eslint-disable-next-line no-lone-blocks
  150. { UserscriptAPI.registerModule('dom', UserscriptAPIDom) }

QingJ © 2025

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