star back image
people4
電飾 電飾
moon
astronaut

【shopify】最近見た商品の表示 localStorage

BLOG shopifyWEBログ
読了約:65分

商品の写真をちらっと見て、買わないで去っていく人は数え切れないほどいっぱい。
でも、さっきまで見てくれてた商品を簡単に忘れてほしくないですよね。

可能性が少しでもあるなら、あわよくば買ってほしい。だから。思い出してもらうために(記憶させて)チラッとどこかで目に付くよう表示させたい。そういう要件はありますか?

amazonなどの大手サイトで見かけるアレですか。

「お客様が閲覧した商品」「閲覧履歴に基づくおすすめ商品」などなど。呼び方はさまざま。応用も沢山あると思いますが、今回はlocalStorageでシンプルに「いったん見た商品を記憶して、どこかで表示させる」という機能のみの実装です。

え?どうやってつくるんですか。さっぱりわかりません。

でもいまはAIがあります。

【共有】localStorageコード by shopify

Amazonでは堂々とトップページに表示されてますが、shopifyでは好きなところへ表示できるようにしたいです。

要件は以下です。

  • 商品画像、商品名、商品価格
  • Swiperを仕込みたい
  • SwiperはPCのとき4個、スマホは2個以上でスライド機能が発動
  • 最大10個までに制限の重複なし

1.商品詳細ページのファイルに保存のためのlocalStorageの記述を書き込みます。

