Htmx hx-indicator with multiple elements on page

1.1k Views Asked by At

I'm showing a loading spinner on my page when triggering a htmx-request like so:

<div class="col-4 px-4dot5 py-3 bg-secondary-light">
    <label for="stellenangebotLink" class="form-label fs-5">Link</label>
    <div id="linkHelpBlock">E-Mail-Adresse oder Website-URL</div>
    <?= $this->include('components/_htmx_spinner') ?>
    <input
        hx-post="/medien/personal-kampagne/stellenangebot-modal/link"
        hx-trigger="keyup changed delay:300ms"
        hx-include="#stellenangebot_id"
        hx-target="#stellenangebotLinkWrapper"
        hx-indicator=".htmx-indicator"
        name="link"
        id="stellenangebotLink"
        class="form-control"
        aria-describedby="linkHelpBlock">
</div>

Spinner code:

<div id="spinner" class="spinner-border spinner-border-sm mx-2 htmx-indicator" role="status">
  <span class="visually-hidden">Loading...</span>
</div>

The problem is that i have multiple such elements on my page and i only want to show the closest spinner. Furthermore i want to reuse that spinner-fragment on other elements (no sequential Ids).

The hx-indicator page says:

The value of this attribute is a CSS query selector of the element or elements to apply the class to, or the keyword closest, followed by a CSS selector, which will find the closest ancestor element or itself, that matches the given CSS selector (e.g. closest tr);

But what is such a CSS selector? closest .htmx-indicator gives a javascript-error (htmx.min.js:1 Uncaught TypeError: Cannot read properties of null (reading 'htmx-internal-data')). closest div would be too broad.

So my question is: How can i select the closest spinner-fragment with hx-indicator?

1

There are 1 best solutions below

2
On BEST ANSWER

It looks like they "translate" closest .htmx-indicator into the method call .closest('.htmx-indicator') - but that alone won't help you here, to actually target the .htmx-indicator element. .closest() checks the element itself and all its ancestors for a match to the selector - but your .htmx-indicator is not an ancestor of the input element, so it won't be found (and that then in turn explains the JS error you got.)

You would need to traverse downwards in the DOM tree again to actually find that spinner element, after you found a common ancestor of both, something like .closest('div').querySelector('.htmx-indicator') - but it doesn't look they provide any "syntax" for that.

closest div would be too broad

Why? According to their documentation, whatever element you actually target, will get the htmx-request class added to it; and the stylesheet should already come with

.htmx-request .htmx-indicator{
    opacity:1
}
.htmx-request.htmx-indicator{
    opacity:1
}

So whether you manage to get the .htmx-indicator element itself getting the class htmx-request applied to it, or whether it gets set on the outermost div in your given example code, should not really matter - it should apply the opacity value to .htmx-indicator either way ...?

(And that ID there on <div id="spinner" should go, if you include that same code in multiple places on one page.)