Saya menggantikan htmx dengan komponen web yang mudah

WBOY
Lepaskan: 2024-09-07 06:37:16
asal
452 orang telah melayarinya

I replaced htmx with a simple web component

(Kredit imej: https://www.maicar.com/GML/Ajax1.html)

Baru-baru ini saya berbual di Mastodon tentang cara saya menggunakan htmx untuk mencapai kejayaan yang besar, dan seseorang menyertakan sebutan saya yang mencabar saya mengenainya, dan bagaimana htmx sebenarnya merupakan pergantungan yang agak berat memandangkan saya menggunakannya. Mereka memaut saya ke siaran ini dan segala-galanya.

Pada mulanya, saya agak jengkel. Saya fikir saya telah melakukan kerja yang cukup baik untuk memastikan perkara yang ringan, dan htmx telah melayani saya dengan baik, tetapi kemudian saya memakai topi yang saya cuba pakai sepanjang masa ini apabila ia datang untuk mencipta semula cara saya melakukan pembangunan web : betulkah andaian saya? Bolehkah saya berbuat lebih baik?

Jadi saya meneruskan dan menggantikan keseluruhan penggunaan htmx saya dengan komponen web vanillajs yang kecil, 100 baris, yang akan saya sertakan dalam siaran ini secara keseluruhannya:

export class AjaxIt extends HTMLElement {
  constructor() {
    super();
    this.addEventListener("submit", this.#handleSubmit);
    this.addEventListener("click", this.#handleClick);
  }

  #handleSubmit(e: SubmitEvent) {
    const form = e.target as HTMLFormElement;
    if (form.parentElement !== this) return;
    e.preventDefault();
    const beforeEv = new CustomEvent("ajax-it:beforeRequest", {
      bubbles: true,
      composed: true,
      cancelable: true,
    });
    form.dispatchEvent(beforeEv);
    if (beforeEv.defaultPrevented) {
      return;
    }
    const data = new FormData(form);
    form.dispatchEvent(new CustomEvent("ajax-it:beforeSend", { bubbles: true, composed: true }));
    const action = (e.submitter as HTMLButtonElement | null)?.formAction || form.action;
    (async () => {
      try {
        const res = await fetch(action, {
          method: form.method || "POST",
          headers: {
            "Content-Type": "application/x-www-form-urlencoded",
            "Ajax-It": "true",
          },
          body: new URLSearchParams(data as unknown as Record<string, string>),
        });
        if (!res.ok) {
          throw new Error("request failed");
        }
        form.dispatchEvent(new CustomEvent("ajax-it:afterRequest", { bubbles: true, composed: true }));
        const text = await res.text();
        this.#injectReplacements(text, new URL(res.url).hash);
      } catch {
        form.dispatchEvent(new CustomEvent("ajax-it:requestFailed", { bubbles: true, composed: true }));
      }
    })();
  }

  #handleClick(e: MouseEvent) {
    const anchor = e.target as HTMLAnchorElement;
    if (anchor.tagName !== "A" || anchor.parentElement !== this) return;
    e.preventDefault();
    anchor.dispatchEvent(new CustomEvent("ajax-it:beforeRequest", { bubbles: true, composed: true }));
    anchor.dispatchEvent(new CustomEvent("ajax-it:beforeSend", { bubbles: true, composed: true }));
    (async () => {
      try {
        const res = await fetch(anchor.href, {
          method: "GET",
          headers: {
            "Ajax-It": "true",
          },
        });
        if (!res.ok) {
          throw new Error("request failed");
        }
        anchor.dispatchEvent(new CustomEvent("ajax-it:afterRequest", { bubbles: true, composed: true }));
        const text = await res.text();
        this.#injectReplacements(text, new URL(res.url).hash);
      } catch {
        anchor.dispatchEvent(new CustomEvent("ajax-it:requestFailed", { bubbles: true, composed: true }));
      }
    })();
  }

  #injectReplacements(html: string, hash: string) {
    setTimeout(() => {
      const div = document.createElement("div");
      div.innerHTML = html;
      const mainTargetConsumed = !!hash && !!div.querySelector(
        hash,
      );
      const elements = [...div.querySelectorAll("[id]") ?? []];
      for (const element of elements.reverse()) {
        // If we have a parent that's already going to replace us, don't bother,
        // it will be dragged in when we replace the ancestor.
        const parentWithID = element.parentElement?.closest("[id]");
        if (parentWithID && document.getElementById(parentWithID.id)) {
          continue;
        }
        document.getElementById(element.id)?.replaceWith(element);
      }
      if (mainTargetConsumed) return;
      if (hash) {
        document
          .querySelector(hash)
          ?.replaceWith(...div.childNodes || []);
      }
    });
  }
}
customElements.define("ajax-it", AjaxIt);
Salin selepas log masuk

Anda menggunakannya seperti ini:

<ajax-it>
  <form action="/some/url">
    <input name=name>
  </form>
</ajax-it>
Salin selepas log masuk

Dan itu sahaja! Sebarang elemen dengan id yang disertakan dalam respons akan diganti apabila respons itu kembali. Ia berfungsi untuk unsur juga!

Elemen berfungsi dua cara utama:

  1. Jika tindakan atau href anda termasuk cincang, elemen pada atau halaman semasa dengan padanan id cincang itu akan digantikan dengan kandungan keseluruhan respons.
  2. Jika html anda yang dikembalikan mengandungi elemen yang mempunyai ID sendiri, dan ID tersebut mempunyai padanan dalam dokumen semasa, elemen tersebut akan diganti dahulu (dan dikecualikan daripada penggantian "keseluruhan respons" di atas). Ini pada asasnya cara anda melakukan swap "di luar jalur" (aka hx-swap-oob).

Jadi, dengan beberapa html seperti ini:

<div id=extra-stuff></div>
<div id=user-list></div>

<ajax-it>
  <a href="/users/list#put-it-here">
    Get users
  </a>
</ajax-it>
Salin selepas log masuk

dan respons pelayan seperti ini:

<ul>
  <li>user 1
  <li>user 2
</ul>
Salin selepas log masuk

Anda akan mendapat:

Tetapi jika jawapan anda ialah:

<ul>
  <li>user 1
  <li>user 2
</ul>

Hello, I'm out-of-band

Salin selepas log masuk

anda mungkin akan mendapat:

Hello, I'm out-of-band

<ul> <li>user 1 <li>user 2 </ul> Get users
Salin selepas log masuk

...dengan id=bahan tambahan ditukar di luar jalur dan

sumber:dev.to
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan
Tentang kita Penafian Sitemap
Laman web PHP Cina:Latihan PHP dalam talian kebajikan awam,Bantu pelajar PHP berkembang dengan cepat!