From 2978f18d8e5a261449da750f638f7a76986c57a4 Mon Sep 17 00:00:00 2001 From: Abdi Saeed Date: Wed, 30 Sep 2026 16:20:28 +0100 Subject: [PATCH] Stop embedded codelabs scrolling the parent page on load codelab-elements.js focuses the current step, which scrolls an embedding page to the iframe. When framed, force preventScroll on focus() calls. --- build.sh | 6 ++++++ static/iframe-focus-fix.js | 13 +++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 static/iframe-focus-fix.js diff --git a/build.sh b/build.sh index d5bade9..66bf894 100755 --- a/build.sh +++ b/build.sh @@ -6,6 +6,12 @@ set -euo pipefail PATH="/tmp/claat:${PATH}" python3 ./build.py sed -e 's#https://storage.googleapis.com/claat-public#/static#g' -i build/*/index.html +# Stop focus() inside an embedding iframe from scrolling the parent page (see static/iframe-focus-fix.js) +sed -e 's##&#' -i build/*/index.html +if grep -L 'iframe-focus-fix.js' build/*/index.html | grep -q .; then + echo "iframe-focus-fix.js was not injected into every page - has claat's script tag changed?" >&2 + exit 1 +fi # The public static hosting seems offline (e.g. https://storage.googleapis.com/claat-public/codelab-elements.css) so we vendor these assets: # Built by running `USE_BAZEL_VERSION=0.19.1 bazel build :bundle` with patches to: diff --git a/static/iframe-focus-fix.js b/static/iframe-focus-fix.js new file mode 100644 index 0000000..7237d37 --- /dev/null +++ b/static/iframe-focus-fix.js @@ -0,0 +1,13 @@ +// When a codelab is embedded in an iframe (e.g. on the CYF curriculum site), +// codelab-elements.js focuses the current step on load. Focusing an element +// inside an iframe scrolls the parent page to the iframe, losing the reader's +// place. Force preventScroll on every focus() call, only when embedded. +// Must load before codelab-elements.js. +(function () { + if (window.self === window.top) return; + var originalFocus = HTMLElement.prototype.focus; + HTMLElement.prototype.focus = function (options) { + var opts = Object.assign({}, options, { preventScroll: true }); + return originalFocus.call(this, opts); + }; +})();