Skip to content

Instantly share code, notes, and snippets.

@selfagency
Created July 13, 2026 19:44
Show Gist options
  • Select an option

  • Save selfagency/3de50b7d7c0f05c32a5cf2a7fab30ca1 to your computer and use it in GitHub Desktop.

Select an option

Save selfagency/3de50b7d7c0f05c32a5cf2a7fab30ca1 to your computer and use it in GitHub Desktop.
default zoom for google docs userscript
// ==UserScript==
// @name Default Zoom for Google Docs/Sheets
// @namespace default-zoom-google-workspace
// @version 1.0
// @description Automatically sets your preferred zoom level in Google Docs and Sheets on load, by simulating the zoom menu clicks (since Google exposes no API for this).
// @match https://docs.google.com/document/d/*
// @match https://docs.google.com/spreadsheets/d/*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @run-at document-idle
// ==/UserScript==
(function () {
'use strict';
const STORAGE_KEY = 'defaultZoomPercent';
const VALID_ZOOMS = [50, 75, 90, 100, 125, 150, 175, 200];
const DEFAULT_ZOOM = GM_getValue(STORAGE_KEY, 100);
const MAX_WAIT_MS = 15000;
const POLL_INTERVAL = 200;
// Let user pick/change their preferred zoom via Tampermonkey menu
GM_registerMenuCommand('Set default zoom level…', () => {
const input = prompt(
`Enter default zoom (${VALID_ZOOMS.join(', ')}):`,
String(DEFAULT_ZOOM)
);
const value = parseInt(input, 10);
if (VALID_ZOOMS.includes(value)) {
GM_setValue(STORAGE_KEY, value);
alert(`Default zoom set to ${value}%. Reload the doc to apply.`);
} else if (input !== null) {
alert('Invalid value. Choose one of: ' + VALID_ZOOMS.join(', '));
}
});
// Skip work if already at the desired zoom
function getCurrentZoomButton() {
// Docs/Sheets render the current zoom % as a clickable toolbar control.
// Match by visible text like "100%" rather than brittle dynamic IDs.
const candidates = document.querySelectorAll(
'[role="button"], .goog-inline-block, .docs-icon-button-content'
);
for (const el of candidates) {
const text = (el.textContent || '').trim();
if (/^\d{2,3}%$/.test(text)) {
return el;
}
}
return null;
}
function findMenuItemByPercent(percent) {
const items = document.querySelectorAll(
'[role="menuitem"], [role="menuitemradio"], .goog-menuitem'
);
for (const item of items) {
const text = (item.textContent || '').trim();
if (text === `${percent}%`) {
return item;
}
}
return null;
}
function clickElement(el) {
if (!el) return false;
el.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true }));
el.dispatchEvent(new MouseEvent('mouseup', { bubbles: true, cancelable: true }));
el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
return true;
}
function setZoom(percent) {
const zoomButton = getCurrentZoomButton();
if (!zoomButton) return false;
// Already at target zoom — skip
if (zoomButton.textContent.trim() === `${percent}%`) {
return true;
}
// Open the zoom dropdown
clickElement(zoomButton);
// Wait a tick for the menu to render, then click the matching option
setTimeout(() => {
const menuItem = findMenuItemByPercent(percent);
if (menuItem) {
clickElement(menuItem);
} else {
// Close menu if we couldn't find a match, to avoid leaving it open
document.body.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
}
}, 150);
return true;
}
function waitForToolbarAndApply() {
const start = Date.now();
const interval = setInterval(() => {
const zoomButton = getCurrentZoomButton();
if (zoomButton) {
clearInterval(interval);
setZoom(DEFAULT_ZOOM);
} else if (Date.now() - start > MAX_WAIT_MS) {
clearInterval(interval);
console.warn('[Default Zoom] Toolbar zoom control not found, giving up.');
}
}, POLL_INTERVAL);
}
waitForToolbarAndApply();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment