
localStrageを使って「ピン留め機能」を実装してみた
仕事で「商品をピン留めして保存しておきたい、それを分かりやすい形でページの上部に表示しておきたい」という要件のものがあり、そちらで実装した機能を紹介します。
意外と「ピン留め機能」を検索しても中々ヒットしなかったので、同じような機能を実装したい方は是非参考になれば。
動きとしては、アイテムの中にあるピンボタンをクリック


するとアイコンが赤く変わりwebストレージ上に「ピン留めされた」という情報が残り、次回ページに訪れた際も「ピン留めされたアイテム」として表示される。
再度オンになっているピンをクリックすると、ピン留めが解除され元のアイテムの状態に戻る。
というシンプルなものです。
ユーザーのログイン情報などは使用せずという条件があったのでキャッシュ上に何かしらピン留めの「キー」として残しておくものが必要になります。
クッキー使用等いろいろ考えたんですが、保存期間が長く容量も多いlocalStorageの方が適していると思ったのでそちらを使用しました。
実際のコードになります。
<li class="item">
<a class="item__imageLink" href="###">
<img class="item__image" src="/###/###/###.png" alt="">
</a>
<div class="item__pinButton"></div>
</li>
実装したhtmlを一部抜粋したものです。
こちらのliタグがアイテムになるんですが、今回の実装にあたって少し特殊な仕様がありまして、それを説明するのはだいぶ難しいので軽くお伝えすると[href="###"、src="/###/###/###.png"]などの情報がjson形式で動的に入ってくるんですが、それらをjsで取得して適宜入れ込んでいく必要があり、何を「キー」として持たせるか結構悩みました、、
今回、画像の更新がない(ファイル名が変わらない)とのことだったので画像ファイル名をキーとして持たせることにしました。
(動的ではない場合は、各liに個別のdata属性を持たせる等の対応でいいと思います)
以下がjsになります。(jquery使用)
let pinBtn = $('.item__pinButton');
const STORAGE_KEY = "path";
var storagePath;
var storageData = [];
pinBtn.click(function() {
let pinItem = $(this).parent();
let imagePath = pinItem.find('.item__image').attr('src').replace(/.jpg|.png|.jpeg/g, '').split('/');
imagePath = imagePath[imagePath.length - 1];
// ピン留め
if(pinItem.hasAttr('data-key') !== true) {
pinItem.attr('data-key', imagePath);
let clone_pinning = pinItem.clone(true);
$('.-pinning-area').find('.rl-column1').append(clone_pinning);
$('.-pinning-area').find('.item').attr('class','item');
storagePath = imagePath;
storageData.push(storagePath);
localStorage.setItem(STORAGE_KEY, JSON.stringify(storageData));
let itemArea_item = $('.-itemList').find('.item');
itemArea_item.each(function(index, element){
let imagePath_item = $(element).find('.item__image').attr('src').replace(/.jpg|.png|.jpeg/g, '').split('/');
imagePath_item = imagePath_item[imagePath_item.length - 1];
if($(element).hasAttr('data-key') == false && imagePath == imagePath_item) {
$(element).attr('data-key', imagePath);
}
})
} else {
// ピン解除
$('.-pinning-area').find('.item').each(function(){
if ($(this).attr('data-key') == imagePath) {
let removePath = $(this).attr('data-key');
$('[data-key="'+removePath+'"]').removeAttr('data-key');
$(this).remove();
pinItem.removeAttr('data-key');
let idx = storageData.indexOf(imagePath);
if(idx >= 0){
storageData.splice(idx, 1);
localStorage.setItem(STORAGE_KEY, JSON.stringify(storageData));
}
} else if($(this).hasAttr('data-key') !== true) {
$(this).remove();
pinItem.removeAttr('data-key');
let idx = storageData.indexOf(imagePath);
if(idx >= 0){
storageData.splice(idx, 1);
localStorage.setItem(STORAGE_KEY, JSON.stringify(storageData));
}
}
});
pinItem.removeAttr('data-key');
let idx = storageData.indexOf(imagePath);
if(idx >= 0){
storageData.splice(idx, 1);
localStorage.setItem(STORAGE_KEY, JSON.stringify(storageData));
}
}
})
元から展開されている.-itemList内にある各アイテム.itemのピンをクリックすると、.-pinning-area内のulにピン留めされたものがクローンされます。
画像ファイル名のキーはアイテムもliタグにdata-keyとして保存されます。
そしてそのdata-keyを配列storageDataに格納して、localStorageへ保存しています。
if(localStorage.getItem(STORAGE_KEY)) {
storageData = JSON.parse(localStorage.getItem(STORAGE_KEY));
localStorage.removeItem(storagePath);
let items = $('.-itemList').find('.item');
items.each(function(index, element){
let imagePath = $(element).find('.item__image').attr('src').replace(/.jpg|.png|.jpeg/g, '').split('/');
imagePath = imagePath[imagePath.length - 1];
let idx = storageData.indexOf(imagePath);
if(idx >= 0){
let clone_pinning = $(element).clone(true).attr('data-key', imagePath);
$('.-pinning-area').find('.rl-column1').append(clone_pinning);
$('.-pinning-area').find('.item').attr('class','item');
$(element).attr('data-key', imagePath);
}
})
}
以上、localStorageを使用したピン留め実装の一部でした。
仕事上で使用したものだったので実際に作ったページ等はお見せすることはできないですが、何か参考になれば幸いです。