script js:thを自動生成しtrを並び替え可能にするscript 作成日: 2026年11月7日
要件としてはタイトルの通りで、あとは他にtableが同ページにあった場合同じようにされても困るのでtableに指定の属性がある場合とした。
こういったものは出尽くした感もある、今更だが作ったのでおの備忘。
イメージとしてはこんな感じ。

(function () {'use strict';
// 対象は class="row-drag" を持つテーブルのみvar TABLE_SELECTOR = 'table.row-drag';
function injectStyle() {if (document.getElementById('row-sort-style')) return;var st = document.createElement('style');st.id = 'row-sort-style';st.textContent ='table.row-drag tr th{width:22px;}' +'table.row-drag .row-drag-handle{width:22px;min-width:22px;padding:0;cursor:grab;touch-action:none;' +'user-select:none;-webkit-user-select:none;background-color:#e5e7eb;' +'background-image:linear-gradient(#6b7280,#6b7280),linear-gradient(#6b7280,#6b7280),linear-gradient(#6b7280,#6b7280);' +'background-size:12px 2px;background-repeat:no-repeat;' +'background-position:center calc(50% - 4px),center 50%,center calc(50% + 4px);}' +'table.row-drag .row-drag-handle:hover{background-color:#d1d5db;}' +'table.row-drag tr.is-dragging{opacity:.85;outline:2px solid #2563eb;outline-offset:-2px;box-shadow:0 2px 8px rgba(0,0,0,.25);}' +'body.row-sorting,body.row-sorting *{cursor:grabbing !important;user-select:none !important;-webkit-user-select:none !important;}';document.head.appendChild(st);}
function initTable(table) {if (table.dataset.rowSortInit === '1') return;if (table.rows.length < 2) return;table.dataset.rowSortInit = '1';
var headRow = table.rows[0];
Array.prototype.slice.call(table.rows, 1).forEach(function (tr) {var th = document.createElement('th');th.className = 'row-drag-handle';th.title = 'ドラッグして並べ替え';tr.insertBefore(th, tr.firstChild);th.addEventListener('pointerdown', function (e) {startDrag(e, tr, headRow);});});}
function startDrag(e, tr, headRow) {if (e.button !== undefined && e.button !== 0) return;e.preventDefault();
tr.classList.add('is-dragging');document.body.classList.add('row-sorting');
function onMove(ev) {var parent = tr.parentNode;var siblings = Array.prototype.filter.call(parent.rows, function (r) {return r !== tr && r !== headRow;});var target = null;for (var i = 0; i < siblings.length; i++) {var rect = siblings[i].getBoundingClientRect();if (ev.clientY < rect.top + rect.height / 2) {target = siblings[i];break;}}if (target) {if (tr.nextElementSibling !== target) parent.insertBefore(tr, target);} else if (parent.lastElementChild !== tr) {parent.appendChild(tr);}}
function onEnd() {tr.classList.remove('is-dragging');document.body.classList.remove('row-sorting');document.removeEventListener('pointermove', onMove);document.removeEventListener('pointerup', onEnd);document.removeEventListener('pointercancel', onEnd);}
document.addEventListener('pointermove', onMove);document.addEventListener('pointerup', onEnd);document.addEventListener('pointercancel', onEnd);}
function init() {injectStyle();Array.prototype.forEach.call(document.querySelectorAll(TABLE_SELECTOR), initTable);}
window.initRowSort = init;
if (document.readyState === 'loading') {document.addEventListener('DOMContentLoaded', init);} else {init();}})();