jQuery Hook

用于定位jquery添加的各种事件

当前为 2021-11-15 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name jQuery Hook
  3. // @namespace https://github.com/CC11001100/crawler-js-hook-framework-public/tree/master/020-jQuery-hook
  4. // @version 0.1
  5. // @description 用于定位jquery添加的各种事件
  6. // @document https://github.com/CC11001100/crawler-js-hook-framework-public/blob/master/020-jQuery-hook/README.md
  7. // @author CC11001100
  8. // @match *://*/*
  9. // @run-at document-start
  10. // @grant none
  11. // ==/UserScript==
  12. (() => {
  13.  
  14. const globalUniqPrefix = "cc11001100";
  15.  
  16. // 在第一次设置jquery的时候添加Hook
  17. Object.defineProperty(window, "$", {
  18. set: $ => {
  19.  
  20. // 为jquery的各种方法添加Hook
  21. try {
  22. addHook($);
  23. } catch (e) {
  24. console.error("为jQuery添加Hook时报错: " + e)
  25. }
  26.  
  27. // 删除set描述符拦截,恢复正常赋值
  28. delete window["$"];
  29. window["$"] = $;
  30. },
  31. configurable: true
  32. });
  33.  
  34. /**
  35. * 为jquery添加一些hook,等会儿使用jquery为dom元素绑定事件的话就会被捕获到
  36. * @param $
  37. */
  38. function addHook($) {
  39.  
  40. if (!$["fn"]) {
  41. console.log("当前页面虽然声明了$变量,但并不是jQuery,因此忽略。");
  42. return;
  43. }
  44.  
  45. // 一些比较通用的事件的拦截
  46. const eventNameList = [
  47. "click", "dblclick", "blur", "change", "contextmenu", "error", "focus",
  48. "focusin", "focusout", "hover", "holdReady", "proxy", "ready", "keydown", "keypress",
  49. "keyup", "live", "load", "mousedown", "mouseenter", "mouseleave", "mousemove", "mouseout",
  50. "mouseover", "mouseup"
  51. ];
  52. for (let eventName of eventNameList) {
  53. const old = $.fn[eventName];
  54. $.fn[eventName] = function () {
  55. try {
  56. setEventFunctionNameToDomObjectAttribute(this, eventName, arguments[0]);
  57. } catch (e) {
  58. console.error(`为jQuery添加${eventName}类型的事件的Hook时发生错误: ${e}`);
  59. }
  60. return old.apply(this, arguments);
  61. }
  62. }
  63.  
  64. // on,不仅是内置事件类型,还有可能有一些自定义的事件类型
  65. // https://api.jquery.com/on/
  66. const fnOnHolder = $.fn.on;
  67. $.fn.on = function () {
  68. try {
  69. const eventName = arguments[0];
  70. let eventFunction = undefined;
  71. for (let x of arguments) {
  72. if (x instanceof Function) {
  73. eventFunction = x;
  74. break;
  75. }
  76. }
  77. if (eventFunction instanceof Function) {
  78. setEventFunctionNameToDomObjectAttribute(this, eventName, eventFunction);
  79. }
  80. } catch (e) {
  81. console.error(`为jQuery添加on方法的Hook时发生错误: ${e}`);
  82. }
  83. return fnOnHolder.apply(this, arguments);
  84. }
  85.  
  86. // TODO 还有delegate之类的比较隐晦的绑定事件的方式
  87.  
  88. console.log(`当前页面使用了jQueryjQuery Hook已初始化完毕。`);
  89. }
  90.  
  91. const addressIdGeneratorMap = {};
  92.  
  93. /**
  94. * 生成一个全局唯一的标识
  95. * @param eventName
  96. */
  97. function globalUnique(eventName) {
  98. const id = (addressIdGeneratorMap[eventName] || 0) + 1;
  99. addressIdGeneratorMap[eventName] = id;
  100. return `${globalUniqPrefix}_${eventName}_${id}`;
  101. }
  102.  
  103. /**
  104. * 为绑定了jquery事件的dom元素添加元素,提示所绑定的事件与对应的函数代码的全局变量的名称,只需要复制粘贴跟进去即可
  105. * 注意,有可能会为同一个元素重复绑定相同的事件
  106. *
  107. * @param domObject
  108. * @param eventName
  109. * @param eventFunction
  110. */
  111. function setEventFunctionNameToDomObjectAttribute(domObject, eventName, eventFunction) {
  112. // TODO bug fix 注意,事件名可能会包含一些非法的字符
  113. // cc11001100-jquery-$destroy-event-function
  114. eventName = safeSymbol(eventName);
  115. const eventFunctionGlobalName = globalUnique(eventName);
  116. window[eventFunctionGlobalName] = eventFunction;
  117. const attrName = `${globalUniqPrefix}-jQuery-${eventName}-event-function`;
  118. if (domObject.attr(attrName)) {
  119. domObject.attr(attrName + "-" + new Date().getTime(), eventFunctionGlobalName);
  120. } else {
  121. domObject.attr(attrName, eventFunctionGlobalName);
  122. }
  123. }
  124.  
  125. /***
  126. *
  127. * @param name
  128. */
  129. function safeSymbol(name) {
  130. const replaceMap = {
  131. ".": "_dot_",
  132. "$": "_dollar_",
  133. "-": "_dash_"
  134. };
  135. for (let key of Object.getOwnPropertyNames(replaceMap)) {
  136. name = name.replace(key, replaceMap[key]);
  137. }
  138. return name;
  139. }
  140.  
  141. })();
  142.