練習のためローカルストレージを使用し、お気に入り追加ボタンを押した記事をマイページに表示させたり、削除する機能の実装に挑戦しました。
こんにちは。はるです。
現在、所属している「もりけん塾」でJavaScriptのハンズオン課題 に取り組み、レビューをいただいています。
今日は、課題34 についてアウトプットをしていきます。
(前回までのアウトプットは、こちらです。)
実装する過程で学んだことを、学習ノートとして記録していきます。
認識の間違えている箇所がありましたら、お問い合わせからご指摘いただけるとうれしいです。
JavaScript課題34の仕様
(もりけんさんのJavaScriptハンズオン課題より)
JavaScript課題34の学習記録
制作物
ニュース個別ページを動的に表示する
個別ページ表示用に article.html を作成。
個別ページにリンクを貼る際に、/article.html?id=** のようにURLパラメータで記事固有のidを付与して、動的に記事が表示されるように実装しました。
URLパラメータのidから記事の情報を特定する
URLパラメータのidと、jsonデータのidが一致しているデータを返す関数を作成。
引数のdataにはfetchしたjsonデータが渡ってきます。
( fetchの一連のコードは前回の解説にあるので割愛 )
const urlParameter = Object.fromEntries(new URLSearchParams(window.location.search));
const getArticleData = data => {
const articles = data.map(item => item.articles);
let targetData;
articles.forEach(article => {
article.forEach(item => {
if(item.id === urlParameter.id) targetData = item;
});
});
return targetData;
}
特定したデータから記事内容を作成
タイトル、サムネイル、お気に入り追加ボタン、本文をjsonデータから取得して記事を作成します。
特に特記することがないのでそのまま掲載します。
const setTitle = data => document.querySelector('title').textContent = `${data.title}`;
const createCategoryLabel = data => {
const categoryLabel = createElementWithClassName('p', 'category');
const articleId = urlParameter.id;
categoryLabel.textContent = findCategoryById(data, articleId);
return categoryLabel;
}
const createArticleHead = data => {
const articleHead = createElementWithClassName('div', 'article__head');
const title = createElementWithClassName('h1', 'article__title');
const favoriteButton = createElementWithClassName('button', 'article__btn');
const favoriteImg = document.createElement('img');
title.textContent = data.title;
favoriteButton.type = "button";
favoriteButton.id = "js-favorite-button";
favoriteImg.src = '/assets/img/icon-star.png';
favoriteImg.alt = 'お気に入りに追加';
favoriteButton.appendChild(favoriteImg);
articleHead.appendChild(title).after(favoriteButton);
return articleHead;
};
const createArticleInfo = data => {
const articleInfo = createElementWithClassName('div', 'article__info js-article-info');
const date = createElementWithClassName('time', 'article__date');
date.textContent = `${data.date}`;
articleInfo.appendChild(date);
return articleInfo;
}
const createArticleContents = data => {
const articleContents = createElementWithClassName('p', 'article__text');
articleContents.textContent = `${data.content}`;
return articleContents;
}
const createThumbnail = data => {
const thumbnailWrapper = createElementWithClassName("picture", "article__thumbnail");
const thumbnailWebp = document.createElement("source");
const thumbnailJpg = document.createElement("img");
const noImgSrc = "/assets/img/no-img.jpg";
thumbnailJpg.alt = "";
thumbnailJpg.src = data.img || noImgSrc;
thumbnailWrapper.appendChild(thumbnailJpg);
if(data.webp){
thumbnailWebp.srcset = data.webp;
thumbnailWrapper.insertAdjacentElement("afterbegin", thumbnailWebp);
}
return thumbnailWrapper;
};
記事をrenderする
const renderArticle = data => {
const targetData = getArticleData(data);
const articleElement = document.getElementById('js-article');
setTitle(targetData);
articleElement.appendChild(createArticleHead(targetData)).after(createArticleInfo(targetData));
articleElement.appendChild(createArticleContents(targetData)).after(createThumbnail(targetData));
renderCategory(data);
}
ここまででURLパラメータのidから判別した個別記事が作成→renderされます。
htmlは1つですが、jsonデータにある16記事分のデータを動的に表示することができます。
お気に入りに追加する
お気に入りデータはローカルストレージに保存されるようにします。
※ローカルストレージは課題上、危険性を理解した上で練習のために使用しています。
登録済みのお気に入りデータを取得する
JSON.parse(json)を使用することでJSON文字列をオブジェクトに変換することができます。
引数に入った値がjson形式ではなかった場合は、例外が投げられます。そのため、try…catchで例外処理をするのが安全ということを知りました。
また、実際のアプリケーションでJSONを扱うのは、外部のプログラムとデータを交換する用途がほとんどです。 外部のプログラムが送ってくるデータが常にJSONとして正しい保証はありません。 そのため、
JSON.parse
メソッドは基本的にtry...catch
構文で例外処理をするべきです。
(js-primer)
該当コードです。jsonパースした結果をリターンしています。
const getRegisteredFavoriteData = () => {
let registeredFavoriteData = null;
try {
registeredFavoriteData = JSON.parse(localStorage.getItem('registeredFavoriteData'));
} catch (error) {
console.log(`jsonパースでエラーが発生しました: ${error}`);
displayInfo(articleWrapper,'エラーが発生しました');
}
return registeredFavoriteData;
}
新しいお気に入りデータの作成
引数には、お気に入りボタンを押した個別記事のデータが渡ってきます。
記事のidやタイトルなど必要な情報をオブジェクトに格納します。
const createFavoriteData = data => {
const favoriteData = {
'id': data.id,
'date': data.date,
'title': data.title,
'img': data.img,
'webp': data.webp
}
先ほどのjsonパースした登録ずみお気に入りデータがあれば、それに新しいデータを追加。
登録されたデータがなければ、favoriteDataをそのまま返すようにします。
const registeredFavoriteData = getRegisteredFavoriteData();
const newFavoriteData = registeredFavoriteData !== null ? [...registeredFavoriteData, favoriteData] : [favoriteData];
return newFavoriteData;
}
ローカルストレージに新しいデータを保存
最後に、localStrage.setItemを使って、createFavoriteData
()
で作成した新しいお気に入りデータを保存します。
const saveArticleData = data => {
const targetData = getArticleData(data);
localStorage.setItem("registeredFavoriteData", JSON.stringify(createFavoriteData(targetData)));
}
お気に入り追加ボタンを押すと実行
スターの色が変わり、記事のデータがローカルストレージに保存されます。
const addEventListenerForFavoriteButton = data => {
const favoriteButton = document.getElementById('js-favorite-button');
favoriteButton.addEventListener('click', (e) => {
if(e.target.disabled) return;
changeButtonDisabled(e.target);
saveArticleData(data);
})
}
お気に入り一覧ページ
マイページに移動すると、ローカルストレージに保存データがあるか確認して、あれば一覧として表示します。
const getFavoriteData = () => {
let favoriteData = null;
try {
favoriteData = JSON.parse(localStorage.getItem('registeredFavoriteData'));
} catch (error) {
console.log(`jsonパースでエラーが発生しました: ${error}`);
displayInfo(articleWrapper,'エラーが発生しました');
}
return favoriteData;
};
const init = () => {
const favoriteData = getFavoriteData();
if (favoriteData.length === 0) {
displayInfo(articleWrapper,'お気に入り記事がありません。');
return;
}
renderArticleList(favoriteData);
addEventListenerForRemoveFavoriteButton();
};
お気に入りデータがない場合には、「お気に入り記事がありません」というテキストを出力します。
お気に入り削除
お気に入り削除ボタンを押すと、ターゲットの記事カード(ローカルストレージのデータも一緒に)を削除します。
const addEventListenerForRemoveFavoriteButton = () => {
const articleList = document.querySelector(".js-article-list");
articleList.addEventListener("click", (e) => {
deleteArticleData(e.target);
if (getFavoriteData().length === 0) {
displayInfo(articleWrapper,'お気に入り記事がありません。');
}
});
};
お気に入り削除ボタンを押すと、e.targetの情報がdeleteArticleData関数に渡ります。
const getArticleId = target => {
const targetArticle = target.closest(".js-article");
const targetArticleLink = targetArticle.querySelector(".js-article-link").href;
const regex = /id=([^&]+)/;
const articleID = targetArticleLink.match(regex)[1];
return articleID;
}
targetArticleLink.match(regex)[1];
const deleteArticleData = target => {
let registeredFavoriteData = getFavoriteData();
const targetIndex = registeredFavoriteData.findIndex(item => item.id === getArticleId(target));
registeredFavoriteData.splice(targetIndex, 1);
localStorage.setItem("registeredFavoriteData", JSON.stringify(registeredFavoriteData));
target.closest(".js-article").remove();
}
このidと、ローカルストレージに保存されているデータのidが一致するものを検索します。
今回は、一致するローカルストレージのデータのIndexを調べました。
const targetIndex = registeredFavoriteData.findIndex(item => item.id === getArticleId(target));
ローカルストレージのデータ配列を新しいものにするため、splice(start, deleteCount);
を使用しました。
spliceは、配列の要素を取り除く・追加する・変更するなどの配列操作を行えるメソッドです。
splice(start, deleteCount)で
start位置からdeleteCountの数だけ削除します。
registeredFavoriteData.splice(targetIndex, 1);
お気に入り削除機能の挙動
コード
lesson34のリンクから実際の挙動を確認できます。
Sign up → Login → ニュース記事一覧へリンク
article.jsとmypage.jsが主なコードです。
//
学習に使用している本は、JavaScript本格入門・独習JavaScriptです。
あとがき
もりけんさん(@terrace_tec)、さえさん(@sae_prog)、takakoさん(@wattwattwatt) 、hasegawaさん(@starsurferz)お忙しい中レビューや動作確認をいただきありがとうございました!
今回も、全8回の細かい粒度に分けてのPRに挑戦しました。
最初にしっかり実装の計画を立てる必要があるので、その練習になったと思います。
教えていただいたことを生かして、次の課題に進みたいと思います!
今日は以上です。
//
【もりけん塾で勉強しています】
もりけんさん(@terrace_tec)のHPはこちら