Pages

2026/02/26

ないなら作ったらいいじゃない?Bloggerで簡単調整ランダムポスト表示

2タイプから選べるランダムポスト紹介

ブログは書けば書くほど厚みが出ます。ただし厚みが出るということは、同時に埋もれる記事も増えるということです。検索流入は特定の記事に集中し、読者は目的の記事だけを読んで離脱する。過去の記事は、悪くなくても静かに沈んでいきます。

関連記事表示という方法もあります。あれは合理的です。ただ、合理的すぎる。同じカテゴリの中で循環する構造になるため、ブログ全体を横断する導線にはなりにくい。

そこで、あえて“偶然”を入れてみます。意図的に関連づけるのではなく、無作為に再提示する。ランダムポストはそのための仕組みです。

とはいえ、よくあるランダム表示はそのままでは使いづらい。デザインが合わない、画像が崩れる、色が浮く。機能はあっても、最終的に外されるのはだいたいそのあたりです。

今回はそこを念頭にいれて設計してみました。

今回参考にしたコード : 
なんかいウェブ研究所|Google Blogger で過去の投稿をランダムに表示する方法

ランダムポスト matsusan Ver.|仕様

  • 抽出範囲: ブログ公開時から現在までの**全記事(最大999件)**を対象に、読み込みのたびにシャッフル。
  • ターゲット: サイドバー(幅300px〜350px前後)に最適化。
  • カスタマイズ: URL、画像サイズ、スニペットの有無を冒頭の設定値で変更可能。

ランダムポスト matsusan Ver.|タイプ別比較

項目 リスト型 オーバーレイ型
表示内容 左:画像 / 右:タイトル 画像フル + タイトル下
表示の軽さ 軽量 やや重め
視覚的訴求 控えめ 強め
カスタマイズ性 サムネイルサイズ・アス比・スニペットの有無・各種色設定等 表示件数・グラデーション強度等

ランダムポスト matsusan Ver.|コード

リスト型 オーバーレイ型

<div id="random-posts-list-final"></div>

<style>
/* ==========================================
   USER CUSTOMIZE: ここでデザインを調整
   ========================================== */
:root {
  --rp-title-color: #333333;    /* タイトルの色 */
  --rp-summary-color: #777777;  /* 要約の色 */
  --rp-border-color: #f0f0f0;   /* 区切り線の色 */
  --rp-hover-bg: #fafafa;       /* ホバー時の背景色 */
  --rp-item-gap: 15px;          /* 記事同士の上下間隔 */
  --rp-img-radius: 6px;         /* 画像の角丸 */
}

#random-posts-list-final {
  width: 100%;
  max-width: 350px;
  margin: 0 auto;
  font-family: sans-serif;
}

#random-posts-list-final a {
  display: flex;
  align-items: flex-start;
  gap: 15px;
  padding: var(--rp-item-gap) 0;
  border-bottom: 1px solid var(--rp-border-color);
  text-decoration: none;
  transition: background-color 0.2s;
}

#random-posts-list-final a:last-child { border-bottom: none; }
#random-posts-list-final a:hover { background-color: var(--rp-hover-bg); }

#random-posts-list-final img {
  flex-shrink: 0;
  object-fit: cover;
  border-radius: var(--rp-img-radius);
  background-color: #eee;
}

.rp-text-wrap { flex: 1; overflow: hidden; }

