No.26

生活とか写真とか音楽とか?あと美味しいもの?

【2026年版】はてなブログにおける縦写真のサイズを調整する。

www.takchaso.com

8年前(!)にこんな記事を書きました。ブログで縦写真貼ると横幅最大になるから横写真と比べて存在感ありすぎてきつい!なので縦写真に photo-center という class を手動で付けて、CSS で横幅 70% + 中央揃えにしたった!という話。

見た目良くなるんだけど、縦写真1枚ずつclass指定するっていう地獄の作業があり、結果めんどくなって記事によっては普通に横幅100%で出してしまっていました。

こないだ年末年始の記事を書いてるときにまためんどくなって、そもそも縦が長い画像貼ったら勝手に小さくしてくれ!!と思ってchatGPTに頼んだら出てきたので共有します。めちゃくちゃ楽になった。

以下、chatGPTによる解説です。

やりたいこと

挙動を整理するとこう。

状況 表示
横写真 フル幅
縦写真(単体) 75%で中央寄せ
横並びの写真 そのまま(縮めない)
縦だけどフル幅にしたい フル幅

人間が考えなくていいように、
写真を貼るだけで自動で決まるのが理想。

仕組みの考え方

はてなブログでは画像は LazyLoad され、DOMも動的に変わる。
そのため naturalWidth / naturalHeight での判定は安定しない。

