HTMLの空白行を削除するスクリプト
<!-- markdown-mode-on -->
## **概要**
今まで、気の向くままにブログを書いてきて、表題(h2, h3など)の前後に気分で空白行を入れたり、入れなかったりしていた。
その空白行の入れ方にも、`<br/>`タグであったり、`<p>`タグや`<div>`タグであったり、まちまちのやり方をしていた。
それを統一感のあるものにしようと考えた。
<a name="more"></a>
## 現状把握(空白行の書き方)
記事のHTMLにある空白行を削除するには、空白行の書き方を把握することが重要だ。それを列記してみる。
### 空白行の一般的な書き方
1. **`<br/>` タグの使用**
```html
<p>これは本文です。</p>
<br/>
<p>これは次の段落です。</p>
```
→ `<br/>` タグを削除すれば、空白行がなくなります。
2. **`<p>` タグの余白によるもの**
```html
<p>これは本文です。</p>
<p>これは次の段落です。</p>
```
→ 余分な `<p>` タグを削除したり、CSSで `margin` の調整をすることで改善できます。
3. **空白スペースや改行 (` `) の使用**
```html
<p>これは本文です。</p>
<p> </p>
<p>これは次の段落です。</p>
```
→ ` ` のある `<p>` タグを削除するか、CSSで `display: none;` にすればOK。
4. **空白の `div` タグの使用**
```html
<div style="height: 20px;"></div>
```
→ 不要な `div` タグを削除するか、CSS で `display: none;` を適用。
5. **CSSによる `margin` 設定**
```css
p {
margin-bottom: 20px;
}
```
→ `margin-bottom` の値を小さくするか `margin: 0;` に変更すると空白行が減ります。
## 空白行の削除方針
### 方法
1. **HTMLの整理(書き換え)** → 上記の不要なタグ (`<br/>`、余分な `<p>`、` ` など) を削除
1. **CSSの調整** → `margin` や `padding` の値を適切に設定する
1. **JavaScriptで動的に削除**(大量の空白を削除したい場合)
```javascript
document.querySelectorAll('p:empty, div:empty').forEach(el => el.remove());
```
これにより、空の `<p>` や `<div>` を削除できる。
### 方針
前項のやり方で空白行の削除ができるが、個々の記事HTMLから空白行を見つけ出し、それを削除したHTMLに更新するとなると、1記事ごとにやるにしろ一括でやるにしろ、大変な作業となる。
そこで、
1. **テーマxml**に空白削除スクリプトを追加し、**DOM (Document Object Model) 上での削除**(※下記注釈)をおこなう。
1. **テーマxml**で、見出しの前後に適切な空白行をつけるCSS調整をおこなう。
DOM 上の削除とは?
- 実際の HTML ファイルのコードには影響を与えず、ブラウザが読み込んだ後のページ上で br や空の要素を削除する。
- 記事HTML ファイル自体は変更されない。
- 開発者ツール (F12 キー) で動作を確認すると、DOM から要素が消えているのがわかる。
## スクリプト
削除したい空白行の書き方と、除外事例を挙げて、<svg aria-label="i-copilot" class="blog-logo" height="24" width="24"><use href="#i-copilot"></use></svg><span style="font-size:12px;"> </span> Copilot にスクリプトを書いてもらった。一発では決まらず、何度か修正を繰り返している。
今、気づいている範囲での最終形である。
```javascript
<script>
/*<![CDATA[*/
document.addEventListener("DOMContentLoaded", function () {
console.log("? 空白削除スクリプト開始");
// ① 改行目的の <br> タグを削除(ただし #dark-header, #dark-toggler, .item-control, .quick-edit, .blogcard, .katex 内の要素, `<table>` の内部は削除しない)
document.querySelectorAll("br").forEach(el => {
if (!el.closest("#dark-header") && !el.closest("#dark-toggler") && !el.closest(".item-control") && !el.closest(".quick-edit") && !el.closest(".blogcard") && !el.closest(".katex") && (!el.closest("table")) {
el.remove();
}
});
console.log("? 不要な <br> の削除完了");
// ② 空の <p> や <div> の削除(#dark-header, #dark-toggler, .item-control, .quick-edit, .blogcard, .katex は削除しない)
document.querySelectorAll("p, div").forEach(el => {
if (!el.textContent.trim() && !el.closest("#dark-header") && !el.closest("#dark-toggler") && !el.closest(".item-control") && !el.closest(".quick-edit") && !el.closest(".blogcard") && !el.closest(".katex")) {
console.log(`削除: ${el.tagName}`);
el.remove();
}
});
console.log("? 空の <p> や <div> の削除完了");
// ③ span要素の削除を制限(ただし #dark-header, #dark-toggler, .item-control, .quick-edit, .blogcard, .katex 内のものは削除しない)
document.querySelectorAll("span").forEach(el => {
if (!el.textContent.trim() && !el.closest("#dark-header") && !el.closest("#dark-toggler") && !el.closest(".item-control") && !el.closest(".quick-edit") && !el.closest(".blogcard") && !el.closest(".katex")) {
console.log(`削除: ${el.tagName}`);
el.remove();
}
});
console.log("? 空の <span> の削除完了");
// ④ MutationObserver の修正(ボタンやアイコン・ブログカード・KaTeX 数式が削除されないようにする)
const observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
mutation.removedNodes.forEach(node => {
if (node.id === "dark-toggler" || node.id === "dark-header" || node.classList.contains("item-control") || node.classList.contains("quick-edit") || node.classList.contains("blogcard") || node.classList.contains("katex")) {
console.warn("?? 重要な要素が削除されたので復元します", node);
document.body.appendChild(node); // 強制的に復元
}
});
});
});
observer.observe(document.body, { childList: true, subtree: true });
console.log("? MutationObserver 設定完了(ボタン・アイコン・ブログカード・数式を監視)");
});
/*]]>*/
</script>
```
## CSS調整
### 見出し(h1, h2, h3, h4, h5, h6)
オリジナルJetthemaでの見出し前後の空白行の設定は、次のようになっている。`jettheme-v2.xml`では、590行。
``` css
.entry-text h1,
.entry-text h2,
.entry-text h3,
.entry-text h4,
.entry-text h5,
.entry-text h6 {
color: var(--jt-heading-color);
padding-top: 1em;
margin-bottom: 1rem;
}
```
これを少し調整して、次のようにする。`/*Your custom CSS is here*/` に書く。デフォルトでも悪いわけでもないので、これは好みの問題である。
```css
.entry-text h1,
.entry-text h2,
.entry-text h3,
.entry-text h4,
.entry-text h5,
.entry-text h6 {
margin-top: 1em;
margin-bottom: 1em;
}
```
### 画像
```css
/* 記事やブログカードの画像のみに適用(クイック編集アイコンには影響なし) */
.blogcard img:not(:has(+ figcaption)),
.post img:not(:has(+ figcaption)) {
display: block;
margin-bottom: 1.8rem;
}
/* キャプション付きの画像には影響を与えない */
figcaption {
display: block;
margin-bottom: 1.8rem;
}
```
## 削除対象から除外が必要な要素
### ✅ KaTeX の数式 (.katex)
- KaTeX の数式は `<span>` や `<div>` を使って構造化されている
- 数式表記の HTML に textContent.trim() の判定が影響
### ✅ ダークモードアイコン
- #dark-header,#dark-toggler の要素を削除対象から除外
### ✅ ソース編集ボタン (.item-control)
- .closest(".item-control") をチェック
→ item-control の子要素である `<p>` や `<div>` は削除対象から除外
- ソース編集ボタンが削除されないよう制御
→ ブログのデザインを維持しながら空白削除できるはず。
### ✅ クイック編集ボタン
- .quick-editの要素を削除対象から除外
### ✅ ブログカード (.blogcard) も影響を受けない
- .closest(".blogcard") をチェックして、ブログカード内の `<span>` や `<div>` を削除しないように変更
- ブログカードの構造を維持しつつ、不要な空白行を削除できるよう調整
### ✅ MutationObserver によって、誤削除された場合は強制復元
- nextElementSibling を確認 → 連続する `<br/><br/>` を削除
- MutationObserver を追加 → 動的に追加された `<br/>` も削除
## 関連リンク
</div></span></div></p></div></span></div></p></p></p></p></p></div></p>