Steamer Lane Studio技術備忘録Script

js:thを自動生成しtrを並び替え可能にするscript

script js:thを自動生成しtrを並び替え可能にするscript 作成日: 2026年11月7日

要件としてはタイトルの通りで、あとは他にtableが同ページにあった場合同じようにされても困るのでtableに指定の属性がある場合とした。
こういったものは出尽くした感もある、今更だが作ったのでおの備忘。

イメージとしてはこんな感じ。
js:thを自動生成しtrを並び替え可能にするscript

(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();}})();