main-product.liquid

  <script>
  if (window.location.pathname.match(/^\/products\//)) {
    var handle = window.location.pathname.split('/products/')[1].replace(/\/$/, '');
    var viewed = JSON.parse(localStorage.getItem('recently_viewed_handles') || '[]');
    viewed = viewed.filter(function(h) { return h !== handle; });
    viewed.unshift(handle);
    viewed = viewed.slice(0, 10);
    localStorage.setItem('recently_viewed_handles', JSON.stringify(viewed));
  }
  </script>

/products/のURLが商品ページなので、/products/商品ハンドルを保存します。

json形式で保存する記述です。
htmlコードの最後ら辺に追加したらいいと思います。

2.どこかのページに差し込めるよう、専用のリキッドファイルを新規作成します。

sections/recently-viewed.liquid

{% comment %}
  最近見た商品セクション
  - localStorageで商品ハンドルを管理
  - JSでAjax取得&スライダー表示
{% endcomment %}

<div id="recently-viewed-products-section" class="related-products">
  <h2 class="related-products__title">最近見た商品</h2>
  <div class="related-products__slider swiper js-slider">
    <div class="swiper-wrapper" id="recently-viewed-products-list">
      <!-- JSで商品カードをここに挿入 -->
    </div>
  </div>
  <div class="swiper-pagination js-slider-pagination"></div>
  <div class="swiper-button-prev js-slider-prev"></div>
  <div class="swiper-button-next js-slider-next"></div>
</div>

<script>
(function() {
  // 商品一覧ページ下部で表示
  document.addEventListener('DOMContentLoaded', function() {
    var container = document.getElementById('recently-viewed-products-list');
    if (!container) return;
    var handles = JSON.parse(localStorage.getItem('recently_viewed_handles') || '[]');
    console.log('閲覧履歴ハンドル:', handles);
    if (handles.length === 0) {
      document.getElementById('recently-viewed-products-section').style.display = 'none';
      return;
    }

    // Ajaxで商品データを取得
    var requests = handles.map(function(handle) {
      return fetch('/products/' + handle + '.js').then(function(res) { return res.json(); });
    });

    Promise.all(requests).then(function(products) {
      products.forEach(function(product) {
        var html = `
          <div class="swiper-slide">
            <a href="${product.url}" class="related-products__item">
              <div class="related-products__image">
                <img src="${product.featured_image}" alt="${product.title}">
              </div>
              <div class="related-products__info">
                <div class="related-products__title">${product.title}</div>
                <div class="related-products__price">¥${(product.price / 100).toLocaleString()}</div>
              </div>
            </a>
          </div>
        `;
        container.insertAdjacentHTML('beforeend', html);
      });

      // Swiper初期化
      if (typeof Swiper !== 'undefined') {
        new Swiper('.related-products__slider', {
          slidesPerView: 2,
          spaceBetween: 16,
          loop: false,
          navigation: {
            nextEl: '.js-slider-next',
            prevEl: '.js-slider-prev'
          },
          pagination: {
            el: '.js-slider-pagination',
            clickable: true
          },
          breakpoints: {
            768: {
              slidesPerView: 4
            }
          }
        });
      }
    });
  });
})();
</script>

{% schema %}
  {
    "name": "Recently Viewed",
    "tag": "section",
    "class": "section",
    "settings": [
      
    ],
    "presets": [
      {
        "name": "Recently Viewed"
      }
    ]
  }
{% endschema %}

localStorage からハンドル取得し、
重要 – Shopify Product JSON API での商品情報取得します。

Shopify Product JSON APIは公式のAPIです。
正確には「Shopify Ajax API」の一部として提供されています。

GET /products/{handle}.js

商品ID、タイトル、ハンドル
価格情報
在庫状況
画像URL配列
バリアント情報
タグ、カテゴリ
公開日時 などが取得可能です。

参考
https://shopify.dev/docs/api/ajax/reference/product

以上になります。

3.言語切り替えで英語に切り替わらない対応

console.logと言語切り替え、swiper用サンプルcss付き。
Swiperの読み込ませ忘れずに。

言語切り替えとかしてますか?
使用するテンプレートで多少変わるかもしれませんが参考程度に。

要件は以下です。

  • NEWなどのアイコンバッジ出したい
  • next/prevのボタン位置調整スクリプト付き。
  • schemaで、このリキッドのsectionのpadding調整機能付きです。
<!-- swiper -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@10/swiper-bundle.min.css" />
<script src="https://cdn.jsdelivr.net/npm/swiper@10/swiper-bundle.min.js"></script>
{% comment %}
  最近見た商品セクション
  - localStorageで商品ハンドルを管理
  - JSでAjax取得&スライダー表示
{% endcomment %}

{%- style -%}
.section-{{ section.id }}-padding {
  padding-top: {{ section.settings.padding_top }}px;
  padding-bottom: {{ section.settings.padding_bottom }}px;
}

.c-collection__item-variation{
  display: none;
}
.c-collection__item-new {
  font-family: "MS Pゴシック",sans-serif;
  font-weight: 700;
  font-size: 11px;
  line-height: 100%;
  letter-spacing: 0;
  color: #000A82;
  margin-bottom: 4px;
}
.c-collection__item-preorder {
  font-family: "MS Pゴシック",sans-serif;
  font-weight: 700;
  font-size: 11px;
  line-height: 100%;
  letter-spacing: 0;
  color: #000A82;
  margin-bottom: 4px;
}
.c-collection__item-soldout {
  font-family: "MS Pゴシック",sans-serif;
  font-weight: 700;
  font-size: 11px;
  line-height: 100%;
  letter-spacing: 0;
  color: #adadad;
  margin-bottom: 4px;
}

/* no-imageプレースホルダーのスタイル */
.no-image-placeholder {
  width: 100%;
  background: #f8f9fa;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #6c757d;
  font-size: 14px;
  text-align: center;
  aspect-ratio: 285/356;
}

.no-image-placeholder::before {
  content: "No Image";
  font-family: "MS Pゴシック",sans-serif;
  font-weight: 400;
}
.recently-viewed .swiper-button-next img,
.recently-viewed .swiper-button-prev img {
  opacity: 1;
  transition: opacity 0.3s;
}
@media(any-hover: hover) {
  .recently-viewed .swiper-button-next:hover img,
  .recently-viewed .swiper-button-prev:hover img {
    opacity: .5;
  }
}

@media screen and (min-width: 768px) {
  .l-section__inner.recently-viewed {
  }
  .recently-viewed .p-collection-slider__slider {
    margin-top: 25px;
  }
  .recently-viewed .c-slider-product .swiper-slide {
    width: calc((100% - 80px) / 5);
  }
  .recently-viewed .p-collection-slider__title {
    font-family: "MS Pゴシック",sans-serif;
    font-weight: 700;
    font-size: 24px;
    line-height: 101%;
    letter-spacing: 0.02em;
    color: #101010;
  }
  #recently-viewed-products-section .swiper-button-next, #recently-viewed-products-section .swiper-button-prev {
    top: calc(64% - var(--slider-text-height-r));
    width: 40px;
    height: 40px;
    background-color: #fff;
    border-radius: 50%;
    box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.1);
    z-index: 1;
  }
  .recently-viewed .swiper-button-next {
    right: -18px;
  }
  .recently-viewed .swiper-button-prev {
    left: -20px;
  }
  .recently-viewed .swiper-button-next:after,
  .recently-viewed .swiper-button-prev:after {
    display: none;
  }
  .recently-viewed .swiper-button-next img, .recently-viewed .swiper-button-prev img {
    width: 17px;
    height: 17px;
    position: relative;
    top: 1px;
  }
  .recently-viewed .swiper-button-prev img {
    transform: scale(-1, 1);
  }
}
@media screen and (max-width: 767px) {
  .section-{{ section.id }}-padding {
    padding-top: {{ section.settings.padding_top_sp }}px;
    padding-bottom: {{ section.settings.padding_bottom }}px;
  }
  .l-section__inner.recently-viewed {
    width: 100%;
  }
  .recently-viewed .p-collection-slider__slider {
    margin-top: 18px;
  }
  .recently-viewed .p-collection-slider__title {
    font-family: "MS Pゴシック",sans-serif;
    font-weight: 700;
    font-size: 20px;
    line-height: 100%;
    letter-spacing: 0.02em;
    color: #101010;
    margin-left: 20px;
  }
  .recently-viewed .c-collection {
    display: flex;
    grid-template-columns: 1fr 1fr;
    row-gap: unset;
    -webkit-column-gap: unset;
    -moz-column-gap: unset;
    column-gap: unset;
  }
  .recently-viewed .swiper-slide:first-child  {
    margin-left: 20px;
  }
  .recently-viewed .swiper-button-next, .recently-viewed .swiper-button-prev {
    top: 44%;
    width: 40px;
    height: 40px;
    background-color: #fff;
    border-radius: 50%;
    box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.1);
    z-index: 1;
  }
  .recently-viewed .swiper-button-next {
    right: 10px;
    display: none;
  }
  .recently-viewed .swiper-button-prev {
    display: none;
  }
  .recently-viewed .swiper-button-next:after,
  .recently-viewed .swiper-button-prev:after {
    display: none;
  }
  .recently-viewed .swiper-button-next img, .recently-viewed .swiper-button-prev img {
    width: 17px;
    height: 17px;
    position: relative;
    top: 1px;
  }
  .recently-viewed .swiper-button-prev img {
    transform: scale(-1, 1);
  }
  .c-collection__item-title{
    font-weight: 700;
    font-size: 12px;
    line-height: 114.99999999999999%;
    letter-spacing: 0;
  }
  .c-collection__item-price {
    font-weight: 700;
    font-size: 12px;
    line-height: 114.99999999999999%;
    letter-spacing: 0;
    color: #ADADAD;
  }
}
{%- endstyle -%}
<div class="section-{{ section.id }}-padding l-section p-collection-slider p-recently-viewed">
  <div class="l-section__inner p-collection-slider__inner recently-viewed get">

    <div id="recently-viewed-products-section">
      <h2 class="p-collection-slider__title u-font-en-h2 u-font-en-bold">{{ section.settings.title }}</h2>
      <div class="p-collection-slider__slider c-slider-product">
        <div class="p-collection-slider__container related-products__slider c-slider-product__slider swiper js-recently-viewed-slider">
          <div class="p-collection-slider__list c-collection swiper-wrapper" id="recently-viewed-products-list">
            <!-- JSで商品カードをここに挿入 -->
          </div>
        </div>
        <div class="swiper-button-prev js-recently-viewed-prev"><img class="icon" src="https://cdn.shopify.com/s/files/1/0936/4501/3274/files/icon_arrow_recently_viewed.svg?v=1748225734" width="17" height="17" alt="arrow prev"></div>
        <div class="swiper-button-next js-recently-viewed-next"><img class="icon" src="https://cdn.shopify.com/s/files/1/0936/4501/3274/files/icon_arrow_recently_viewed.svg?v=1748225734" width="17" height="17" alt="arrow next"></div>
      </div>
    </div>

  </div>
