htmx/www/docs.md
2020-05-11 11:42:54 -07:00

18 KiB

layout title
layout.njk </> kutty - high power tools for html

Kutty in a Nutshell

Kutty is a library that allows you to access modern browser features directly from HTML, rather than using javascript.

To understand kutty, first lets take a look at an anchor tag:

  <a href="/blog">Blog</a>

This anchor tag tells a browser:

"When a user clicks on this link, issue an HTTP GET request to '/blog' and load the response content into the browser window".

With that in mind, consider the following bit of HTML:

  <div kt-post="/clicked"
       kt-trigger="clicked"
       kt-target="#parent-div"
       kt-swap="outerHTML">
    Click Me!
  </div>

This tells kutty:

"When a user clicks on this div, issue an HTTP POST request to '/clicked' and use the content from the response to replace the element with the id parent-div in the DOM"

Kutty extends and generalizes the core idea of HTML as a hypertext, opening up many more possibilities directly within the language:

  • Now any element, not just anchors and forms, can issue a HTTP request
  • Now any event, not just clicks or form submissions, can trigger requests
  • Now any HTTP verb, not just GET and POST, can be used
  • Now any element, not just the entire window, can be the target for update by the request

Note that when you are using kutty, on the server side you respond with HTML, not JSON. This keeps you firmly within the original web programming model, using Hypertext As The Engine Of Application State without even needing to really understand that concept.

It's worth mentioning that, if you prefer, you can use the data- prefix when using kutty:

  <a data-kt-post="/click">Click Me!</a>

Installing

Kutty is a dependency-free javascript library. It can be used via NPM as "kutty.org" or downloaded or included from unpkg or your other favorite NPM-based CDN:

    <script src="https://unpkg.com/kutty.org@0.0.1"></script>

AJAX

The primary feature of kutty is attributes that allow you to issue AJAX requests directly from HTML:

  • kt-get - Issues a GET request to the given URL
  • kt-post - Issues a POST request to the given URL
  • kt-put - Issues a PUT request to the given URL (see details)
  • kt-patch - Issues a PATCH request to the given URL (see details)
  • kt-delete - Issues a GET request to the given URL (see details)

Each of these attributes takes a URL to issue an AJAX request to. The element will issue a request of the specified type to the given URL when the element is triggered:

  <div kt-put="/messages">
    Put To Messages
  </div>

This tells the browser:

When a user clicks on this div, PUT to the URL /messages and load the response into the div

Triggering Requests

By default, AJAX requests are triggered by the "natural" event of an element:

  • input, textarea & select: the change event
  • form: the submit event
  • everything else: the click event

If you don't want the request to happen on the default event, you can use the kt-trigger attribute to specify the event of interest. Here is a div that posts to /mouse_entered when a mouse enters it:

   <div kt-post="/mouse_entered" kt-trigger="mouseenter">
      [Here Mouse, Mouse!]
   </div>

If you want a request to only happen once, you can use the once modifier for the trigger:

   <div kt-post="/mouse_entered" kt-trigger="mouseenter once"">
     [Here Mouse, Mouse!]
   </div>

There are few other modifiers you can use for trigger:

  • changed - only issue a request if the value of the element has changed
  • delay:<time interval> - wait the given amount of time (e.g. 1s) before issuing the request. If the event triggers again, the countdown is reset.

You can use these two attributes to implement a common UX pattern, Live Search:

   <input type="text" name="q" 
          kt-get="/trigger_delay" 
          kt-trigger="keyup changed delay:500ms" 
          kt-target="#search-results" 
          placeholder="Search..."/>
    <div id="search-results"></div>

This input will issue a request 500 milliseconds after a key up event if the input has been changed and puts the results into the div#search-results.

Special Events

kutty provides a few special events for use in kt-trigger:

  • load - fires once when the element is first loaded
  • revealed - fires once when an element first scrolls into the viewport

You can also use custom events to trigger requests if you have an advanced use case.

Polling

If you want an element to poll the given URL rather than wait for an event, you can use the every syntax with ik-trigger:

  <div kt-get="/news" trigger="every 2s">
  </div>

This tells kutty

Every 2 seconds, issue a GET to /news and load the response into the div

Server Sent Events

Server Sent Events are a way for servers to send events to browsers. It provides a higher-level mechanism for communication between the server and the browser than websockets.

If you want an element to respond to a Server Sent Event via kutty, you need to do two things:

  1. Define an SSE source. To do this, add a kt-sse-src attribute on a parent element that specifies the URL from which Server Sent Events will be received.

  2. Specify the Server Sent Event that will trigger the element, with the prefix sse:

Here is an example:

    <body kt-sse-src="/sse_messages">
        <div trigger="sse:new_news" kt-get="/news"></div>
    </body>

Depending on your implementation, this may be more efficient than the polling example above since the server would notify the div if there was new news to get, rather than the steady requests that a poll causes.

Request Indicators

When an AJAX request is issued it is often good to let the user know that something is happening, since the browser will not give them any feedback. You can accomplish this in kutty by using kutty-indicator class:

By default the kutty-request class will be put on the element issuing the request. When paired with the kutty-indicator class this will end up showing the element with the kutty-indicator class on it during the request:

  <button kt-get="/click">
      Click Me!
     <img class="kutty-indicator" src="/spinner.gif"/>
  </button>

The kutty-indicator class uses opacity to hide and show the progress indicator. If you would prefer another mechanism you can create your own CSS transition like so:

    .kutty-indicator{
        display:none;
    }
    .kutty-request .my-indicator{
        display:inline;
    }
    .kutty-request.my-indicator{
        display:inline;
    }

If you want the kutty-request class added to a different element, you can use the kt-indicator attribute with a CSS selector to do so:

  <div>
      <button kt-get="/click" kt-indicator="#indicator">
        Click Me!
      </button>
      <img id="indicator" class="kutty-indicator" src="/spinner.gif"/>  
  </div>

Targets

If you want the response to be loaded into a different element other than the one that made the request, you can use the kt-target attribute, which takes a CSS selector. Looking back at our Live Search example:

   <input type="text" name="q" 
          kt-get="/trigger_delay" 
          kt-trigger="keyup delay:500ms changed" 
          kt-target="#search-results"
          placeholder="Search..."/>
    <div id="search-results"></div>

You can see that the results from the search are going to be loaded into div#search-results, rather than into the input tag.

Swapping

kutty offers a few different ways to swap the HTML returned into the DOM. By default, the content replaces the innerHTML of the target element. You can modify this by using the kt-swap attribute with any of the following values:

  • innerHTML - the default, puts the content inside the target element
  • outerHTML - replaces the entire target element with the returned content
  • afterbegin - prepends the content before the first child inside the target
  • beforebegin - prepends the content before the target in the targets parent element
  • beforeend - appends the content after the last child inside the target
  • afterend - appends the content after the target in the targets parent element

Out of Band Swaps

If you want to swap content from a response directly into the DOM by using the id attribute you can use the kt-swap-oob attribute in the response html:

  <div id="message" kt-swap-oob="true">Swap me directly!</div>
  Additional Content

In this response, div#message would be swapped directly into the matching DOM element, while the additional content would be swapped into the target in the normal manner.

You can use this technique to "piggy-back" updates on other requests.

Note that out of band elements must be in the top level of the response, and not children of the top level elements.

Selecting Content To Swap

If you want to select a subset of the response HTML to swap into the target, you can use the kt-select attribute, which takes a CSS selector and selects the matching elements from the response.

Forms & Input Values

By default, an element will include its value if it has one. Additionally, if the element is in a form, all values in the form will be included in the request.

If you wish to include the values of other elements, you can use the kt-include attribute with a CSS selector of all the elements whose values you want to include in the request.

Finally, if you want to programatically modify the arguments, you can use the values.kutty event to do so.

History Support

kutty provides a simple mechanism for interacting with the browser history API:

If you want a given element to push its request URL into the browser navigation bar and add the current state of the page to the browser's history, include the kt-push attribute:

    <a kt-get="/Blog" kt-push="true">Blog</a>

When a user clicks on this link, kutty will snapshot the current DOM and store it before it makes a request to /blog. It then does the swap and pushes a new location onto the history stack.

When a user hits the back button, kutty will retrieve the old content from storage and swap it back into the target, simulating "going back" to the previous state.

Specifying History Snapshot Element

By default, kutty will use the body to take and restore the history snapshop from. This is usually the right thing, but if you want to use a narrower element for snapshotting you can use the kt-history-element attribute to specify a different one.

Careful: this element will need to be on all pages or restoring from history won't work reliably.

Requests & Responses

Kutty expects responses to the AJAX requests it makes to be HTML, typically HTML fragments (although a full HTML document, matched with a kt-select tag can be useful too). Kutty will then swap the returned HTML into the document at the target specified and with the swap strategy specified.

Sometimes you might want to do nothing in the swap, but still perhaps trigger a client side event (see below). For this situation you can return a 204 - No Content response code, and kutty will ignore the content of the response.

In the event of an error response from the server (e.g. a 404 or a 501), kutty will trigger the responseError.kutty event, which you can handle. In the event of a connection error, the sendError.kutty will be triggered.

Request Headers

kutty includes a number of useful headers in requests:

  • X-KT-Request - will be set to "true"
  • X-KT-Trigger-Id - will be set to the id of the element that triggered the request
  • X-KT-Trigger-Name - will be set to the name of the element that triggered the request
  • X-KT-Target-Id - will be set to the id of the target element
  • X-KT-Current-URL - will be set to the URL of the browser
  • X-KT-Prompt - will be set to the value entered by the user when prompted via kt-prompt
  • X-KT-Event-Target - the id of the original target of the event that triggered the request
  • X-KT-Active-Element - the id of the current active element
  • X-KT-Active-Element-Value - the value of the current active element

Response Headers

kutty supports two special response headers:

  • X-KT-Trigger - can be used to trigger client side events, see the documentation for examples.
  • X-KT-Push - can be used to push a new URL into the browsers address bar

Request Order of Operations

The order of operations in a kutty request are:

  • The element is triggered and begins a request
    • Values are gathered for the request
    • The kutty-request class is applied to the appropriate elements
    • The request is then issued asynchronously via AJAX
      • Upon getting a response the target element is marked with the kutty-swapping class
      • An optional swap delay is applied (see the kt-swap-delay attribute)
      • The actual content swap is done
        • the kutty-swapping class is removed from the target
        • the kutty-settling class is applied to the target
        • A settle delay is done (default: 100ms)
        • The DOM is settled
        • the kutty-settling class is removed from the target

You can use the kutty-swapping and kutty-settling classes to create CSS transitions between pages.

Miscellaneous Attributes

In addition to the core AJAX functionality, kutty also has a few other tricks up its sleeve that help you build nice interfaces without javascript.

Class Swapping

Kutty supports an attribute, kt-classes that allows you to add, remove and toggle classes after a delay. This can be used to create CSS transition effects.

Here are some examples:

<div kt-classes="add foo"/> <!-- adds the class "foo" after 100ms -->
<div kt-classes="remove bar:1s"/> <!-- removes the class "bar" after 1s -->
<div kt-classes="remove bar:1s, add foo:1s"/> <!-- removes the class "bar" after 1s
                                               then adds the class "foo" 1s after that -->
<div kt-classes="remove bar:1s & add foo:1s"/> <!-- removes the class "bar" and adds 
                                                    class "foo" after 1s  -->
<div kt-classes="toggle foo:1s"/> <!-- toggles the class "foo" every 1s -->

Full documentation is available on the documentation page.

Boosting

Kutty supports "boosting" regular HTML anchors and forms with the kt-boost attribute. This attribute will convert all anchor tags and forms into AJAX requests that, by default, target the body of the page. This is similar to Turbolinks, but isn't quite as magical and is more configurable.

Events & Logging

Kutty has an extensive events mechanism, which doubles as the logging system.

If you want to register for a given kutty event you can use the following javascript:

  kutty.on("load.kutty", function(evt) {
        myJavascriptLib.init(evt.details.elt);  
  });

This event is fired every time an element is loaded into the DOM by kutty, and is effectively the load event. In fact this is so common, you can use the helper function:

  kutty.onLoad(function(target) {
        myJavascriptLib.init(target);  
  });

This does the same thing as the first example, but is a little cleaner.

The full set of events can be seen on the reference page.

Logging

If you set a logger at kutty.logger, every event will be logged. This can be very useful for troubleshooting:

    kutty.logger = function(elt, event, data) {
        if(console) {
            console.log(event, elt, data);
        }
    }

Kutty can also send errors to a URL that is specified with the kt-error-url attributes. If this attribute is set on a parent element all events that have the word Error in their name will be sent to the given URL as a JSON POST. This can be useful for debugging client-side issues.

Conclusion

And that's it! Have fun with kutty: you can accomplish quite a bit without a lot of code.