From 68dc46534a9ad042f08a156162a2e413b89acf3c Mon Sep 17 00:00:00 2001 From: Oliver Shi Date: Mon, 12 Dec 2022 12:15:38 -0500 Subject: [PATCH] fix iframe.js breaking links with hash URLs (#1115) Previously, iframe.js would not work with links contains hash URLs, i.e. links like ```html Skip to content ``` notice the URL starts with a hashtag. These links are different than normal HTML links, in that instead of redirecting to a new page, they will scroll the browser down to the element with the id matching the link. For the example above, it would scroll down to an element like ```html
main content!
``` However, iframe.js has an issue where, when one of these links are clicked, the iframe.js will reload and the parent frame's URL will be changed. This issue was caused by the following process: 1. iframe.js's has a window.popstate listener, which reloads the iframe whenever a popstate event was triggered (for instance by clicking on one of these links), 2. clicking a hash link will trigger this listener, reloading the iframe 3. reloading the iframe causes the inner iframe to send messages to the outer frame 4. these messages are received by iframe.js's onMessage listener, which then performs a history.replaceState (for certain types of messages) To avoid this situation, this PR updates the popstate listener to be a no-op if the new URL of the page is the same as the old one, ignoring any hash URLs J=TECHOPS-7401 TEST=manual ran the theme's test site, created a basic iframe implementation before this change, hash urls would have the broken behavior after the change, they would scroll down the page without reloading the iframe or changing the parent's URL --- static/js/iframe-common.js | 26 +++++++++++++++++++++----- test-site/.gitignore | 2 ++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/static/js/iframe-common.js b/static/js/iframe-common.js index ec9960f24..2919ab1d1 100644 --- a/static/js/iframe-common.js +++ b/static/js/iframe-common.js @@ -80,11 +80,7 @@ export function generateIFrame(domain, answersExperienceFrame) { // For Safari document.body.scrollTop = 0; }); - - window.onpopstate = function() { - iframe.contentWindow.location.replace(calcFrameSrc()); - }; - + registerPopStateListener(); containerEl.appendChild(iframe); // Notify iframe of a click event on parent window @@ -135,6 +131,26 @@ export function generateIFrame(domain, answersExperienceFrame) { }, '#answers-frame'); } +function registerPopStateListener() { + let previousLocationHref = window.location.href; + + /** + * Reloads the iframe with a recalculated src URL. + * Does not reload the iframe if the URL has only changed its hash param. + */ + function popStateListener() { + /** @param {string} url */ + function getURLWithoutHash(url) { + return url.split('#')[0]; + } + if (getURLWithoutHash(previousLocationHref) !== getURLWithoutHash(window.location.href)) { + iframe.contentWindow.location.replace(calcFrameSrc()); + } + previousLocationHref = window.location.href; + } + window.addEventListener('popstate', popStateListener); +} + /** * Sends data to the answers iframe if possible. Otherwise the message is queued * so that it can be sent when the iframe initializes. diff --git a/test-site/.gitignore b/test-site/.gitignore index 44b520f28..27ba7aac7 100644 --- a/test-site/.gitignore +++ b/test-site/.gitignore @@ -13,6 +13,8 @@ webpack-config.js !config/global_config.json !config/locale_config.json !config/index.json +!config/index.ar.json +!config/index.es.json !pages/index.html.hbs !public/iframe_test.html !public/overlay.html \ No newline at end of file