mirror of
https://github.com/LukeHagar/unicorn-utterances.git
synced 2025-12-07 12:57:45 +00:00
chore: migrate many of our interactive scripts to defer
This commit is contained in:
26
public/scripts/backbtn.js
Normal file
26
public/scripts/backbtn.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/* AFTER CHANGING THIS FILE, PLEASE MANUALLY MINIFY IT AND PUT INTO backbtn.min.js */
|
||||||
|
/* TODO: Add minifier to build script */
|
||||||
|
window.onload = () => {
|
||||||
|
const backBtn = document.querySelector('#backbtn');
|
||||||
|
|
||||||
|
let hasHistory = false;
|
||||||
|
window.addEventListener('beforeunload', () => {
|
||||||
|
hasHistory = true;
|
||||||
|
})
|
||||||
|
|
||||||
|
backBtn.addEventListener('click', () => {
|
||||||
|
if (!document.referrer) {
|
||||||
|
// This is the first page the user has visited on the site in this session
|
||||||
|
window.location.href = '/';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
history.back();
|
||||||
|
|
||||||
|
// User cannot go back, meaning that we're at the first page of the site session
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!hasHistory){
|
||||||
|
window.location.href = "/";
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
|
})
|
||||||
|
}
|
||||||
1
public/scripts/backbtn.min.js
vendored
Normal file
1
public/scripts/backbtn.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
window.onload=()=>{let e=document.querySelector("#backbtn"),r=!1;window.addEventListener("beforeunload",()=>{r=!0}),e.addEventListener("click",()=>{if(!document.referrer){window.location.href="/";return}history.back(),setTimeout(()=>{r||(window.location.href="/")},200)})};
|
||||||
167
public/scripts/tabs.js
Normal file
167
public/scripts/tabs.js
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
/* AFTER CHANGING THIS FILE, PLEASE MANUALLY MINIFY IT AND PUT INTO tabs.min.js */
|
||||||
|
|
||||||
|
const LOCAL_STORAGE_KEY = "tabs-selection";
|
||||||
|
|
||||||
|
window.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const tabLists = document.querySelectorAll('[role="tablist"]');
|
||||||
|
|
||||||
|
tabLists.forEach(tabList => {
|
||||||
|
/**
|
||||||
|
* @type {NodeListOf<HTMLElement>}
|
||||||
|
*/
|
||||||
|
const tabs = tabList.querySelectorAll('[role="tab"]');
|
||||||
|
|
||||||
|
// Add a click event handler to each tab
|
||||||
|
tabs.forEach((tab) => {
|
||||||
|
tab.addEventListener('click', e => {
|
||||||
|
/**
|
||||||
|
* @type {HTMLElement}
|
||||||
|
*/
|
||||||
|
const target = e.target;
|
||||||
|
// Scroll onto screen in order to avoid jumping page locations
|
||||||
|
setTimeout(() => {
|
||||||
|
target.scrollIntoView({
|
||||||
|
behavior: "auto",
|
||||||
|
block: "center",
|
||||||
|
inline: "center",
|
||||||
|
});
|
||||||
|
}, 0);
|
||||||
|
changeTabs({ target })
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Enable arrow navigation between tabs in the tab list
|
||||||
|
let tabFocus = 0;
|
||||||
|
|
||||||
|
tabList.addEventListener('keydown', (_e) => {
|
||||||
|
/**
|
||||||
|
* @type {KeyboardEvent}
|
||||||
|
*/
|
||||||
|
const e = _e;
|
||||||
|
// Move right
|
||||||
|
if (e.keyCode === 39 || e.keyCode === 37) {
|
||||||
|
tabs[tabFocus].setAttribute('tabindex', `-1`);
|
||||||
|
if (e.keyCode === 39) {
|
||||||
|
tabFocus++;
|
||||||
|
// If we're at the end, go to the start
|
||||||
|
if (tabFocus >= tabs.length) {
|
||||||
|
tabFocus = 0;
|
||||||
|
}
|
||||||
|
// Move left
|
||||||
|
} else if (e.keyCode === 37) {
|
||||||
|
tabFocus--;
|
||||||
|
// If we're at the start, move to the end
|
||||||
|
if (tabFocus < 0) {
|
||||||
|
tabFocus = tabs.length - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tabs[tabFocus].setAttribute('tabindex', `0`);
|
||||||
|
tabs[tabFocus].focus();
|
||||||
|
tabs[tabFocus].click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const currentTab = localStorage.getItem(LOCAL_STORAGE_KEY);
|
||||||
|
if (currentTab) {
|
||||||
|
/**
|
||||||
|
* @type {HTMLElement}
|
||||||
|
*/
|
||||||
|
const el = document.querySelector(`[data-tabname="${currentTab}"]`);
|
||||||
|
if (el) changeTabs({ target: el });
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeTabs(_e) {
|
||||||
|
/**
|
||||||
|
* @type {{ target: HTMLElement }}
|
||||||
|
*/
|
||||||
|
const e = _e;
|
||||||
|
const target = e.target;
|
||||||
|
const parent = target.parentNode;
|
||||||
|
const grandparent = parent.parentNode;
|
||||||
|
|
||||||
|
// Remove all current selected tabs
|
||||||
|
parent
|
||||||
|
.querySelectorAll('[aria-selected="true"]')
|
||||||
|
.forEach((t) => t.setAttribute('aria-selected', `false`));
|
||||||
|
|
||||||
|
// Set this tab as selected
|
||||||
|
target.setAttribute('aria-selected', `true`);
|
||||||
|
|
||||||
|
const tabName = target.dataset.tabname;
|
||||||
|
/**
|
||||||
|
* @type {NodeListOf<HTMLElement>}
|
||||||
|
*/
|
||||||
|
const relatedTabs = document.querySelectorAll(`[role="tab"][data-tabname="${target.dataset.tabname}"]`);
|
||||||
|
|
||||||
|
localStorage.setItem(LOCAL_STORAGE_KEY, tabName);
|
||||||
|
|
||||||
|
for (let relatedTab of relatedTabs) {
|
||||||
|
if (relatedTab === target) continue;
|
||||||
|
changeTabs({ target: relatedTab });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide all tab panels
|
||||||
|
grandparent
|
||||||
|
.querySelectorAll('[role="tabpanel"]')
|
||||||
|
.forEach((p) => p.setAttribute('hidden', `true`));
|
||||||
|
|
||||||
|
// Show the selected panel
|
||||||
|
grandparent.parentNode
|
||||||
|
.querySelector(`#${target.getAttribute('aria-controls')}`)
|
||||||
|
.removeAttribute('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} el
|
||||||
|
* @param {(el: HTMLElement) => boolean} check
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function checkElementsParents(el, check) {
|
||||||
|
if (el.parentElement) {
|
||||||
|
if (!check(el.parentElement)) {
|
||||||
|
return checkElementsParents(el.parentElement, check);
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(() => {
|
||||||
|
// If user has linked to a heading that's inside of a tab
|
||||||
|
const hash = window.location.hash;
|
||||||
|
if (!hash) return;
|
||||||
|
const heading = document.querySelector < HTMLElement > (hash);
|
||||||
|
if (!heading) return;
|
||||||
|
const isHidden = checkElementsParents(heading, el =>
|
||||||
|
el.hasAttribute('hidden') && el.getAttribute('hidden') !== "false"
|
||||||
|
)
|
||||||
|
// If it's not hidden, then we can assume that the browser will auto-scroll to it
|
||||||
|
if (!isHidden) return;
|
||||||
|
const partialHash = hash.slice(1);
|
||||||
|
try {
|
||||||
|
const matchingTab = document.querySelector < HTMLElement > (
|
||||||
|
`[data-headers*="${partialHash}"`
|
||||||
|
);
|
||||||
|
if (!matchingTab) return;
|
||||||
|
// If header is not in a tab
|
||||||
|
const tabName = matchingTab.getAttribute("data-tabname");
|
||||||
|
if (!tabName) return;
|
||||||
|
matchingTab.click();
|
||||||
|
setTimeout(() => {
|
||||||
|
const el = document.querySelector(hash);
|
||||||
|
if (!el) return;
|
||||||
|
el.scrollIntoView(true);
|
||||||
|
}, 0);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error finding matching tab", e);
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
|
||||||
|
});
|
||||||
1
public/scripts/tabs.min.js
vendored
Normal file
1
public/scripts/tabs.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
const LOCAL_STORAGE_KEY="tabs-selection";window.addEventListener("DOMContentLoaded",()=>{let e=document.querySelectorAll('[role="tablist"]');e.forEach(e=>{let t=e.querySelectorAll('[role="tab"]');t.forEach(e=>{e.addEventListener("click",e=>{let t=e.target;setTimeout(()=>{t.scrollIntoView({behavior:"auto",block:"center",inline:"center"})},0),a({target:t})})});let r=0;e.addEventListener("keydown",e=>{let a=e;(39===a.keyCode||37===a.keyCode)&&(t[r].setAttribute("tabindex","-1"),39===a.keyCode?++r>=t.length&&(r=0):37===a.keyCode&&--r<0&&(r=t.length-1),t[r].setAttribute("tabindex","0"),t[r].focus(),t[r].click())})});let t=localStorage.getItem(LOCAL_STORAGE_KEY);if(t){let r=document.querySelector(`[data-tabname="${t}"]`);r&&a({target:r})}function a(e){let t=e.target,r=t.parentNode,l=r.parentNode;r.querySelectorAll('[aria-selected="true"]').forEach(e=>e.setAttribute("aria-selected","false")),t.setAttribute("aria-selected","true");let n=t.dataset.tabname,o=document.querySelectorAll(`[role="tab"][data-tabname="${t.dataset.tabname}"]`);for(let i of(localStorage.setItem(LOCAL_STORAGE_KEY,n),o))i!==t&&a({target:i});l.querySelectorAll('[role="tabpanel"]').forEach(e=>e.setAttribute("hidden","true")),l.parentNode.querySelector(`#${t.getAttribute("aria-controls")}`).removeAttribute("hidden")}function l(e,t){return!!e.parentElement&&(!!t(e.parentElement)||l(e.parentElement,t))}(()=>{let e=window.location.hash;if(!e)return;let t=document.querySelector<HTMLElement>e;if(!t)return;let r=l(t,e=>e.hasAttribute("hidden")&&"false"!==e.getAttribute("hidden"));if(!r)return;let a=e.slice(1);try{let n=document.querySelector<HTMLElement>`[data-headers*="${a}"`;if(!n)return;let o=n.getAttribute("data-tabname");if(!o)return;n.click(),setTimeout(()=>{let t=document.querySelector(e);t&&t.scrollIntoView(!0)},0)}catch(i){console.error("Error finding matching tab",i)}})()});
|
||||||
28
public/scripts/themetoggle.js
Normal file
28
public/scripts/themetoggle.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/* AFTER CHANGING THIS FILE, PLEASE MANUALLY MINIFY IT AND PUT INTO tabs.min.js */
|
||||||
|
const COLOR_MODE_STORAGE_KEY = "currentTheme";
|
||||||
|
|
||||||
|
const themeToggleBtn = document.querySelector('#theme-toggle-button');
|
||||||
|
const darkIconEl = document.querySelector('#dark-icon');
|
||||||
|
const lightIconEl = document.querySelector('#light-icon');
|
||||||
|
function toggleButton(theme) {
|
||||||
|
themeToggleBtn.ariaPressed = `${theme === 'dark'}`;
|
||||||
|
if (theme === 'light') {
|
||||||
|
lightIconEl.style.display = null;
|
||||||
|
darkIconEl.style.display = 'none';
|
||||||
|
} else {
|
||||||
|
lightIconEl.style.display = 'none';
|
||||||
|
darkIconEl.style.display = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Migrate to `classList`
|
||||||
|
const initialTheme = document.documentElement.className;
|
||||||
|
toggleButton(initialTheme);
|
||||||
|
themeToggleBtn.addEventListener('click', () => {
|
||||||
|
const currentTheme = document.documentElement.className;
|
||||||
|
document.documentElement.className = currentTheme === 'light' ? 'dark' : 'light';
|
||||||
|
// TODO: Persist new setting
|
||||||
|
const newTheme = document.documentElement.className;
|
||||||
|
toggleButton(newTheme);
|
||||||
|
localStorage.setItem(COLOR_MODE_STORAGE_KEY, newTheme)
|
||||||
|
})
|
||||||
1
public/scripts/themetoggle.min.js
vendored
Normal file
1
public/scripts/themetoggle.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
const COLOR_MODE_STORAGE_KEY="currentTheme",themeToggleBtn=document.querySelector("#theme-toggle-button"),darkIconEl=document.querySelector("#dark-icon"),lightIconEl=document.querySelector("#light-icon");function toggleButton(e){themeToggleBtn.ariaPressed=`${"dark"===e}`,"light"===e?(lightIconEl.style.display=null,darkIconEl.style.display="none"):(lightIconEl.style.display="none",darkIconEl.style.display=null)}const initialTheme=document.documentElement.className;toggleButton(initialTheme),themeToggleBtn.addEventListener("click",()=>{let e=document.documentElement.className;document.documentElement.className="light"===e?"dark":"light";let t=document.documentElement.className;toggleButton(t),localStorage.setItem("currentTheme",t)});
|
||||||
@@ -1,11 +1,6 @@
|
|||||||
---
|
---
|
||||||
import { Icon } from 'astro-icon';
|
import { Icon } from 'astro-icon';
|
||||||
import btnStyles from "./dark-light-button.module.scss";
|
import btnStyles from "./dark-light-button.module.scss";
|
||||||
|
|
||||||
import {
|
|
||||||
COLOR_MODE_STORAGE_KEY,
|
|
||||||
} from "constants/index";
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<button
|
<button
|
||||||
@@ -18,32 +13,4 @@ import {
|
|||||||
<Icon name="light" height="36" width="36" id="light-icon" style="display: none;"/>
|
<Icon name="light" height="36" width="36" id="light-icon" style="display: none;"/>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- DO NOT SWITCH THE ORDER OF THESE TWO TAGS, IT WILL BREAK FUNCTIONALITY -->
|
<script defer is:inline src="/scripts/themetoggle.min.js"/>
|
||||||
|
|
||||||
<script is:inline define:vars={{COLOR_MODE_STORAGE_KEY}}>
|
|
||||||
const themeToggleBtn = document.querySelector('#theme-toggle-button');
|
|
||||||
const darkIconEl = document.querySelector('#dark-icon');
|
|
||||||
const lightIconEl = document.querySelector('#light-icon');
|
|
||||||
function toggleButton(theme) {
|
|
||||||
themeToggleBtn.ariaPressed = `${theme === 'dark'}`;
|
|
||||||
if (theme === 'light') {
|
|
||||||
lightIconEl.style.display = null;
|
|
||||||
darkIconEl.style.display = 'none';
|
|
||||||
} else {
|
|
||||||
lightIconEl.style.display = 'none';
|
|
||||||
darkIconEl.style.display = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Migrate to `classList`
|
|
||||||
const initialTheme = document.documentElement.className;
|
|
||||||
toggleButton(initialTheme);
|
|
||||||
themeToggleBtn.addEventListener('click', () => {
|
|
||||||
const currentTheme = document.documentElement.className;
|
|
||||||
document.documentElement.className = currentTheme === 'light' ? 'dark' : 'light';
|
|
||||||
// TODO: Persist new setting
|
|
||||||
const newTheme = document.documentElement.className;
|
|
||||||
toggleButton(newTheme);
|
|
||||||
localStorage.setItem(COLOR_MODE_STORAGE_KEY, newTheme)
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|||||||
@@ -19,30 +19,7 @@ const isCollection = Astro.url.pathname.startsWith(`${rootPath}collections`);
|
|||||||
<button id="backbtn" class={`${layoutStyles.backBtn} baseBtn`} aria-label="Go back">
|
<button id="backbtn" class={`${layoutStyles.backBtn} baseBtn`} aria-label="Go back">
|
||||||
<Icon height="36" width="36" name="back" />
|
<Icon height="36" width="36" name="back" />
|
||||||
</button>
|
</button>
|
||||||
<script is:inline>
|
<script is:inline defer src="/scripts/backbtn.min.js"/>
|
||||||
const backBtn = document.querySelector('#backbtn');
|
|
||||||
|
|
||||||
let hasHistory = false;
|
|
||||||
window.addEventListener('beforeunload', () => {
|
|
||||||
hasHistory = true;
|
|
||||||
})
|
|
||||||
|
|
||||||
backBtn.addEventListener('click', () => {
|
|
||||||
if (!document.referrer) {
|
|
||||||
// This is the first page the user has visited on the site in this session
|
|
||||||
window.location.href = '/';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
history.back();
|
|
||||||
|
|
||||||
// User cannot go back, meaning that we're at the first page of the site session
|
|
||||||
setTimeout(() => {
|
|
||||||
if (!hasHistory){
|
|
||||||
window.location.href = "/";
|
|
||||||
}
|
|
||||||
}, 200);
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div />
|
<div />
|
||||||
|
|||||||
@@ -1,144 +1,4 @@
|
|||||||
---
|
---
|
||||||
---
|
---
|
||||||
|
|
||||||
<script>
|
<script is:inline defer src="/scripts/tabs.min.js"/>
|
||||||
const LOCAL_STORAGE_KEY = "tabs-selection";
|
|
||||||
|
|
||||||
window.addEventListener('DOMContentLoaded', () => {
|
|
||||||
const tabLists = document.querySelectorAll('[role="tablist"]');
|
|
||||||
|
|
||||||
tabLists.forEach(tabList => {
|
|
||||||
const tabs = tabList.querySelectorAll('[role="tab"]') as NodeListOf<HTMLElement>;
|
|
||||||
|
|
||||||
// Add a click event handler to each tab
|
|
||||||
tabs.forEach((tab) => {
|
|
||||||
tab.addEventListener('click', e => {
|
|
||||||
const target = e.target as HTMLElement;
|
|
||||||
// Scroll onto screen in order to avoid jumping page locations
|
|
||||||
setTimeout(() => {
|
|
||||||
target.scrollIntoView({
|
|
||||||
behavior: "auto",
|
|
||||||
block: "center",
|
|
||||||
inline: "center",
|
|
||||||
});
|
|
||||||
}, 0);
|
|
||||||
changeTabs({ target })
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Enable arrow navigation between tabs in the tab list
|
|
||||||
let tabFocus = 0;
|
|
||||||
|
|
||||||
tabList.addEventListener('keydown', (e: KeyboardEvent) => {
|
|
||||||
// Move right
|
|
||||||
if (e.keyCode === 39 || e.keyCode === 37) {
|
|
||||||
tabs[tabFocus].setAttribute('tabindex', `-1`);
|
|
||||||
if (e.keyCode === 39) {
|
|
||||||
tabFocus++;
|
|
||||||
// If we're at the end, go to the start
|
|
||||||
if (tabFocus >= tabs.length) {
|
|
||||||
tabFocus = 0;
|
|
||||||
}
|
|
||||||
// Move left
|
|
||||||
} else if (e.keyCode === 37) {
|
|
||||||
tabFocus--;
|
|
||||||
// If we're at the start, move to the end
|
|
||||||
if (tabFocus < 0) {
|
|
||||||
tabFocus = tabs.length - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tabs[tabFocus].setAttribute('tabindex', `0`);
|
|
||||||
tabs[tabFocus].focus();
|
|
||||||
tabs[tabFocus].click();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const currentTab = localStorage.getItem(LOCAL_STORAGE_KEY);
|
|
||||||
if (currentTab) {
|
|
||||||
const el = document.querySelector(`[data-tabname="${currentTab}"]`) as HTMLElement;
|
|
||||||
if (el) changeTabs({target: el});
|
|
||||||
}
|
|
||||||
|
|
||||||
function changeTabs(e: { target: HTMLElement }) {
|
|
||||||
const target = e.target;
|
|
||||||
const parent = target.parentNode;
|
|
||||||
const grandparent = parent.parentNode;
|
|
||||||
|
|
||||||
// Remove all current selected tabs
|
|
||||||
parent
|
|
||||||
.querySelectorAll('[aria-selected="true"]')
|
|
||||||
.forEach((t) => t.setAttribute('aria-selected', `false`));
|
|
||||||
|
|
||||||
// Set this tab as selected
|
|
||||||
target.setAttribute('aria-selected', `true`);
|
|
||||||
|
|
||||||
const tabName = target.dataset.tabname;
|
|
||||||
const relatedTabs: NodeListOf<HTMLElement> = document.querySelectorAll(`[role="tab"][data-tabname="${target.dataset.tabname}"]`);
|
|
||||||
|
|
||||||
localStorage.setItem(LOCAL_STORAGE_KEY, tabName);
|
|
||||||
|
|
||||||
for (let relatedTab of relatedTabs) {
|
|
||||||
if (relatedTab === target) continue;
|
|
||||||
changeTabs({target: relatedTab});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hide all tab panels
|
|
||||||
grandparent
|
|
||||||
.querySelectorAll('[role="tabpanel"]')
|
|
||||||
.forEach((p) => p.setAttribute('hidden', `true`));
|
|
||||||
|
|
||||||
// Show the selected panel
|
|
||||||
grandparent.parentNode
|
|
||||||
.querySelector(`#${target.getAttribute('aria-controls')}`)
|
|
||||||
.removeAttribute('hidden');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------- */
|
|
||||||
|
|
||||||
function checkElementsParents(el: HTMLElement, check: (el: HTMLElement) => boolean): boolean {
|
|
||||||
if (el.parentElement) {
|
|
||||||
if (!check(el.parentElement)) {
|
|
||||||
return checkElementsParents(el.parentElement, check);
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(() => {
|
|
||||||
// If user has linked to a heading that's inside of a tab
|
|
||||||
const hash = window.location.hash;
|
|
||||||
if (!hash) return;
|
|
||||||
const heading = document.querySelector<HTMLElement>(hash);
|
|
||||||
if (!heading) return;
|
|
||||||
const isHidden = checkElementsParents(heading, el =>
|
|
||||||
el.hasAttribute('hidden') && el.getAttribute('hidden') !== "false"
|
|
||||||
)
|
|
||||||
// If it's not hidden, then we can assume that the browser will auto-scroll to it
|
|
||||||
if (!isHidden) return;
|
|
||||||
const partialHash = hash.slice(1);
|
|
||||||
try {
|
|
||||||
const matchingTab = document.querySelector<HTMLElement>(
|
|
||||||
`[data-headers*="${partialHash}"`
|
|
||||||
);
|
|
||||||
if (!matchingTab) return;
|
|
||||||
// If header is not in a tab
|
|
||||||
const tabName = matchingTab.getAttribute("data-tabname");
|
|
||||||
if (!tabName) return;
|
|
||||||
matchingTab.click();
|
|
||||||
setTimeout(() => {
|
|
||||||
const el = document.querySelector(hash);
|
|
||||||
if (!el) return;
|
|
||||||
el.scrollIntoView(true);
|
|
||||||
}, 0);
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Error finding matching tab", e);
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
Reference in New Issue
Block a user