redoc/src/utils/dom.ts
Aarni Koskela 3028d3d7ac
Fix Chrome scrolling by using alternate scrollIntoViewIfNeeded (#1823)
* Break out scrollIntoViewIfNeeded into freely usable function

* Always use alternate implementation of scrollIntoViewIfNeeded

Refs #1714

Refs #1742
2021-12-09 12:51:02 +02:00

78 lines
2.4 KiB
TypeScript

export const IS_BROWSER = typeof window !== 'undefined' && 'HTMLElement' in window;
export function querySelector(selector: string): Element | null {
if (typeof document !== 'undefined') {
return document.querySelector(selector);
}
return null;
}
/**
* Drop everything inside <...> (i.e., tags/elements), and keep the text.
* Unlike browser innerText, this removes newlines; it also doesn't handle
* un-encoded `<` or `>` characters very well, so don't feed it malformed HTML
*/
export function html2Str(html: string): string {
return html
.split(/<[^>]+>/)
.map(chunk => {
return chunk.trim();
})
.filter(trimmedChunk => {
return trimmedChunk.length > 0;
})
.join(' ');
}
// Alternate scrollIntoViewIfNeeded implementation.
// Used in all cases, since it seems Chrome's implementation is buggy
// when "Experimental Web Platform Features" is enabled (at least of version 96).
// See #1714, #1742
export function scrollIntoViewIfNeeded(el: HTMLElement, centerIfNeeded = true) {
const parent = el.parentNode as HTMLElement | null;
if (!parent) {
return;
}
const parentComputedStyle = window.getComputedStyle(parent, undefined);
const parentBorderTopWidth = parseInt(
parentComputedStyle.getPropertyValue('border-top-width'),
10,
);
const parentBorderLeftWidth = parseInt(
parentComputedStyle.getPropertyValue('border-left-width'),
10,
);
const overTop = el.offsetTop - parent.offsetTop < parent.scrollTop;
const overBottom =
el.offsetTop - parent.offsetTop + el.clientHeight - parentBorderTopWidth >
parent.scrollTop + parent.clientHeight;
const overLeft = el.offsetLeft - parent.offsetLeft < parent.scrollLeft;
const overRight =
el.offsetLeft - parent.offsetLeft + el.clientWidth - parentBorderLeftWidth >
parent.scrollLeft + parent.clientWidth;
const alignWithTop = overTop && !overBottom;
if ((overTop || overBottom) && centerIfNeeded) {
parent.scrollTop =
el.offsetTop -
parent.offsetTop -
parent.clientHeight / 2 -
parentBorderTopWidth +
el.clientHeight / 2;
}
if ((overLeft || overRight) && centerIfNeeded) {
parent.scrollLeft =
el.offsetLeft -
parent.offsetLeft -
parent.clientWidth / 2 -
parentBorderLeftWidth +
el.clientWidth / 2;
}
if ((overTop || overBottom || overLeft || overRight) && !centerIfNeeded) {
el.scrollIntoView(alignWithTop);
}
}