I've been experimenting with building a website using the Fresh framework. My goal was to add a simple drop-down feature for a button within a navigation bar, but I'm struggling to figure out where to place the necessary code. I attempted creating a class inside the index.tsx
file and initializing it within the export default function Home()
, however, all I encountered was a ReferenceError
due to document
not being defined.
index.tsx
/** @jsx h */
import { h } from "preact";
import HeaderWithLogin from "../islands/HeaderWithLogin.tsx";
import Navbar from "../islands/Navbar.tsx";
class Dropdown {
private _targetElement: Element | null;
private _triggerElement: Element | null;
private _visible: boolean;
constructor(
targetElement: Element | null = null,
triggerElement: Element | null = null
) {
this._targetElement = targetElement;
this._triggerElement = triggerElement;
this._visible = false;
this._init();
}
_init() {
if (this._triggerElement) {
this._triggerElement.addEventListener("click", () => {
this.toggle();
});
}
}
_handleClickOutside(ev: MouseEvent) {
const clickedE = ev.target as Element;
if (
clickedE !== this._targetElement &&
!(this._targetElement as Element).contains(clickedE) &&
!this._triggerElement?.contains(clickedE) &&
this._visible
) {
this.hide();
}
document.body.removeEventListener(
"click",
this._handleClickOutside,
true
);
}
toggle() {
if (this._visible) {
this.hide();
document.body.removeEventListener(
"click",
this._handleClickOutside,
true
);
} else {
this.show();
}
}
show() {
this._targetElement?.classList.remove("hidden");
this._targetElement?.classList.add("block");
document.body.addEventListener("click", this._handleClickOutside, true);
this._visible = true;
}
hide() {
this._targetElement?.classList.remove("block");
this._targetElement?.classList.add("hidden");
this._visible = false;
}
}
function initDropdown() {
document
.querySelectorAll("[data-dropdown-toggle]")
.forEach((triggerElement) => {
const targetElement: Element | null = document.getElementById(
triggerElement.getAttribute("data-dropdown-toggle") as string
);
new Dropdown(targetElement, triggerElement);
});
}
export default function Home() {
if (document.readyState !== 'loading') {
initDropdown();
} else {
document.addEventListener('DOMContentLoaded', initDropdown);
}
return (
<body>
<Navbar></Navbar>
</body>
);
}
Navbar.tsx
/** @jsx h */
import { h } from "preact";
import { tw } from "@twind";
export default function Navbar() {
return (
<nav>
<ul>
<li>
<button data-dropdown-toggle="dropdownElement">Dropdown Element</button>
<div id="dropdownElement">
<ul>
<li>
<a href="/">Link To Other Page</a>
</li>
</ul>
</div>
</li>
<li>
<button>Button 1</button>
</li>
<li>
<a href="/">Button 2</a>
</li>
<li>
<a href="/">Button 3</a>
</li>
</ul>
<div>
<input type="text" id="search-navbar" class={tw`block p-2 pl-10 w-full text-gray-900 bg-gray-50 rounded-lg border border-gray-300 sm:text-sm focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500`} placeholder="Search..."></input>
</div>
</nav>
);
}
Error given:
An error occured during route handling or page rendering.
ReferenceError: document is not defined
at Object.Home (file:///.../routes/index.tsx:89:5)
at h (https://esm.sh/v87/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="d8a8aabdb9bbacf5aabdb6bcbdaaf5acb7f5abacaab1b6bf98edf6eaf6e8">[email protected]</a>/X-ZC9wcmVhY3RAMTAuOC4y/deno/preact-render-to-string.js:4:1003)
at h (https://esm.sh/v87/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="aadad8cfcbc9de87d8cfc4cecfd887dec587d9ded8c3c4cdea9f8498849a">[email protected]</a>/X-ZC9wcmVhY3RAMTAuOC4y/deno/preact-render-to-string.js:4:1103)
at h (https://esm.sh/v87/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="324240575351461f40575c5657401f465d1f4146405b5c5572071c001c02">[email protected]</a>/X-ZC9wcmVhY3RAMTAuOC4y/deno/preact-render-to-string.js:4:1103)
at h (https://esm.sh/v87/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="acdcdec9cdcfd881dec9c2c8c9de81d8c381dfd8dec5c2cbec99829e829c">[email protected]</a>/X-ZC9wcmVhY3RAMTAuOC4y/deno/preact-render-to-string.js:4:1103)
at h (https://esm.sh/v87/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="572725323634237a2532393332257a23387a2423253e3930176279657967">[email protected]</a>/X-ZC9wcmVhY3RAMTAuOC4y/deno/preact-render-to-string.js:4:1103)
at m (https://esm.sh/v87/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="4d3d3f282c2e39603f282329283f603922603e393f24232a0d78637f637d">[email protected]</a>/X-ZC9wcmVhY3RAMTAuOC4y/deno/preact-render-to-string.js:3:684)
at render (https://deno.land/x/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="8aecf8eff9e2cabba4baa4bb">[email protected]</a>/src/server/render.tsx:180:16)
at Object.render [as renderFn] (file:///.../main.ts:20:3)
at render (https://deno.land/x/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="9afce8ffe9f2daabb4aab4ab">[email protected]</a>/src/server/render.tsx:184:14)
To resolve this error, I believe that including the required code in a script
tag would suffice. However, I'm also interested in exploring alternative methods of incorporating code into pages. Is there a more efficient way to do so, or is the script
tag the most optimal solution?