</div>

<script>
(function() {
  // 商品一覧ページ下部で表示
  document.addEventListener('DOMContentLoaded', function() {
    var container = document.getElementById('recently-viewed-products-list');
    if (!container) return;
    var handles = JSON.parse(localStorage.getItem('recently_viewed_handles') || '[]');
    //console.log('閲覧履歴ハンドル:', handles);
    if (handles.length === 0) {
      //document.getElementById('recently-viewed-products-section').style.display = 'none';
      // 未閲覧の時はセクション全体を非表示
      document.querySelector('.p-recently-viewed').closest('.shopify-section').style.display = 'none';
      return;
    }

    // 最大6件までに制限
    //handles = handles.slice(0, 6);

    // 現在の言語を取得(シンプル版)
    // var currentLanguage = document.documentElement.lang || 'ja';
    // var languageParam = currentLanguage === 'en' ? '?locale=en' : '';
    // 現在の言語を取得(複数方法で検出)
    var currentLanguage = 'ja'; // デフォルト
    
    // 方法1: URLパスから取得(最も確実)
    if (window.location.pathname.startsWith('/en/') || window.location.pathname.includes('/en/')) {
      currentLanguage = 'en';
    }
    // 方法2: HTMLのlang属性から取得
    else if (document.documentElement.lang) {
      currentLanguage = document.documentElement.lang;
    }
    // 方法3: Shopifyの言語設定から取得
    else if (window.Shopify && window.Shopify.locale) {
      currentLanguage = window.Shopify.locale;
    }
    
    var languageParam = currentLanguage === 'en' ? '?locale=en' : '';
    
    // 言語に応じたAPIエンドポイントを構築
    var baseUrl = currentLanguage === 'en' ? '/en/products/' : '/products/';
    
    // デバッグ用:言語情報をコンソールに出力
    // console.log('Current language:', currentLanguage);
    // console.log('Language parameter:', languageParam);
    // console.log('Base URL:', baseUrl);
    // console.log('HTML lang attribute:', document.documentElement.lang);
    // console.log('Current URL:', window.location.href);

    // Ajaxで商品データを取得
    var requests = handles.map(function(handle) {
      //return fetch('/products/' + handle + '.js').then(function(res) { return res.json(); });
      //return fetch('/products/' + handle + '.js')
      //return fetch('/products/' + handle + '.js' + languageParam)
      var requestUrl = currentLanguage === 'en' ? 
        baseUrl + handle + '.js' + languageParam : 
        '/products/' + handle + '.js' + languageParam;
      //console.log('Requesting product:', requestUrl); // デバッグ用
      return fetch(requestUrl)
        .then(function(res) { 
          if (!res.ok) {
            throw new Error('Product not found: ' + handle);
          }
          return res.json(); 
        })
        .catch(function(error) {
          //console.warn('Failed to fetch product:', handle, error);
          return null; // エラーの場合はnullを返す
        });
    });

    Promise.all(requests).then(function(products) {
      // nullの商品をフィルタリング
      products = products.filter(function(product) {
        return product !== null;
      });

      // デバッグ用:取得した商品データを確認
      // console.log('Fetched products:', products);
      // if (products.length > 0) {
      //   console.log('First product title:', products[0].title);
      //   console.log('First product handle:', products[0].handle);
      // }

      // 商品が1つも取得できなかった場合
      if (products.length === 0) {
        //console.log('No products found in recently viewed');
        return;
      }
      
      products.forEach(function(product) {
        // メイン画像
        var mainImage = product.featured_image;
        // hover画像(2枚目があれば)
        var hoverImage = product.images[1] ? product.images[1] : product.featured_image;
        // NEWバッジ(公開から31日以内なら)
        var publishedAt = new Date(product.published_at);
        var now = new Date();
        var daysSincePublish = (now - publishedAt) / (1000 * 60 * 60 * 24);
        var isNew = daysSincePublish <= 31;
        // 在庫状況
        var isSoldOut = !product.available;
        
        // 先行予約販売の判定
        var isPreOrder = false;
        if (product.tags && Array.isArray(product.tags)) {
          isPreOrder = product.tags.includes('先行予約販売');
        }

        // カラーサークル(option1がCOLORの場合のみ)
        // var colorCircles = '';
        // if (typeof product.options[0] === 'string' && product.options[0].toUpperCase() === 'COLOR') {
        //   var colors = [];
        //   product.variants.forEach(function(variant) {
        //     if (colors.indexOf(variant.option1) === -1) {
        //       colors.push(variant.option1);
        //       // 色コードは取得できないので仮で#aaa
        //       colorCircles += `<div class="c-collection__item-color js-product-variation-button" data-color-id="${variant.option1}" style="background-color:#aaa"></div>`;
        //     }
        //   });
        // }

        // 画像URLの検証とフォールバック
        var mainImageUrl = mainImage || '';
        var hoverImageUrl = hoverImage || mainImageUrl;
        
        // 1. URL検証(軽量)
        if (mainImageUrl.includes('no-image') || 
            mainImageUrl.includes('2048-a2addb12') || 
            mainImageUrl.includes('cdn/shopifycloud/storefront/assets/no-image') ||
            !mainImageUrl ||
            mainImageUrl === '') {
          mainImageUrl = '';
        }
        
        // ホバー画像の処理を改善
        if (hoverImageUrl.includes('no-image') || 
            hoverImageUrl.includes('2048-a2addb12') || 
            hoverImageUrl.includes('cdn/shopifycloud/storefront/assets/no-image') ||
            !hoverImageUrl ||
            hoverImageUrl === '') {
          // メイン画像がある場合はそれを使用、ない場合は空
          hoverImageUrl = mainImageUrl || '';
        }
        
        // ホバー画像がメイン画像と同じ場合は空にする(ホバー効果を無効化)
        if (hoverImageUrl === mainImageUrl) {
          hoverImageUrl = '';
        }
        
        // デバッグ用:画像URLを確認
        // console.log('Original main image:', mainImage);
        // console.log('Processed main image URL:', mainImageUrl);
        // console.log('Original hover image:', hoverImage);
        // console.log('Processed hover image URL:', hoverImageUrl);

        // バッジの表示判定
        var badgeHtml = '';
        if (isSoldOut) {
          badgeHtml = '<div class="c-collection__item-soldout">SOLD OUT</div>';
        } else if (isPreOrder) {
          // 言語に応じて表示を変更
          var preOrderText = currentLanguage === 'en' ? 'PRE-ORDER' : '先行予約販売';
          badgeHtml = '<div class="c-collection__item-preorder">' + preOrderText + '</div>';
        } else if (isNew) {
          badgeHtml = '<div class="c-collection__item-new">NEW</div>';
        }

        var html = `
          <div class="c-collection__item js-product-item swiper-slide">
            <a href="${product.url}">
              <div class="c-collection__item-media -clip-path">
                <div class="c-collection__item-image js-product-variation-image is-active" data-color-id="">

                  ${mainImageUrl ? `<img src="${mainImageUrl}" alt="${product.title}">` : '<div class="no-image-placeholder"></div>'}
                </div>
                <div class="c-collection__item-image c-collection__item-image--hover">

                  ${hoverImageUrl ? `<img src="${hoverImageUrl}" alt="">` : '<div class="no-image-placeholder"></div>'}
                </div>
              </div>
              <div class="c-collection__item-content">

                ${badgeHtml}
                <h3 class="c-collection__item-title">${product.title}</h3>
                <div class="c-collection__item-price">
                  <span class="price-item price-item--regular">¥${(product.price / 100).toLocaleString()}</span>
                </div>
              </div>
            </a>
            <div class="c-collection__item-variation">

            </div>
          </div>
        `;
        container.insertAdjacentHTML('beforeend', html);
      });

      // スライダーの初期化
      const recentlyViewedSlider = new Swiper('.js-recently-viewed-slider', {
        speed: 800,
        slidesPerView: 2.3, // SPのチラ見せ
        spaceBetween: 10,
        slidesOffsetAfter: 40,
        loop: false,
        navigation: {
          nextEl: '.js-recently-viewed-next',
          prevEl: '.js-recently-viewed-prev'
        },
        simulateTouch: true,
        touchRatio: 1,
        grabCursor: true,
        breakpoints: {
          768: {
            slidesPerView: 5,
            spaceBetween: 20,
            slidesOffsetAfter: 0
          }
        },
        on: {
          init: function() {
            // スライダー初期化後に高さを設定
            setTimeout(function() {
              getSliderTextHeightRecent();
            }, 100);
          }
        }
      });
    });
  });
})();

