htmx icon indicating copy to clipboard operation
htmx copied to clipboard

Any way to target local content like <a href="#fragment">Local Link</a> ?

Open JohanMollevikCap opened this issue 4 months ago • 4 comments

I am exploring htmx a bit and am liking what I see. One thing that I had hoped would work but did not was accessing content in the same page.

With pure HTML you can do a local link using Local Link and I was trying the following htmx code to see if I could do the same with htmx. That does not work however and instead of using the local page it goes out and does a GET request to the same URL as the current page. Can this GET request be avoided somehow?

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>HTMLX Test</title>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/htmx.min.js"
                integrity="sha384-Akqfrbj/HpNVo8k11SXBb6TlBWmXXlYQrCSqEWmyKJe+hDm3Z/B2WVG4smwBkRVm"
                crossorigin="anonymous"></script>
  </head>
  <body>
  <div id="#storage">
        <div id="#test">Test Test</div>
  </div>
  <div id="#content">
        <button hx-get="#storage" hx-select="#test" hx-swap="outerHTML">Replace this with the content under id=test</button>
  </div>
  </body>
</html>

I found this extension that had a different behavior in mind for this syntax https://github.com/TheDocTrier/hash-select

But this is not what I am trying to do, I am trying to avoid making requests in some cases.

Can this be done in some way, and would it be a good idea if there were some way to make this work?

JohanMollevikCap avatar Aug 20 '25 12:08 JohanMollevikCap

Yeah htmx does not have a great option out of the bat sorry as all htmx style requests are always ajax requests. You can create a htmx extension that can probably cover this local select use case. It is often good to use

I think a better idea is to make use of the browsers built in caching support as you can return a partial or full response that has some fairly static content you might want to swap in quickly later. If you set the right cache control headers in your response it will be cached in browser for a period of time and this allows multiple htmx requests with different hx-selects to query parts as required from this cached data which means the browser does all the work for you and you will not be hitting network requests for repeat small patches of static content. You can also use the pre-load htmx extension to improve the caching behavior if required.

MichaelWest22 avatar Aug 21 '25 02:08 MichaelWest22

Thanks, the mock-requests library looks interesting, I had not considered intercepting the requests that late in the process. If I where to create a htmx extension would you envission that using something like mock-requests to intercept the network calls or would a better design interdict the execution at some other point? I was looking a bit at how to get such an extension working but could not make sense of how to hook myself into the existing code to replace the network call with the results of a function call.

JohanMollevikCap avatar Aug 21 '25 08:08 JohanMollevikCap

(function() {
  let internalAPI = null;

  htmx.defineExtension('local-template', {
    init: function(api) {
      // Called once, receives `api` for internal use
      internalAPI = api;
    },
    onEvent: function(name, evt) {
    
      if (name === "htmx:beforeRequest") {
        console.log(evt.detail)
        var path = evt.detail.requestConfig?.path;

        if (!path) return;
        var template = document.getElementById(path);
        if (template && template.tagName === "TEMPLATE") {
          evt.preventDefault();
          var target = evt.detail.target;
          const swapSpec = internalAPI.getSwapSpecification(evt.detail.elt)
          internalAPI.swap(target, template.innerHTML, swapSpec);
        }
      }
    }
  });
})();

yeah I think something like this using htmx:beforeRequest as you can preventDefault to stop the request happening if you detect that it is a local one

MichaelWest22 avatar Aug 22 '25 01:08 MichaelWest22

Thanks, when I removed the tagName === "TEMPLATE" condition this did what I expected. If removing that is a good idea is a separate discussion. But thanks.

Is there some use to the community to put this in its own github repo for easier linking or should the existence of this discussion in the bug tracker be enough?

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>HTMLX Test</title>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/htmx.min.js"
        integrity="sha384-Akqfrbj/HpNVo8k11SXBb6TlBWmXXlYQrCSqEWmyKJe+hDm3Z/B2WVG4smwBkRVm"
        crossorigin="anonymous"></script>
    <script>
      (function() {
        let internalAPI = null;

        htmx.defineExtension('local-template', {
          init: function(api) {
            // Called once, receives `api` for internal use
            internalAPI = api;
          },
          onEvent: function(name, evt) {
            if (name === "htmx:beforeRequest") {
              console.log(evt.detail)
              var path = evt.detail.requestConfig?.path;

              if (!path) return;
              var template = document.getElementById(path);
              if (template) {
                evt.preventDefault();
                var target = evt.detail.target;
                const swapSpec = internalAPI.getSwapSpecification(evt.detail.elt)
                internalAPI.swap(target, template.innerHTML, swapSpec);
              }
            }
          }
        });
      })();
    </script>
  </head>
  <body hx-ext="local-template">
    <div id="#storage">
      <div id="#test">Test Test</div>
    </div>
    <div id="#content">
      <button hx-get="#storage" hx-select="#test" hx-swap="outerHTML">Replace this with the content under id=test</button>
    </div>
    <div id="#content">
      <button hx-get=".#storage" hx-select="#test" hx-swap="outerHTML">Replace this with the content under id=test</button>
    </div>
  </body>
</html>

JohanMollevikCap avatar Aug 22 '25 12:08 JohanMollevikCap