テーブルコンテナの「div要素」のクラス名「data-table-class」により、スクリプトからJSONファイルのコンテンツをテーブルに出力し、CSS 整形する。
テーブルコンテナのデータ要素「data-json-url」から JSON ファイル URL を読み取り、「data-table-class」からクラス名、スクリプトからセルに「行 – 列番号」クラス名を付与し、⚡テーブルスクロールスティッキーをスタイルする。
データを読み込み中…
<!-- コンテナクラス名 / ファイル URL / テーブルクラス名 を入力 -->
<div
class="json-table-loader"
data-json-url="/wp-content/themes/wp-test/assets/json/ohtani-hr-2026-sample.json"
data-table-class="table-scroll-sticky"
>
データを読み込み中...
</div>
(function() {
async function initJsonTables() {
const containers = document.querySelectorAll('.json-table-loader');
for (const container of containers) {
const url = container.dataset.jsonUrl;
if (!url) continue;
// URLのファイル名からクラス名を生成
const fileName = url.split('/').pop().split('.').shift();
const fileClassName = `${fileName}-json-table`;
// データ属性からカスタムクラス名を取得
const customClass = container.dataset.tableClass || '';
try {
const response = await fetch(url);
if (!response.ok) throw new Error('Network response was not ok');
const data = await response.json();
if (data && data.length > 0) {
// 両方のクラス名を渡す
renderTable(container, data, fileClassName, customClass);
}
} catch (error) {
console.error('Fetch error:', error);
container.textContent = 'エラー: データの読み込みに失敗しました。';
}
}
}
function renderTable(target, data, fileClassName, customClass) {
const table = document.createElement('table');
// //// テーブルにデフォルトクラス名を追加 //// //
const classList = ['table-base', 'th-td-border', fileClassName];
// カスタムクラス追加(空白区切りの複数指定可)
if (customClass) {
classList.push(...customClass.split(' '));
}
table.classList.add(...classList);
const keys = Object.keys(data[0]);
// thead生成
const thead = document.createElement('thead');
const hTr = document.createElement('tr');
hTr.classList.add('tr-head');
keys.forEach((key, i) => {
const th = document.createElement('th');
th.textContent = key;
th.classList.add(`th-th-${i + 1}`);
hTr.appendChild(th);
});
thead.appendChild(hTr);
table.appendChild(thead);
// tbody生成
const tbody = document.createElement('tbody');
data.forEach((item, rI) => {
const bTr = document.createElement('tr');
bTr.classList.add(`tr-${rI + 1}`);
keys.forEach((key, cI) => {
const td = document.createElement('td');
td.textContent = item[key];
td.classList.add(`tb-td-${rI + 1}-${cI + 1}`);
bTr.appendChild(td);
});
tbody.appendChild(bTr);
});
table.appendChild(tbody);
target.innerHTML = '';
target.appendChild(table);
/* 追加機能 */
/* 先頭 td PSポストシーズン(レギュラーシーズンカウント外)背景色 */
const targetCells1 = table.querySelectorAll('td[class^="tb-td-"][class$="-1"]'); /* tb-td-*-1 */
targetCells1.forEach(cell => {
if (cell.textContent.includes('PS')) {
cell.classList.add('bg-lightgoldenrodyellow');
}/* 追加機能終わり */
});
}
initJsonTables();
})();
table.ohtani-hr-2026-sample-json-table {
font-size: var(--wp--preset--font-size--step-0000);
white-space: nowrap;
thead th, tbody td {
padding: 0 0.3em;
text-align: center;
}
/*「tb-th-」で始まり「-列番号」で終わるクラス名を指定 */
td[class^="tb-td-"][class$="-3"],
td[class^="tb-td-"][class$="-9"],
td[class^="tb-td-"][class$="-13"],
td[class^="tb-td-"][class$="-21"] {
text-align: left;
}
td[class^="tb-td-"][class$="-1"] {
background-color: ivory;
padding: 0;
}
} /* /table.ohtani-hr-2025-json-table */
/* スクロールバースタイル */
::-webkit-scrollbar {
width: 8px;
height: 8px;
background-color: #eee;
border-radius: 20px;
}
::-webkit-scrollbar-thumb {
background-color: #ddd;
border-radius: 20px;
}
::-webkit-scrollbar-thumb:hover {
background-color: #ccc;
}
/*
* テーブルスティッキー
* table にクラス名 table-scroll-sticky、
* テーブルコンテナを親に relative、固定要素を子に sticky
* 固定要素の擬似要素に影を設定
*/
/* テーブルコンテナ */
div:has(> .table-scroll-sticky) {
overflow: auto;
position: relative;
z-index: 1;
}
/* th 固定解除 */
.table-scroll-sticky thead th {
position: sticky;
top: 0;
z-index: 1;
}
/* td 固定 */
.table-scroll-sticky tbody td:nth-child(3) {
position: sticky;
left: 0;
z-index: 2;
opacity: 0.9;
}
/* 固定 td 影 */
.table-scroll-sticky tbody td:nth-child(3)::before {
content: '';
position: absolute;
right: 0;
top: 0;
width: 0.5px;
height: 100%;
box-shadow: 0.1px 0 1px #000;
background-color: #ddd;
}