function getSliderTextHeightRecent() {
  const containerRecent = document.querySelector('#recently-viewed-products-section');

  if (containerRecent) {
    // 商品画像またはno-image-placeholderの高さと位置を取得
    const productImage = containerRecent.querySelector('.c-collection__item-image img, .c-collection__item-image .no-image-placeholder');
    const sliderContainer = containerRecent.querySelector('.p-collection-slider__slider');

    if (productImage && sliderContainer) {
      const imageHeight = productImage.offsetHeight;
      const imageTop = productImage.offsetTop;
      const sliderTop = sliderContainer.offsetTop;

      // 商品画像の中央位置を計算
      const imageCenter = imageTop + (imageHeight / 2);
      const sliderCenter = sliderTop + (sliderContainer.offsetHeight / 2);

      // 商品画像の中央とスライダーの中央の差分を計算
      const offset = sliderCenter - imageCenter;

      containerRecent.style.setProperty('--slider-text-height-r', offset + 'px');
      //console.log('Product image/placeholder height:', imageHeight);
      //console.log('Image center position:', imageCenter);
      //console.log('Slider center position:', sliderCenter);
      //console.log('Offset for button positioning:', offset);
    } else {
      // フォールバック: スライダー全体の高さを使用
      const textHeight = (containerRecent.offsetHeight + 12) / 2;
      containerRecent.style.setProperty('--slider-text-height-r', textHeight + 'px');
      //console.log('Fallback - Recent slider text height:', textHeight);
    }
  }
}

