Skip to content

Instantly share code, notes, and snippets.

@JonathanGawrych
Created November 14, 2025 23:21
Show Gist options
  • Select an option

  • Save JonathanGawrych/efc6213fc4bf35a7f35461653fb4cb0e to your computer and use it in GitHub Desktop.

Select an option

Save JonathanGawrych/efc6213fc4bf35a7f35461653fb4cb0e to your computer and use it in GitHub Desktop.
Because Virtual Scroll sucks, disable it so you can use native search
// ==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