nowcoder extend class

展开多少届。直观看到用户是多少届的

  1. // ==UserScript==
  2. // @name nowcoder extend class
  3. // @description 展开多少届。直观看到用户是多少届的
  4. // @version 1.1
  5. // @include https://www.nowcoder.com/search/*
  6. // @include https://www.nowcoder.com/feed/main/detail/*
  7. // @namespace baojie.nowcoder
  8. // @license MIT
  9. // @grant GM_xmlhttpRequest
  10. // ==/UserScript==
  11. (function() {
  12. 'use strict';
  13.  
  14. // Cache to store user IDs and their graduation years
  15. const userCache = new Map();
  16. // Set to track user IDs that have been processed
  17. const processedUserIds = new Set();
  18.  
  19. // Add CSS styles for graduation year display
  20. function addStyles() {
  21. // Check if styles already added
  22. if (document.getElementById('nowcoder-grad-year-styles')) {
  23. return;
  24. }
  25. const styleElement = document.createElement('style');
  26. styleElement.id = 'nowcoder-grad-year-styles';
  27. styleElement.textContent = `
  28. .grad-year-display {
  29. color: #ff7830;
  30. margin-left: 5px;
  31. font-size: 12px;
  32. font-weight: bold;
  33. }
  34. `;
  35. document.head.appendChild(styleElement);
  36. }
  37.  
  38. // Extract user ID from element
  39. function getUserIdFromElement(element) {
  40. // For links with href attribute
  41. if (element.tagName === 'A' && element.href) {
  42. const match = element.href.match(/\/users\/(\d+)/);
  43. if (match && match[1]) {
  44. return match[1];
  45. }
  46. }
  47. // For other elements, check parent or child links
  48. const link = element.closest('a[href*="/users/"]') ||
  49. element.querySelector('a[href*="/users/"]');
  50. if (link) {
  51. const match = link.href.match(/\/users\/(\d+)/);
  52. if (match && match[1]) {
  53. return match[1];
  54. }
  55. }
  56. return null;
  57. }
  58.  
  59. // Display the graduation year next to the user name
  60. function displayGradYear(element, gradYear) {
  61. // Find the appropriate container
  62. const nameSpan = element.querySelector('.name-text');
  63. // Check if we already added to this specific element
  64. if (nameSpan.querySelector('.grad-year-display') || element.hasAttribute('data-grad-displayed')) {
  65. return;
  66. }
  67. // Create the graduation year display
  68. const gradYearElement = document.createElement('span');
  69. gradYearElement.className = 'grad-year-display';
  70. gradYearElement.textContent = `(${gradYear})`;
  71. gradYearElement.setAttribute('data-userid', element.getAttribute('data-userid'));
  72. // Add to the page
  73. nameSpan.appendChild(gradYearElement);
  74. // Mark this element as having the graduation year displayed
  75. element.setAttribute('data-grad-displayed', 'true');
  76. }
  77.  
  78. // Fetch user info using GM_xmlhttpRequest
  79. function fetchUserInfo(userId) {
  80. return new Promise((resolve) => {
  81. const url = `https://gw-c.nowcoder.com/api/sparta/user/info/card/${userId}?_=${Date.now()}`;
  82. // Use GM_xmlhttpRequest directly, avoiding the unsafe header issue
  83. GM_xmlhttpRequest({
  84. method: 'GET',
  85. url: url,
  86. headers: {
  87. 'Accept': 'application/json',
  88. 'X-Requested-With': 'XMLHttpRequest'
  89. // Removed Referer header to avoid the 'unsafe header' error
  90. },
  91. onload: function(response) {
  92. try {
  93. const data = JSON.parse(response.responseText);
  94. if (data.success && data.data && data.data.workTime) {
  95. resolve(data.data.workTime);
  96. } else {
  97. resolve(null);
  98. }
  99. } catch (e) {
  100. console.error('Failed to parse response', e);
  101. resolve(null);
  102. }
  103. },
  104. onerror: function() {
  105. resolve(null);
  106. }
  107. });
  108. });
  109. }
  110.  
  111. // Process a single user element
  112. async function processUserElement(element) {
  113. // Skip if already fully processed
  114. if (element.hasAttribute('data-grad-displayed')) {
  115. return;
  116. }
  117. const userId = getUserIdFromElement(element);
  118. if (!userId) {
  119. return;
  120. }
  121. // Store userId as data attribute to help with duplicate detection
  122. element.setAttribute('data-userid', userId);
  123. // Skip if already in progress for this user ID
  124. if (processedUserIds.has(userId)) {
  125. // If we already know this user's info, display it
  126. if (userCache.has(userId)) {
  127. displayGradYear(element, userCache.get(userId));
  128. }
  129. return;
  130. }
  131. // Mark as being processed
  132. processedUserIds.add(userId);
  133. // Check cache first
  134. if (userCache.has(userId)) {
  135. displayGradYear(element, userCache.get(userId));
  136. return;
  137. }
  138. // Fetch user info
  139. const gradYear = await fetchUserInfo(userId);
  140. if (gradYear) {
  141. userCache.set(userId, gradYear);
  142. // Update all instances of this user on the page
  143. document.querySelectorAll(`[data-userid="${userId}"]:not([data-grad-displayed])`).forEach(el => {
  144. displayGradYear(el, gradYear);
  145. });
  146. }
  147. }
  148.  
  149. // Process all user elements visible on the page
  150. function processVisibleElements() {
  151. // Find all user name links
  152. document.querySelectorAll('a[href*="/users/"]').forEach(link => {
  153. if (link.closest('.user-nickname') || link.querySelector('.name-text')) {
  154. processUserElement(link);
  155. }
  156. });
  157. }
  158.  
  159. // Simple debounce function
  160. function debounce(func, wait) {
  161. let timeout;
  162. return function() {
  163. const context = this, args = arguments;
  164. clearTimeout(timeout);
  165. timeout = setTimeout(() => func.apply(context, args), wait);
  166. };
  167. }
  168.  
  169. // Initialize
  170. function init() {
  171. console.log('Nowcoder graduation year display script initialized');
  172. addStyles();
  173. // Initial processing with a short delay
  174. setTimeout(processVisibleElements, 500);
  175. // Process on scroll (debounced)
  176. window.addEventListener('scroll', debounce(processVisibleElements, 300));
  177. // Process on content changes
  178. const observer = new MutationObserver(debounce(mutations => {
  179. let hasNewNodes = false;
  180. mutations.forEach(mutation => {
  181. if (mutation.addedNodes.length > 0) {
  182. hasNewNodes = true;
  183. }
  184. });
  185. if (hasNewNodes) {
  186. processVisibleElements();
  187. }
  188. }, 300));
  189. observer.observe(document.body, { childList: true, subtree: true });
  190. }
  191.  
  192. // Run after page has loaded
  193. if (document.readyState === 'loading') {
  194. document.addEventListener('DOMContentLoaded', init);
  195. } else {
  196. init();
  197. }
  198. })();

QingJ © 2025

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