document.addEventListener('DOMContentLoaded', function() {
  // recentlyViewedSlider();
  // getSliderTextHeightRecent();
});
document.addEventListener('shopify:section:load', function() {
  // recentlyViewedSlider();
  // getSliderTextHeightRecent();
});

// 言語切り替え時の再読み込み
document.addEventListener('shopify:section:reorder', function() {
  // セクションが再読み込みされた時に最近見た商品を更新
  setTimeout(function() {
    var container = document.getElementById('recently-viewed-products-list');
    if (container) {
      container.innerHTML = ''; // 既存の商品をクリア
      // ページを再読み込みして商品を再取得
      location.reload();
    }
  }, 100);
});

// URL変更時の検出(言語切り替え時)
var currentUrl = window.location.href;
window.addEventListener('popstate', function() {
  if (currentUrl !== window.location.href) {
    currentUrl = window.location.href;
    // 言語が変更された場合、最近見た商品セクションを更新
    var container = document.getElementById('recently-viewed-products-list');
    if (container) {
      container.innerHTML = '';
      // 少し遅延させてから再読み込み
      setTimeout(function() {
        location.reload();
      }, 200);
    }
  }
});
</script>


{% schema %}
  {
    "name": "Recently Viewed",
    "tag": "section",
    "class": "section",
    "settings": [
    {
      "type": "range",
      "id": "padding_top",
      "min": 0,
      "max": 120,
      "step": 2,
      "unit": "px",
      "label": "t:sections.all.padding.padding_top",
      "default": 10
    },
    {
      "type": "range",
      "id": "padding_top_sp",
      "min": 0,
      "max": 120,
      "step": 2,
      "unit": "px",
      "label": "t:sections.all.padding.padding_top_sp",
      "default": 10
    },
    {
      "type": "range",
      "id": "padding_bottom",
      "min": 0,
      "max": 120,
      "step": 2,
      "unit": "px",
      "label": "t:sections.all.padding.padding_bottom",
      "default": 0
    },
    {
      "type": "text",
      "id": "title",
      "default": "Recently Viewed",
      "label": "タイトル"
    },
    ],
    "presets": [
      {
        "name": "最近見た商品"
      }
    ]
  }
{% endschema %}

