← ateliercms
meica.ch/werkstatt/webdev/ateliercms/gallery-ordering

AtelierCMS: drag-to-reorder gallery without a JS framework

The admin needed to reorder exhibition photos. Previous behaviour: upload order was display order, which was fine until a client uploaded 12 photos out of sequence and asked me to fix it.

The HTML5 Drag and Drop API is underused. For a simple list of thumbnails it’s completely sufficient.

<div id="gallery-sort" data-gallery="winter-2026">
  {foreach $images as $img}
    <div class="sort-item" draggable="true" data-id="{$img.id}">
      <img src="{$img.thumb}" alt="">
    </div>
  {/foreach}
</div>
// ~40 lines, no dependencies
const list = document.getElementById('gallery-sort');
let dragged = null;

list.addEventListener('dragstart', e => { dragged = e.target.closest('.sort-item'); });
list.addEventListener('dragover',  e => {
  e.preventDefault();
  const target = e.target.closest('.sort-item');
  if (target && target !== dragged) {
    const rect = target.getBoundingClientRect();
    const after = e.clientY > rect.top + rect.height / 2;
    list.insertBefore(dragged, after ? target.nextSibling : target);
  }
});
list.addEventListener('dragend', () => {
  const order = [...list.querySelectorAll('.sort-item')].map(el => el.dataset.id);
  fetch('/admin/gallery-order', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ gallery: list.dataset.gallery, order }),
  });
});

Order persists to a _order.json sidecar in the gallery directory. The public template reads this file if it exists; falls back to filename sort if not.

The only gotcha: mobile. Touch events are not fired for draggable elements by default on iOS Safari. I added a touch-action polyfill for the admin, since the clients are all desktop users anyway. Not worth solving for now.