.rp-title {
  display: block;
  color: var(--rp-title-color) !important;
  font-size: 14px;
  font-weight: bold;
  line-height: 1.4;
  margin-bottom: 4px;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.rp-summary {
  display: block;
  color: var(--rp-summary-color) !important;
  font-size: 12px;
  line-height: 1.4;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
</style>

<script>
//<![CDATA[
(function() {
  /* ==========================================
      CONFIG: JS設定エリア
     ========================================== */
  const CONFIG = {
    blogURL: 'https://サブドメイン.blogspot.com/', // 自分のURLに変更
    postCount: 4,      // 表示件数
    imgWidth: 80,      // 画像の横幅(px)
    isSquare: true,    // true: 正方形 / false: ワイド(16:9)
    showSnippet: true, // true: 要約を表示 / false: タイトルのみ
    snippetLength: 60  // ★ここで好きな文字数(キャラクター数)に変更可能
  };

  const domain = CONFIG.blogURL.replace(/^https?:\/\//, '').split('/')[0];
  const container = document.getElementById('random-posts-list-final');

  window.callback_list_final = function(data) {
    if (!data || !data.feed || !data.feed.entry) {
      container.innerHTML = '<p style="font-size:12px;color:red;">記事が見つかりませんでした。</p>';
      return;
    }

    const entries = data.feed.entry;
    const count = Math.min(entries.length, CONFIG.postCount);
    const picked = [];

    while (picked.length < count) {
      const r = Math.floor(Math.random() * entries.length);
      if (!picked.includes(r)) picked.push(r);
    }

    let html = '';
    for (let i = 0; i < count; i++) {
      const entry = entries[picked[i]];
      const title = entry.title.$t;
      const link = entry.link.find(l => l.rel === 'alternate').href;
      
      let img = 'https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPDYu4ibLWCVdJdTwQa2zoBViIfND-ZB0Y9g8IU1Csk_7AoRwVi1efzTdGFKjaiXh9LPWyCllES9iKlhik8b2G2liUUd8oeAA4NOVflZO3VqPtxhzuUteSAIGCRkyw2Ps8R5FjyFd1FzhmgPYCeAUGBM2qx3Z-lXUGUft6xgiKGUORq3Uz2ULqbSZFEsQ/s1600/NoImage.png';
      if (entry.media$thumbnail) {
        const sVal = `s${CONFIG.imgWidth * 2}-c`; 
        img = entry.media$thumbnail.url.replace(/\/s[0-9]+(-c)?/, '/' + sVal);
      }

      const imgHeight = CONFIG.isSquare ? CONFIG.imgWidth : Math.round(CONFIG.imgWidth * 9 / 16);

      let summaryHtml = '';
      if (CONFIG.showSnippet) {
        const summary = (entry.summary ? entry.summary.$t : (entry.content ? entry.content.$t : ''))
          .replace(/<[^>]*>?/gm, '') // HTMLタグを除去
          .substring(0, CONFIG.snippetLength); // ★設定した文字数で切り取り
        summaryHtml = `<span class="rp-summary">${summary}...</span>`;
      }

      html += `
        <a href="${link}">
          <img src="${img}" style="width:${CONFIG.imgWidth}px; height:${imgHeight}px;" alt="">
          <div class="rp-text-wrap">
            <span class="rp-title">${title}</span>
            ${summaryHtml}
          </div>
        </a>`;
    }
    container.innerHTML = html;
  };

  const script = document.createElement('script');
  script.src = `https://${domain}/feeds/posts/default?alt=json-in-script&callback=callback_list_final&max-results=999&t=${new Date().getTime()}`;
  document.body.appendChild(script);
})();
//]]>
</script>
            

<div id="random-posts-overlay-final"></div>

<style>
/* ==========================================
   USER CUSTOMIZE: ここでデザインを調整
   ========================================== */
:root {
  --rp-ov-title-color: #ffffff;    /* タイトルの色 */
  --rp-ov-gap: 15px;               /* 記事と記事の間隔 */
  --rp-ov-radius: 8px;             /* 角丸の大きさ */
  --rp-ov-aspect: 16 / 9;          /* 画像のアスペクト比 16:9のまま推奨*/
  --rp-ov-overlay-strength: 0.8;   /* 下部の黒グラデーションの強さ(0〜1) */
}

#random-posts-overlay-final {
  width: 100%;
  max-width: 350px;
  margin: 0 auto;
  font-family: sans-serif;
}

#random-posts-overlay-final a {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: var(--rp-ov-aspect);
  margin-bottom: var(--rp-ov-gap);
  border-radius: var(--rp-ov-radius);
  overflow: hidden;
  text-decoration: none;
}

#random-posts-overlay-final a:last-child { margin-bottom: 0; }

#random-posts-overlay-final img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.5s ease;
  background-color: #eee;
}