詳しい解説はAIに放り込んでください。

342行目 mainImageUrl.includes('2048-a2addb12') ||

2048-a2addb12 ←はなんですか?

聞くとそれはno-image画像が環境によってshopifyが勝手に画像に付け加えるテキストで、確認して安全のために入れているらしいです。

// パターン1(一般的)
https://cdn.shopify.com/shopifycloud/shopify/assets/no-image-2048-a2addb12.gif

// パターン2
https://your-store.myshopify.com/cdn/shop/files/no-image-placeholder.png

// パターン3
https://cdn.shopifycloud/storefront/assets/no-image-2048-a2addb12.png

【確認方法】ローカルストレージを見たい

保存されている状況を見たくありませんか。
chromeのどこで見るのかメモします。

検証ツールの「アプリケーション」にあります。

ストレージ > ローカルストレージ > ドメインURL
と選ぶと、localStorageのキー名のrecently_viewed_handlesがあるのを確認できると思います。

chromeのローカルストレージの確認場所

これは商品を1件見た形跡のようですね。

Console で直接確認するスクリプトも教えてもらいました。

//またはJSONとして整形版で見たい時
JSON.parse(localStorage.getItem('recently_viewed_handles'))

↑違う商品を見たので、1つ増えています。
確認できると少し楽しいかも。

星間旅路のメロディ

「宇宙の静けさに包まれながら、漂流する過去の音楽を捜し求め、銀河の奥底でその旋律に耳を傾ける。」

「この電波はどこの星からきたのだろうか。」

水の豊かな惑星の歌。
生命も輝いていた、そんな雰囲気がします。