そこで、

  • 実際の表示サイズ(getBoundingClientRect()
  • 画面に入ったタイミング(IntersectionObserver
  • 横並び領域(.images-row

を組み合わせて
「この画像は単体の縦写真か?」を判断して .photo-center を付与する。

フッタHTMLに入れるJS

管理画面
デザイン → カスタマイズ → フッタHTML に以下を貼る。

<script>
(() => {
  const CONTENT_SELECTOR = ".entry-content, .entry-content-inner, .entry-body, .entry-body-text, article";
  const GRID_SELECTOR =
    ".images-row, .containerv2, .containerv3, .grid-container, .cal3-grid-container, .cal4-grid-container";

  const processed = new WeakSet();

  function isInGrid(img) {
    return !!img.closest(GRID_SELECTOR);
  }

  function getTargets() {
    const roots = document.querySelectorAll(CONTENT_SELECTOR);
    const imgs = [];
    roots.forEach(root => root.querySelectorAll("img").forEach(img => imgs.push(img)));
    return imgs;
  }

  function markManual() {
    getTargets().forEach(img => {
      if (img.classList.contains("photo-center")) img.dataset.manualCenter = "1";
    });
  }

  function decide(img) {
    if (!img || processed.has(img)) return;

    if (img.classList.contains("photo-full")) {
      img.classList.remove("photo-center");
      processed.add(img);
      return;
    }

    if (isInGrid(img)) {
      img.classList.remove("photo-center");
      processed.add(img);
      return;
    }

    if (img.dataset.manualCenter === "1") {
      processed.add(img);
      return;
    }

    const rect = img.getBoundingClientRect();
    if (rect.width === 0 || rect.height === 0) return;

    if (rect.height > rect.width) img.classList.add("photo-center");
    processed.add(img);
  }

  function init() {
    markManual();
    getTargets().forEach(decide);

    const io = new IntersectionObserver(entries => {
      entries.forEach(e => e.isIntersecting && decide(e.target));
    }, { rootMargin: "300px 0px" });

    getTargets().forEach(img => io.observe(img));

    const mo = new MutationObserver(() => {
      getTargets().forEach(img => {
        if (!processed.has(img)) {
          io.observe(img);
          if (img.getBoundingClientRect().width > 0) decide(img);
        }
      });
    });

    mo.observe(document.body, { childList: true, subtree: true });
  }

  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", init);
  } else {
    init();
  }
})();
</script>

CSS

/* 縦写真を少し小さく中央寄せ */
.entry-content img.photo-center,
.entry-content-inner img.photo-center,
.entry-body img.photo-center,
.entry-body-text img.photo-center,
article img.photo-center {
  display: block;
  margin: 1.5em auto;
  max-width: 75% !important;
  height: auto !important;
}

/* この1枚だけ主役にしたい時(縦でもフル幅) */
.entry-content img.photo-full,
.entry-content-inner img.photo-full,
.entry-body img.photo-full,
.entry-body-text img.photo-full,
article img.photo-full {
  width: 100% !important;
  max-width: 100% !important;
  margin: 1.5em auto !important;
  display: block !important;
}

/* figure に photo-full が付いた場合、中の画像をフル幅に */
.figure-image.photo-full img,
figure.photo-full img {
  width: 100% !important;
  max-width: 100% !important;
  height: auto !important;
  display: block !important;
  margin: 0 auto !important;
}

/* 横並び(.images-row)は縮小しない */
.images-row img.photo-center {
  width: 100% !important;
  max-width: 100% !important;
  margin: 0 !important;
}

使い方

普段は何もしない。
画像を貼るだけで自動的に:

  • 縦写真 → 75%
  • 横写真 → フル幅
  • 横並び → そのまま

「縦写真だけどフル幅にしたい」場合だけ、

<img class="hatena-fotolife photo-full" ...>

と、対象の画像に photo-full を追加する。

注意事項(他のテーマで使う場合)

この方法は、はてなブログの標準的なHTML構造を前提にしています。

具体的には、次のような構造を仮定しています。

  • 記事本文が
    .entry-content / .entry-body / article などの中にある
  • Fotolife画像が
    <figure class="figure-image"><img> という構造で出力される
  • 横並び画像が
    .images-row でラップされる

ほとんどの公式テーマ・一般的なカスタムテーマではこの条件を満たしますが、

  • テーマ側で画像を独自ラップしている
  • 記事本文のクラス名を大きく変えている
  • 独自のギャラリーUIを入れている

といった場合は、そのままでは効かないことがあります。

その場合は、JS内の次の部分を
自分のテーマのHTML構造に合わせて書き換えてください。

const CONTENT_SELECTOR = "...";
const GRID_SELECTOR = "...";

これを自分のテーマに合わせて調整すれば、 どのテーマでも同じ仕組みが使えます。

なぜこの方式が「テーマ依存しにくい」のか

この仕組みは、

  • クラス名や画像サイズの指定に依存せず
  • 実際にブラウザに描画された「表示サイズ」を基準に
  • JavaScriptで photo-center を付け
  • CSSで見た目を制御する

というレイヤ分離構造で作られています。

つまり、

テーマが変わっても
「画像がどう表示されているか」さえ取得できれば動く

という設計になっています。

一般的な「CSSだけで縦写真を調整する方法」は、

  • テーマのHTML構造が変わると壊れる
  • 画像がLazyLoadされると判定できない
  • 横並びやギャラリー表示と衝突しやすい

といった問題を抱えがちですが、

この方式では、

  • IntersectionObserverで画像が表示された瞬間を捕まえ
  • getBoundingClientRect() で実サイズを取得し
  • 横並びレイアウト(.images-row など)を除外

することで、
テーマが変わっても同じ振る舞いを再現できます。

CSSはあくまで

「photo-center という意味ラベルをどう見せるか」

だけを担当し、
ロジックはすべてJavaScript側に集約しています。

この分離があることで、

  • デザインテーマを変えても
  • CSSを書き換えるだけで済み
  • JSロジックはそのまま使い回せる

という状態になります。

ブログの見た目と、
写真の扱い方を疎結合に保つための仕組みです。

---chatGPTによる解説ここまで---

めちゃ楽です

縦写真をただ貼るだけ

縦の横並びのときは縮小しない

photo-fullをつけたらフル幅になる

JSとCSS貼るだけでできるので、ぜひ使ってみてください。

●投げ銭●

特に記事販売をするつもりはないのですが、面白かった!ためになった!良かった!投げ銭をしてあげよう!という方はよろしくおねがいします。ハリボーを買います。

「この続きはcodocで購入」になっていますが、特に続きはありません。投げ銭システムとして使用しています。

この続きはcodocで購入