USTC icourse reviews enhanced

USTC icourse fold reviews and expand control

  1. // ==UserScript==
  2. // @name USTC icourse reviews enhanced
  3. // @namespace http://tampermonkey.net/
  4. // @version 2024-04-32
  5. // @description USTC icourse fold reviews and expand control
  6. // @author liuly
  7. // @match https://icourse.club/course/*
  8. // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
  9. // @license MIT
  10. // ==/UserScript==
  11.  
  12. function addStyle(aCss){
  13. 'use strict';
  14. let head = document.getElementsByTagName('head')[0];
  15. if (head) {
  16. let style = document.createElement('style');
  17. style.setAttribute('type', 'text/css');
  18. style.textContent = aCss;
  19. head.appendChild(style);
  20. return style;
  21. }
  22. return null;
  23. }
  24.  
  25. addStyle(`
  26. .folded {
  27. display: -webkit-box;
  28. -webkit-box-orient: vertical;
  29. -webkit-line-clamp: 20;
  30. overflow: hidden;
  31. }
  32. .display-folded .review-content {
  33. -webkit-line-clamp: unset !important;
  34. }
  35. .read-more {
  36. display: block;
  37. margin-top: 10px;
  38. }
  39. .display-folded .read-more {
  40. display: none !important;
  41. }
  42. .fold-button {
  43. display: none;
  44. }
  45. .display-folded .fold-button {
  46. display: inline !important;
  47. }
  48. .fixed-bottom-bar {
  49. position: fixed;
  50. width: 100%;
  51. bottom: 0;
  52. padding-top: 15px;
  53. background-color: #fff;
  54. }
  55. `);
  56.  
  57. (function() {
  58. 'use strict';
  59.  
  60. let requestId;
  61.  
  62. // 为所有过长评论默认折叠,并在内容末尾增加“打开”文字按钮;在底栏增加“折叠”文字按钮
  63. function initFoldedElements() {
  64. const reviewContents = document.querySelectorAll('.review-content');
  65.  
  66. for (const reviewContent of reviewContents) {
  67. const reviewContentHeight = reviewContent.scrollHeight;
  68. if (reviewContentHeight < 30 * parseFloat(window.getComputedStyle(reviewContent).lineHeight)) {
  69. continue;
  70. }
  71. const wrapperDiv = document.createElement('div');
  72. wrapperDiv.className = 'review-wrapper';
  73.  
  74. // 移动到 wrapper 内,限制行数
  75. reviewContent.parentNode.insertBefore(wrapperDiv, reviewContent);
  76. wrapperDiv.appendChild(reviewContent);
  77. reviewContent.classList.add('folded');
  78.  
  79. // 查看更多
  80. const readMoreButton = document.createElement('button');
  81. readMoreButton.classList.add('read-more');
  82. readMoreButton.textContent = '查看更多';
  83. readMoreButton.addEventListener('click', () => {
  84. wrapperDiv.parentNode.classList.add('display-folded');
  85. });
  86. wrapperDiv.appendChild(readMoreButton);
  87.  
  88. // 折叠
  89. const foldButton = document.createElement('button');
  90. foldButton.classList.add('fold-button');
  91. foldButton.textContent = '折叠';
  92. foldButton.addEventListener('click', () => {
  93. wrapperDiv.parentNode.classList.remove('display-folded');
  94. window.scrollTo({top: wrapperDiv.offsetTop, left: 0});
  95. });
  96. wrapperDiv.nextElementSibling.appendChild(foldButton);
  97. }
  98. }
  99.  
  100. // 在每一帧中执行的函数
  101. function checkContentPosition() {
  102. // 获取所有可能折叠的评课内容元素
  103. const reviewContents = document.querySelectorAll('.review-wrapper');
  104.  
  105. // 获取页面底部位置信息
  106. const pageBottom = window.innerHeight;
  107.  
  108. // 遍历评课内容元素
  109. reviewContents.forEach(reviewContent => {
  110. // 获取评课内容元素的位置信息
  111. const reviewContentRect = reviewContent.getBoundingClientRect();
  112. const reviewContentTop = reviewContentRect.top;
  113. const reviewContentBottom = reviewContentRect.bottom;
  114.  
  115. const bottomBar = reviewContent.nextElementSibling;
  116. // 如果评课内容元素出现,且处在打开状态
  117. const isOpen = reviewContent.parentNode.classList.contains('display-folded');
  118. if (reviewContentTop < pageBottom && reviewContentBottom > pageBottom && isOpen) {
  119. // 设置底栏元素的 fixed bottom 样式
  120. bottomBar.classList.add('fixed-bottom-bar');
  121. } else {
  122. bottomBar.classList.remove('fixed-bottom-bar');
  123. }
  124. });
  125.  
  126. // 继续在下一帧中执行
  127. requestId = window.requestAnimationFrame(checkContentPosition);
  128. }
  129.  
  130. // 在页面加载完成后执行
  131. window.addEventListener('load', () => {
  132. initFoldedElements();
  133. // 在每一帧中检查评课内容元素的位置并设置底栏样式
  134. requestId = window.requestAnimationFrame(checkContentPosition);
  135. });
  136. })();