jQuery-like SPA operation library

SPA like vue is supported。offers functions like click element after loaded, call function after url change。

目前为 2022-02-20 提交的版本,查看 最新版本

此脚本不应直接安装,它是供其他脚本使用的外部库。如果你需要使用该库,请在脚本元属性加入:// @require https://update.cn-greasyfork.org/scripts/440334/1020489/jQuery-like%20SPA%20operation%20library.js

  1. // ==UserScript==
  2. // @name jQuery-like SPA operation library
  3. // @description SPA like vue is supported。offers functions like click element after loaded, call function after url change。
  4. // @author yechenyin
  5. // @licence MIT
  6. // @version 1.0
  7. // ==/UserScript==
  8.  
  9. //闭包里的方法的用法和jquery同命名方法一致
  10. (function () {
  11. let jQuery = function (selector) {
  12. return new jQuery.fn.init(selector)
  13. }
  14. jQuery.fn = jQuery.prototype = {
  15. length: 0,
  16. selector: '',
  17. init: function (elementOrSelector) {
  18. let nodes = []
  19. if (typeof (elementOrSelector) == 'string') {
  20. this.selector = elementOrSelector
  21. nodes = document.querySelectorAll(this.selector)
  22. } else if (elementOrSelector instanceof NodeList) {
  23. nodes = elementOrSelector
  24. } else if (elementOrSelector instanceof Element) {
  25. nodes = [elementOrSelector]
  26. }
  27. this.length = nodes.length
  28. for (let i = 0; i < nodes.length; i++) {
  29. this[i] = nodes[i]
  30. }
  31. },
  32. each: function (callback) {
  33. for (let i = 0; i < this.length; i++) {
  34. callback.call(this[i])
  35. }
  36. },
  37. on: function (event, callback) {
  38. this.each(function () {
  39. this.addEventListener(event, callback)
  40. })
  41. },
  42. text: function (string) {
  43. let i = 0
  44. if (string !== undefined) {
  45. for (i = 0; i < this.length; i++) {
  46. this[i].innerText = string
  47. }
  48. } else {
  49. return this[0].innerText
  50. }
  51. },
  52. val: function (value) {
  53. if (value === undefined) {
  54. let ret
  55. if (this[0].type == 'checkbox')
  56. ret = this[0].checked
  57. else if (this[0].type == 'radio' || this[0].type == 'text' || this[0].tagName == 'select')
  58. ret = this[0].value
  59. return ret
  60. } else {
  61. for (let i = 0; i < this.length; i++) {
  62. if (this[i].type == 'checkbox' && Boolean(value))
  63. this[i].click()
  64. else if (this[i].type == 'radio')
  65. this[i].checked = this[i].value == value
  66. else if (this[i].type == 'text')
  67. this[i].value = value
  68. else if (this[i].tagName == 'select')
  69. this[i].value = value
  70. this[i].dispatchEvent(new Event('input', { bubbles: true }))
  71. }
  72. }
  73. },
  74. attr: function (attribute, value) {
  75. if (value === undefined) {
  76. return this[0].getAttribute(attribute)
  77. } else {
  78. this.each(function () {
  79. this.setAttribute(attribute, value)
  80. })
  81. }
  82. },
  83. click: function () {
  84. this[0].click()
  85. },
  86. find: function (selector) {
  87. let j = 0
  88. let result = []
  89. for (let i = 0; i < this.length; i++) {
  90. if (this[i].querySelectorAll(selector).length) {
  91. }
  92. }
  93. },
  94. append: function (html) {
  95. for (let i = 0; i < this.length; i++) {
  96. this[i].innerHTML += html
  97. }
  98. },
  99. }
  100.  
  101. jQuery.fn.init.prototype = jQuery.fn
  102. if (!window.jQuery) window.jQuery = window.$ = jQuery
  103. })()
  104.  
  105. //每当符合选择器规则的元素插入到页面中时,唤起callback方法。如果trigger_once为true,只唤起一次
  106. jQuery.fn.inserted = function (callback, trigger_once = false) {
  107. let selector = this.selector;
  108. if ($(selector).length > 0) {
  109. console.log($(selector).length + ' ' + selector + " is loaded at begin");
  110. callback.call($(selector));
  111. }
  112. let finished = false
  113. let recallback = function (mutationsList, observer) {
  114. for (let i = 0; i < mutationsList.length; i++) {
  115. //console.log(mutationsList[i].target)
  116. if (mutationsList[i].addedNodes) {
  117. for (let j = 0; j < mutationsList[i].addedNodes.length; j++) {
  118. let element = mutationsList[i].addedNodes[j]
  119. if (!(trigger_once && finished) && element instanceof Element && element.querySelectorAll(selector).length) {
  120. if (element.id)
  121. console.log('#' + element.id + ' which contains ' + selector + ' is loaded')
  122. else if (element.className)
  123. console.log('.' + element.className + ' which contains ' + selector + ' is loaded')
  124. else
  125. console.log(element.outerHtml + ' which contains ' + selector + ' is loaded')
  126. if (trigger_once) {
  127. observer.disconnect()
  128. finished = true
  129. }
  130. callback.call($(element.querySelectorAll(selector)))
  131. }
  132. }
  133. }
  134. }
  135. };
  136. let MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver
  137. if (MutationObserver) {
  138. let observer = new MutationObserver(recallback)
  139. observer.observe(document.body, {
  140. childList: true,
  141. subtree: true
  142. })
  143. }
  144. }
  145.  
  146. //当符合选择器规则的元素插入到页面中时,调用一次callback方法,之后不会在唤起
  147. jQuery.fn.loaded = function (callback) {
  148. if (callback)
  149. this.inserted(callback, true);
  150. }
  151.  
  152. //在元素加载完成后经过delay(单位毫秒)延迟后点击元素
  153. jQuery.fn.clickAfterLoaded = function (delay = 0) {
  154. this.loaded(function () {
  155. setTimeout(function () {
  156. this[0].click()
  157. }.bind(this), delay)
  158. })
  159. }
  160. //在元素加载完成后经过delay(单位毫秒)延迟后设置输入值
  161. jQuery.fn.setAfterLoaded = function (value, delay = 0) {
  162. this.loaded(function () {
  163. setTimeout(function () {
  164. this.val(value)
  165. }.bind(this), delay)
  166. })
  167. }
  168.  
  169. //替代得到http成功回应后的回调
  170. $.replaceResponseCallback = function (callback, continueOriginalCallback = false) {
  171. let open = XMLHttpRequest.prototype.open
  172. XMLHttpRequest.prototype.open = function () {
  173. this.addEventListener(
  174. 'readystatechange',
  175. function () {
  176. if (
  177. this.readyState == 4 &&
  178. this.response
  179. ) {
  180. callback.call(this)
  181. }
  182. },
  183. false
  184. )
  185. if (continueOriginalCallback)
  186. open.apply(this, arguments)
  187. XMLHttpRequest.prototype.open = open
  188. }
  189. }
  190. //url改变后唤起callback,支持监测SPA的#后面的字符串变化
  191. $.onurlchange = function (callback) {
  192. history.pushState = ((f) =>
  193. function pushState() {
  194. let ret = f.apply(this, arguments)
  195. window.dispatchEvent(new Event('pushstate'))
  196. window.dispatchEvent(new Event('urlchange'))
  197. return ret
  198. })(history.pushState)
  199.  
  200. history.replaceState = ((f) =>
  201. function replaceState() {
  202. let ret = f.apply(this, arguments)
  203. window.dispatchEvent(new Event('replacestate'))
  204. window.dispatchEvent(new Event('urlchange'))
  205. return ret
  206. })(history.replaceState)
  207.  
  208. window.addEventListener('popstate', () => {
  209. window.dispatchEvent(new Event('urlchange'))
  210. })
  211. window.addEventListener('urlchange', function () {
  212. callback()
  213. })
  214. }