Fix PageUp and PageDown scrolling for ChatGPT

Redirects PageUp and PageDown keys to scroll the conversation window while typing in the input field.

  1. // ==UserScript==
  2. // @name Fix PageUp and PageDown scrolling for ChatGPT
  3. // @description Redirects PageUp and PageDown keys to scroll the conversation window while typing in the input field.
  4. // @author NWP/DEVULSKY
  5. // @namespace https://gf.qytechs.cn/en/scripts/519486/
  6. // @version 0.3
  7. // @license MIT
  8. // @match https://chat.openai.com/*
  9. // @match https://chatgpt.com/*
  10. // @grant none
  11. //
  12. // ==/UserScript==
  13.  
  14. (function () {
  15. 'use strict';
  16.  
  17. /**
  18. * Finds the scrollable container of the conversation window.
  19. * Falls back to a dynamic class-based selector if a specific container ID is not found.
  20. * @returns {HTMLElement|null} The scrollable container element, or null if not found.
  21. */
  22. const getScrollableContainer = () =>
  23. document.querySelector('#conversation-inner-div') ||
  24. Array.from(document.querySelectorAll('div')).find(div => /^react-scroll-to-bottom--css-\S+$/.test(div.className));
  25.  
  26. /**
  27. * Checks if an element has `overflow: hidden`.
  28. * @param {HTMLElement} element - The element to check.
  29. * @returns {boolean} True if `overflow` is `hidden`, false otherwise.
  30. */
  31. const hasOverflowHidden = (element) => {
  32. if(!element) return false;
  33. const style = getComputedStyle(element);
  34. return style && style.overflow === 'hidden';
  35. };
  36.  
  37. /**
  38. * Smoothly scrolls the container over a period of time.
  39. * @param {HTMLElement} container - The scrollable container.
  40. * @param {number} targetPosition - The target scroll position.
  41. * @param {number} duration - The duration of the scroll animation.
  42. */
  43. const smoothScroll = (container, targetPosition, duration) => {
  44. const startPosition = container.scrollTop;
  45. const distance = targetPosition - startPosition;
  46. let startTime = null;
  47.  
  48. // Animation function that runs on each frame
  49. const animateScroll = (currentTime) => {
  50. if (!startTime) startTime = currentTime;
  51. const timeElapsed = currentTime - startTime;
  52. const progress = Math.min(timeElapsed / duration, 1); // Ensure progress is capped at 1
  53.  
  54. // Apply the easing function
  55. container.scrollTop = startPosition + distance * progress;
  56.  
  57. if (timeElapsed < duration) {
  58. requestAnimationFrame(animateScroll); // Continue the animation until duration is reached
  59. }
  60. };
  61.  
  62. // Start the smooth scrolling animation
  63. requestAnimationFrame(animateScroll);
  64. };
  65.  
  66. /**
  67. * Handles the PageUp and PageDown key events.
  68. * Prevents the default browser behavior and scrolls the conversation window instead.
  69. * @param {KeyboardEvent} event - The keydown event triggered by the user.
  70. */
  71. const handlePageKeys = (event) => {
  72. if (!['PageUp', 'PageDown'].includes(event.key)) return;
  73.  
  74. const scrollableContainer = getScrollableContainer();
  75. if (!scrollableContainer) return;
  76.  
  77. // Add this part
  78. if (hasOverflowHidden(scrollableContainer)) {
  79. scrollableContainer.style.overflow = 'visible';
  80. }
  81.  
  82. event.preventDefault();
  83.  
  84. const scrollFactor = event.key === 'PageUp' ? -0.75 : 0.75;
  85. const targetScrollPosition = scrollableContainer.scrollTop + (scrollableContainer.clientHeight * scrollFactor);
  86.  
  87. smoothScroll(scrollableContainer, targetScrollPosition, 400);
  88. };
  89.  
  90.  
  91. document.addEventListener('keydown', handlePageKeys);
  92. })();

QingJ © 2025

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