204 lines
6.5 KiB
JavaScript
204 lines
6.5 KiB
JavaScript
// エラー表示
|
|
function showError(text) {
|
|
const errorMsgElement = document.querySelector("#errorMsg");
|
|
const errorElement = document.querySelector("#error");
|
|
|
|
if (errorMsgElement) {
|
|
errorMsgElement.textContent = text;
|
|
}
|
|
|
|
if (errorElement) {
|
|
errorElement.style.display = "block";
|
|
setTimeout(() => {
|
|
errorElement.style.display = "none";
|
|
}, 5000);
|
|
}
|
|
}
|
|
|
|
document.addEventListener("DOMContentLoaded", async () => {
|
|
try {
|
|
// contents.json読み込み
|
|
const response = await fetch("/contents.json");
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
const articlesData = data.articles;
|
|
|
|
if (!Array.isArray(articlesData)) {
|
|
throw new Error("記事データが不正な形式です");
|
|
}
|
|
|
|
// #contents読み込み
|
|
const contentsContainer = document.querySelector("#contents");
|
|
|
|
if (!contentsContainer) {
|
|
throw new Error("#contentsが見つかりません");
|
|
}
|
|
|
|
contentsContainer.innerHTML = "";
|
|
|
|
// 記事一覧作成
|
|
articlesData.forEach(article => {
|
|
// ID取得
|
|
const articleElement = document.createElement("div");
|
|
articleElement.className = "items";
|
|
articleElement.id = article.id;
|
|
|
|
// 日付取得
|
|
const dateElement = document.createElement("div");
|
|
dateElement.className = "date";
|
|
dateElement.textContent = String(article.date);
|
|
|
|
// タイトル取得
|
|
const titleElement = document.createElement("h1");
|
|
titleElement.textContent = String(article.title);
|
|
|
|
// 説明取得
|
|
const descriptionElement = document.createElement("p");
|
|
descriptionElement.textContent = String(article.description);
|
|
|
|
// 親要素に追加
|
|
articleElement.appendChild(dateElement);
|
|
articleElement.appendChild(titleElement);
|
|
articleElement.appendChild(descriptionElement);
|
|
contentsContainer.appendChild(articleElement);
|
|
});
|
|
|
|
// URLパラメータから記事IDを取得
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const articleId = urlParams.get("article");
|
|
|
|
if (articleId) {
|
|
const article = articlesData.find(a => a && a.id === articleId);
|
|
if (article) {
|
|
showArticle(article);
|
|
} else {
|
|
showError("記事が存在しませんでした");
|
|
}
|
|
}
|
|
|
|
// 記事移動
|
|
contentsContainer.addEventListener("click", (e) => {
|
|
const itemElement = e.target.closest(".items");
|
|
if (itemElement && itemElement.id) {
|
|
const id = itemElement.id;
|
|
const article = articlesData.find(a => a && a.id === id);
|
|
|
|
if (article) {
|
|
showArticle(article);
|
|
try {
|
|
history.pushState({ articleId: id }, article.title, `?article=${id}`);
|
|
} catch (error) {
|
|
console.warn("History API操作に失敗しました:", error);
|
|
}
|
|
} else {
|
|
showError("記事が存在しませんでした");
|
|
}
|
|
}
|
|
});
|
|
|
|
// 検索
|
|
const searchBox = document.querySelector("#searchbox");
|
|
|
|
if (searchBox) {
|
|
searchBox.addEventListener("input", (e) => {
|
|
const searchText = (e.target.value || '').toLowerCase();
|
|
const items = document.querySelectorAll("#contents .items");
|
|
|
|
items.forEach(item => {
|
|
const titleElement = item.querySelector("h1");
|
|
const descriptionElement = item.querySelector("p");
|
|
|
|
if (titleElement && descriptionElement) {
|
|
const title = (titleElement.textContent || '').toLowerCase();
|
|
const description = (descriptionElement.textContent || '').toLowerCase();
|
|
|
|
if (title.includes(searchText) || description.includes(searchText)) {
|
|
item.style.display = "block";
|
|
} else {
|
|
item.style.display = "none";
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// 記事表示
|
|
async function showArticle(article) {
|
|
// #docs・#top取得
|
|
const docsElement = document.querySelector("#docs");
|
|
const topElement = document.querySelector("#top");
|
|
|
|
// データ存在確認
|
|
if (docsElement && topElement && article && article.title && article.date) {
|
|
// 記事div作成
|
|
const articleDiv = document.createElement("div");
|
|
articleDiv.className = "article";
|
|
|
|
// タイトル取得
|
|
const titleElement = document.createElement("h1");
|
|
titleElement.textContent = String(article.title);
|
|
|
|
// 日付取得
|
|
const dateElement = document.createElement("div");
|
|
dateElement.className = "date";
|
|
dateElement.textContent = String(article.date);
|
|
|
|
// 内容
|
|
const contentElement = document.createElement("div");
|
|
contentElement.className = "content";
|
|
|
|
// 記事内容
|
|
try {
|
|
// mdファイル読み込み
|
|
const response = await fetch(`/articles/${article.id}.md`);
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
const data = await response.text();
|
|
|
|
// パース
|
|
const content = data || '';
|
|
|
|
if (typeof marked !== 'undefined' && typeof marked.parse === 'function') {
|
|
contentElement.innerHTML = marked.parse(content);
|
|
} else {
|
|
contentElement.textContent = content;
|
|
}
|
|
} catch (error) {
|
|
console.error("Markdownパースエラー:", error);
|
|
contentElement.textContent = response || '';
|
|
}
|
|
|
|
// 親要素に追加
|
|
articleDiv.appendChild(titleElement);
|
|
articleDiv.appendChild(dateElement);
|
|
articleDiv.appendChild(contentElement);
|
|
|
|
// 記事表示
|
|
docsElement.innerHTML = "";
|
|
docsElement.appendChild(articleDiv);
|
|
docsElement.style.display = "block";
|
|
|
|
// 記事一覧非表示
|
|
const articleList = document.querySelectorAll(".articlelist");
|
|
|
|
for (let i = 0; i < articleList.length; i++) {
|
|
articleList[i].style.display = "none";
|
|
}
|
|
|
|
// 検索ボックス削除
|
|
document.querySelector(".right").remove();
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error("データの読み込みに失敗しました:", error);
|
|
showError("データの読み込みに失敗しました");
|
|
}
|
|
});
|