#random-posts-overlay-final a:hover img { transform: scale(1.1); }

.rp-ov-content {
  position: absolute;
  bottom: 0; left: 0; right: 0;
  padding: 25px 15px 12px;
  background: linear-gradient(transparent, rgba(0,0,0,var(--rp-ov-overlay-strength)));
  color: var(--rp-ov-title-color) !important;
}

.rp-ov-title {
  display: block;
  font-size: 14px;
  font-weight: bold;
  line-height: 1.4;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-shadow: 1px 1px 3px rgba(0,0,0,0.5);
}
</style>

<script>
//<![CDATA[
(function() {
  /* ==========================================
     CONFIG: JS設定エリア
     ========================================== */
  const CONFIG_A = {
    blogURL: 'https://サブドメイン.blogspot.com/', // 自分のブログURL
    postCount: 3                                 // 表示件数
  };

  // URLからドメインを抽出して整形(二重httpsや末尾スラッシュを防止)
  const domain = CONFIG_A.blogURL.replace(/^https?:\/\//, '').split('/')[0];
  const container = document.getElementById('random-posts-overlay-final');

  window.callback_overlay_final = function(data) {
    if (!data || !data.feed || !data.feed.entry) {
      container.innerHTML = '<p style="font-size:12px;color:red;">記事が見つかりませんでした。</p>';
      return;
    }

    const entries = data.feed.entry;
    const count = Math.min(entries.length, CONFIG_A.postCount);
    const picked = [];

    while (picked.length < count) {
      const r = Math.floor(Math.random() * entries.length);
      if (!picked.includes(r)) picked.push(r);
    }

    let html = '';
    for (let i = 0; i < count; i++) {
      const entry = entries[picked[i]];
      const title = entry.title.$t;
      const link = entry.link.find(l => l.rel === 'alternate').href;
      
      let img = 'https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPDYu4ibLWCVdJdTwQa2zoBViIfND-ZB0Y9g8IU1Csk_7AoRwVi1efzTdGFKjaiXh9LPWyCllES9iKlhik8b2G2liUUd8oeAA4NOVflZO3VqPtxhzuUteSAIGCRkyw2Ps8R5FjyFd1FzhmgPYCeAUGBM2qx3Z-lXUGUft6xgiKGUORq3Uz2ULqbSZFEsQ/s1600/NoImage.png';
      if (entry.media$thumbnail) {
        // 全幅用に大きめの画像をリクエスト
        img = entry.media$thumbnail.url.replace(/\/s[0-9]+(-c)?/, '/w600');
      }

      html += `
        <a href="${link}">
          <img src="${img}" alt="">
          <div class="rp-ov-content">
            <span class="rp-ov-title">${title}</span>
          </div>
        </a>`;
    }
    container.innerHTML = html;
  };

  const scriptA = document.createElement('script');
  // タイムスタンプ(t=)を付与してキャッシュを回避しつつフィード取得
  scriptA.src = `https://${domain}/feeds/posts/default?alt=json-in-script&callback=callback_overlay_final&max-results=999&t=${new Date().getTime()}`;
  document.body.appendChild(scriptA);
})();
//]]>
</script>
            

ランダムポスト matsusan Ver.|カスタマイズ方法

画像は object-fit: cover; を前提とし、比率だけを変更できる構造にしています。テーマに合わせて正方形にも横長にも変更可能です。

色はCSS変数で管理しています。アクセントカラーを一か所変更するだけで全体に反映されます。テーマを壊さずに馴染ませることを優先しています。

派手な機能ではありません。ですが、積み上げた記事を再び循環させるという意味では、地味ですが、効果があります。

blogURL: 'https://***.blogspot.com/',
→ // 自分のURLに変更

postCount: 4, 
→  // 表示件数

imgWidth: 80,
→  // 画像の横幅(px)

isSquare: true,
→  // true: 正方形 / false: ワイド(16:9)

showSnippet: true,
→ // true: 要約を表示 / false: タイトルのみ

snippetLength: 60
→ // ★ここで好きな文字数(キャラクター数)に変更可能

--rp-title-color: #333333;
→ タイトルの色 

--rp-summary-color: #777777;
→ 要約の色

--rp-border-color: #f0f0f0;
→ 区切り線の色

--rp-hover-bg: #fafafa;
→ ホバー時の背景色

--rp-item-gap: 15px; 
→ 記事同士の上下間隔 |オーバーレイ型

--rp-img-radius: 6px; 
→ 画像の角丸|オーバーレイ型

--rp-ov-gap: 15px;
→ 記事と記事の間隔|オーバーレイ型

--rp-ov-radius: 8px; 
→ 角丸の大きさ |オーバーレイ型

--rp-ov-overlay-strength: 0.8;   
→ 下部の黒グラデーションの強さ (0〜1)|オーバーレイ型


こんな使い方も

Bloggerのガジェットを<b:if cond='data:blog.url == data:blog.homepageUrl'>などの条件式を使ってどのページに表示させるか分岐させることができます。

これを利用して「記事以外 (トップページなど) のページ」にはインパクトのあるオーバーレイ型を、「記事のページ」には可読体験を邪魔しないリスト型と使い分けることも可能です。

開発後記

参考と経緯について

先日、誤ってブログの記事をすべてバックアップなしで消去してしまいました。現在は、記憶に残っている内容と、メモしていたタイトルリストを頼りに、記事のサルベージ作業を行っています。

まつさん
すべてがなくなった瞬間に青ざめた。バックアップは基本!! 消えたら自己責任!!

その消えてしまった記事の中に「ランダムポスト紹介」があったため、あらためて書き直そうと思ったのがきっかけです。

ただ、あらためて書こうとした際に、LIMOSUKI|【Blogger】ランダムポストウィジェットの設置方法

その過程で、南海ウェブラボ の記事を見つけ、「見つけた!!」と喜んだのも束の間、そのコードがそのまま動作することはありませんでした。

その後は南海ウェブラボさんのコードの記述ミスや構造を再構築するといった作業をAIとともに行いました。結果として、設定の簡略化や表示分離などを加え、今回の形に落ち着いています。

特別なものではありませんが、少なくとも「自分が使いやすい形」にはなっています。

Bloggerで現在も使われているランダムポスト

Blogger向けのランダムポストウィジェットは、実はそれほど多くありません。

例えば、Limosuki|【Blogger】ランダムポストウィジェットの設置方法 のまとめ記事では4種類が紹介されていますが、そのうち1つは現在使用することができません。 

Blogger Widgets のランダムポストは機能が豊富な反面、設定項目が多く、どこを調整すればよいのか分かりづらい印象があります。 

Helplogger のウィジェットは長年利用されている定番ですが、カスタム項目がほとんどなく、デザインを変えるにはCSSの理解が必要です。

My Blogger Tricks のものは現在も利用可能ですが、環境によっては干渉が起こることがあり、設置場所によっては正常に動作しない場合があります。 

どれも一長一短です。

そこで考えたこと

もし自分があらためて設置するなら、こうあってほしいなと思いました。

  •  設置はできるだけ簡単
  •  最低限のCSSはすぐ変更できる
  •  スクリプトの機能は true / false で切り替えられる
  •  画像サイズを明示的に制御できる (これはLimosukiさんもおっしゃっていました)

初心者は「数値を少し変えるだけ」で使える。中級者以上は、ブラウザの検証ツールを使いながら自分好みに拡張できる余地を残す。

これが今回のランダムポストの設計思想につながっています。

0 件のコメント:

コメントを投稿