imacrayon / alpine-ajax

An Alpine.js plugin for building server-powered frontends.
https://alpine-ajax.js.org
MIT License
558 stars 11 forks source link

Uncaught (in promise) Missing Target: #[object HTMLButtonElement] was not found in the current document. #64

Closed hansaskov1 closed 5 months ago

hansaskov1 commented 5 months ago

I want to try out Alpine-ajax, but I can't get it to work. I have installed it by including it in the head of my HTML file. All seems to work until I interact with the page, and I get an error:

Uncaught (in promise) Missing Target: #[object HTMLButtonElement] was not found in the current document.
    E https://cdn.jsdelivr.net/npm/@imacrayon/alpine-ajax@0.5.0/dist/cdn.min.js:1
    j https://cdn.jsdelivr.net/npm/@imacrayon/alpine-ajax@0.5.0/dist/cdn.min.js:1
    j https://cdn.jsdelivr.net/npm/@imacrayon/alpine-ajax@0.5.0/dist/cdn.min.js:1
    r https://cdn.jsdelivr.net/npm/@imacrayon/alpine-ajax@0.5.0/dist/cdn.min.js:1
[cdn.min.js:1:8310](https://cdn.jsdelivr.net/npm/@imacrayon/alpine-ajax@0.5.0/dist/cdn.min.js)
    r https://cdn.jsdelivr.net/npm/@imacrayon/alpine-ajax@0.5.0/dist/cdn.min.js:1

Screenshot of the error: Screenshot from 2024-03-19 19-23-12

I tried to reproduce the toggle example but this error has stumped me.

I am using Bun and Elysia for serving my code and i made a repository to reproduce this error: https://github.com/hansaskov1/alpine-ajax-button

The entire source code is also here.

import { Elysia } from "elysia";
import { html } from "@elysiajs/html";

const Head = ({ children }: { children: undefined | {} }) => (
    <html lang="en">
        <head>
            <meta charset="UTF-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1.0" />
            <title>Document</title>

            {/* Alpine and Alpine Ajax */}
            <script defer src="https://cdn.jsdelivr.net/npm/@imacrayon/alpine-ajax@0.5.0/dist/cdn.min.js"></script>
            <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.11.1/dist/cdn.min.js"></script>

        </head>
        <body>
            {children}
        </body>
    </html>
);

const LikeButton = ({ id }: { id: string }) => (
    <form id="like" x-init x-target method="post" action={`/comments/${id}/like`}>
        <button name="id" value={id}>Like</button>
    </form>
)

const UnlikeButton = ({ id }: { id: string }) => (
    <form id="like" x-init x-target method="delete" action={`/comments/${id}/like`}>
        <button name="id" value={id} x-autofocus>Unlike</button>
    </form>
)

const app = new Elysia()
    .use(html())
    .get("/", () => (
        <Head>
            <LikeButton id={"1"} />
        </Head>
    ))
    .delete("/comments/:id/like", ({ params }) => (
        <LikeButton id={params.id} />
    ))
    .post("comments/:id/like", ({ params }) => (
        <UnlikeButton id={params.id} />
    ))

    .listen(3000);

console.log(
    `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
);

Here is the rendered html from the browser

<html lang="en" class=" mrcjhi idc0_350">
   <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
      <script defer="" src="https://cdn.jsdelivr.net/npm/@imacrayon/alpine-ajax@0.5.0/dist/cdn.min.js"></script><script defer="" src="https://cdn.jsdelivr.net/npm/alpinejs@3.11.1/dist/cdn.min.js"></script>
   </head>
   <body>
      <form id="like" x-init="" x-target="button" method="post" action="/comments/1/like">
          <button name="id" value="1">Like</button>
      </form>
   </body>
</html>

Could someone please help me understand and resolve this issue?

imacrayon commented 5 months ago

Hi, so there's something a bit off with that rendered HTML:

<form id="like" x-init="" x-target="button" method="post" action="/comments/1/like">

x-target can be either empty (x-target) or x-target="like" but,x-target="button" is the cause of the error. The template code you shared appears fine, I'm not sure where the value of button would be coming from.

hansaskov1 commented 5 months ago

I think i copy pasted the html from my browser incorrectly. The x-target is empty and not button. Sorry for the confusion.

Here is the actual html

<html lang="en" class=" dnvyzo idc0_350">
    <head><meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <script defer="" src="https://cdn.jsdelivr.net/npm/@imacrayon/alpine-ajax@0.5.0/dist/cdn.min.js"></script>
        <script defer="" src="https://cdn.jsdelivr.net/npm/alpinejs@3.11.1/dist/cdn.min.js"></script>
    </head>
    <body>
        <form id="like" x-init="" x-target="" method="post" action="/comments/1/like">
            <button name="id" value="1">Like</button>
        </form>
    </body>
</html>

Based on your answer i am assuming x-target="" should be x-target instead for it to target itself. But i'm not sure if a can configre my html render to not parse it like this

Here is a picture from the browser: Screenshot from 2024-03-19 20-40-16

imacrayon commented 5 months ago

Ah, I was able to track down the problem.

It's some browser DOM weirdness and some unsafe programming on my part. Since your submit button is named id. In JavaScript, document.querySelector('form').id will actually reference the <button> element inside the form instead of the id attribute on the form 😩. I should be using document.querySelector('form').getAttribute('id') instead to prevent that name conflict.

I'll try to get a patch out really soon, but in the meantime if you change that button name to something like name="comment_id" you should have things working.

hansaskov1 commented 5 months ago

Amazing! You're so fast. Thank you very much.

Quite a hilarious bug. Good thing you found it. This also explains why removing name="id" and value="1" from my buttons fixed the issue

imacrayon commented 5 months ago

Thank you for the thorough details!