Created
November 14, 2025 23:21
-
-
Save JonathanGawrych/efc6213fc4bf35a7f35461653fb4cb0e to your computer and use it in GitHub Desktop.
Because Virtual Scroll sucks, disable it so you can use native search
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name Disable Virtual Scroll in JIRA | |
| // @version 2025-01-02 | |
| // @description Because Virtual Scroll sucks, disable it so you can use native search | |
| // @author Jonathan Gawrych | |
| // @match https://*.atlassian.net/* | |
| // @icon https://www.google.com/s2/favicons?sz=64&domain=atlassian.net | |
| // @grant none | |
| // @run-at document-start | |
| // ==/UserScript== | |
| (function() { | |
| 'use strict'; | |
| const EVIL_MAGIC_KEY = '__evil__hack__'; | |
| // In order to reach in to Parcel's module system, we are going to have to do a lot of evil | |
| // Use global object prototype polution with magic getter to get a reference to the module list | |
| // We need to do this before the document starts so we can get our evil in before the list is constructed | |
| Object.defineProperty(Object.prototype, EVIL_MAGIC_KEY, { | |
| // This must be a old function to get the right 'this' | |
| get: function () { | |
| // Return a reference to this | |
| return { exports: this }; | |
| }, | |
| // This must be false, or it breaks for(in) statements | |
| enumerable: false, | |
| configurable: true | |
| }); | |
| // Now we need to wait until the page is loaded | |
| document.addEventListener('DOMContentLoaded', () => { | |
| // Now we need to find the parcelRequire function, however it's suffixed with a hash that I assume changes regularly | |
| const requireKey = Object.keys(window).filter((key) => key.startsWith('parcelRequire'))[0]; | |
| // Safety check | |
| if (requireKey == null) { | |
| console.warn('I could not find the parcelRequire function!'); | |
| return; | |
| } | |
| // Now lets activate the evil hack | |
| const modules = window[requireKey](EVIL_MAGIC_KEY); | |
| // Kill the evil hack. | |
| delete Object.prototype[EVIL_MAGIC_KEY]; | |
| // The module is loaded lazily, so we need to wait for it. | |
| let tries = 100; | |
| const waitInterval = setInterval(() => { | |
| // Now let's find our module | |
| // We can't disable virtual scroll, but we can set the overscan to a large number, such that | |
| // The entire window is loaded | |
| const overscanHeightModule = Object.values(modules).filter( | |
| (module) => typeof module.exports === 'object' && 'useOverscanHeight' in module.exports | |
| )[0]; | |
| // Safety check 2 | |
| if (overscanHeightModule == null) { | |
| if (tries-- === 0) { | |
| console.warn('I could not find the overscanHeightModule!'); | |
| clearInterval(waitInterval); | |
| } | |
| return; | |
| } | |
| clearInterval(waitInterval); | |
| // Now lets override this module's useOverscanHeight method. | |
| // We can't directly do it, but we can use property descriptors to do it | |
| let first = true; | |
| var originalFnDescriptor = Object.getOwnPropertyDescriptor(overscanHeightModule.exports, 'useOverscanHeight'); | |
| Object.defineProperty(overscanHeightModule.exports, 'useOverscanHeight', { | |
| ...originalFnDescriptor, | |
| get: () => { | |
| // Their method returns a method, to get the result | |
| // Also it seems like I can't just replace it, as the page crashes | |
| // We'll call their method, but then replace the result | |
| const oldFn = originalFnDescriptor.get(); | |
| // Monkeypatch! | |
| return function (...args) { | |
| // Call their method, but then ignore their results | |
| const result = oldFn.apply(this, args); | |
| if (first) { | |
| console.log('Disabled Virtual Scroll!'); | |
| first = false; | |
| } | |
| // Virtually no virtual scroll! | |
| return 99999999; | |
| }; | |
| } | |
| }); | |
| }, 100); | |
| }, {once: true}); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment