Skip to Main Content

Forcing Sprig/HTMX Components to Stop Polling

Posted on under Craft Development / Front End

Sprig has quickly become one of my favorite Craft CMS plugins. It reduces some of the friction and overhead that adding interactivity to a site usually brings. I'm currently using it in a client project that requires a fair amount of interactivity and ran into an interesting challenge along the way.

In this project, there is a table component similar to the Sprigboard demo that reloads automatically every 5 seconds via HTMX' polling trigger. Within each table row is another Sprig component - a button that, when clicked, opens a modal dialog that in turn loads a user's data. The trouble is that when the outer table component re-renders via polling, the modal component also re-renders, and poof goes the modal. First I tried using Sprig's s-preserve attribute, but this caused some other issues with my setup. I needed a way to programmatically tell the outer component to stop polling when the modal was opened.

The Normal Way

The standard way to stop a component from polling is to have it return a response code of 286. This is useful if the contents of the polled element's response determines when polling should stop, say a long-running API endpoint that should stop being polled when a specific value is returned. But in our case, it's a user event (disconnected from the polling element itself) that determines when the polling should stop.

The Programmatic Way

Under the hood, HTMX tracks the state and settings of components using an object htmx-internal-data

  
      function getInternalData(elt) {
  var dataProp = 'htmx-internal-data';
  var data = elt[dataProp];
  if (!data) {
    data = elt[dataProp] = {};
  }
  return data;
}  

When a status code of 286 is returned, the following method is triggered:

  
      function cancelPolling(elt) {
  getInternalData(elt).cancelled = true;
}  

Trouble is, neither of these methods are available on the global htmx object. But now that we know how things work under the hood, we can achieve the same thing.

  
      const pollingElement = document.querySelector('#sprig-component');

function disablePolling(el){
  el['htmx-internal-data'].cancelled = true;
  htmx.process(el);
}

disablePolling(pollingElement);  

The last line htmx.process() tells htmx to re-initialize the element with the updated properties. Now polling can be cancelled programmatically regardless of the component's response code.

To fire it back up, run the reverse code and refresh the component.

  
      function enablePolling(el){
  el['htmx-internal-data'].cancelled = false;
  htmx.process(el);
  htmx.trigger(el, 'refresh');
}