jQueryによる外部HTMLの読み込みは様々なところで解説されていますが、読み込まれる前提ではない作りのHTMLを読み込む時の解説は少ないように思います。
今回は必要に迫られ、HTMLから一部の内容を抜き出して別ページに表示します。
「外部」とは言いつつも、同一ドメイン下でのみ有効です。
使っている技術に難しいものはありません。
#環境
- macOS Catalina(10.15.7)
- jQuery 3.6.0
- html5
- Firebase Hosting
- MAMP
#前提
「HTML資産を横展開しよう」というのが出発点。
ただし、横展開するには下記のお約束をクリアする必要がある。
- 基盤HTMLをコピーしてサーバに格納する。構造変更は基本NG。どうしても変更したい場合、基盤及横展開済みのHTMLの更新も必要。
- 新サービスでは表示方法が2種存在する。Webページ単独とWebアプリ上でポップアップ。
- PHPやSSIは使えない(HostingはFirebaseを利用するため)。
HTML構造を変更できないのは渋いが、横展開するHTMLは90ファイルほどある。
90ファイル × 横展開数 = 全HTMLファイル数というのは膨大で、更新するのはもっと渋いwww
さて、どうするか。
#悩まずjQueryのload()を使う
ある頃から「オワコン」と言われ続けているjQuery。
今のところ、Web制作の現場でのjQuery利用率は高いので、急に消滅することはないと思います。
jQuery出現前夜、「Javascriptを極力使わないでWebサイトを構築しよう」という流れもあったので、将来のトレンドなんてわからん1ものです。
Webアプリを開発するなら、Vue.jsとかReact.jsなんかのモダンフレームワークは必要だけど、今回は学習コストを抑えるパターンを採用します。
$(function(){
$("セレクタ").load("URL");
});
jQueryを読み込んでいれば、基本はたったこれだけ。
今回はFirebase Hostingを使う想定なので、環境を作ってあればローカルサーバーを起動して確認ができます。
Firebase Hostingを使う予定がない場合は、手っ取り早くMAMPを導入しよう。
MAMPについては下記のような記事も書いたことがあるし、
Qiitaに無数にある記事を参考にすれば簡単に導入できる。
では、実際に読み込みをやってみる。
##読み込みたいHTMLと表示するHTML
表示がある方がわかりやすいので、サンプルデーターを作ってみた。
さすがに実際のコンテンツを持ってくるわけにはいかないので、中身を差し替えてアップしてある。
CLASS名などは若干変えたが、基本構造はほぼ同じに書き起こした(ちょっと疲れた)。
コンテンツはウィキペディアから借用した。
最近のマイブームは「たまねぎ」なので、安易にサンプルコンテンツに決めたのだが、必要以上に内容が多くてかなり後悔した。
サンプル作るなら、もっと少ない文章のものを参照しようよ、と数日前の自分に強く主張したい。
/4d1f967eb87903d17447
├── index.html → Webサービスのトップページ(今回なし)
├── /assets → Webサービスの共通データー
│ ├── /css
│ ├── /js
│ └── /images
├── /data(基盤HTMLコピー)
│ ├── assets → 読み込みたいHTMLの共通データー
│ │ ├── /css
│ │ ├── /js
│ │ └── /images
│ └── index.html → 読み込みたいHTML
└── /detail
└── index.html → 表示するHTML
読み込みたいHTMLは、基盤HTMLと階層を合わせた状態にする。
というのも、データーに修正や追加があった場合、HTMLをまるごと更新するので、Webサービス用に階層を変更するとコードに細かい修正が発生して運用時に死ぬ。
なお、基盤HTMLは、スマホアプリのWeb Viewで表示するために制作されている。そもそも、横展開を予定して組まれたデーターではない。
アコーディオンパネルはjQueryで実装されているので、動き等は下記URLを参考にされたし。
› 読み込みたい実際のページを確認(外部サイト)
基盤と言っても、スマホアプリ用に作られたHTMLなので、そのまま使用するには不適当なリンクが存在するし(今回例では「畑を見る」ボタン)、ユーザーエージェントで出し分けている部分もある。
なにより、横展開先のWebサービス用にレイアウトを組む必要があるので、コードの追加は避けられない。
だがしかし。
Webサービス用のコードを追加してスマホアプリ用のHTMLに適用すると、今度はスマホアプリ側でHTMLが死ぬ。
ソースコードを追加・変更すると漏れなくどちらかのHTMLが死ぬので、ホイホイ気軽に追加するわけにはいかない。
この2つをうまく使うには、頭をひねるしかないよねー…ということで、とりあえずホイホイ読み込んでみよう。
##雑にまるごとHTMLを読み込む
URLは相対で書いても良い。私はリンク切れが嫌なのでサイトルートパスで書いている。
$(function(){
$("#js-vegetableDetail").load("/4d1f967eb87903d17447/data/index.html");
});
<div id="js-vegetableDetail"></div>
に外部HTMLを読み込む。
IDの方が早いのでJS処理に使い、接頭辞js-
を付けることにした。
絵的にはここらへんに読み込む。
› 表示したい実際のページを確認(外部サイト)
さぁ!ばっちこぉーーーい!!
雑に読み込んだので、タイトルと「※このコンテンツは「jQueryを使って外部htmlを読み込む」のサンプルです。 」は二重に表示されている。
ヘッダの「野菜のたまねぎを紹介」の背景色の左右が切れているし、フッタの「野菜の目次に戻る」というリンクの左側も不自然に切れている。
ただし、アコーディオンパネルや右下に表示されるPageUpは正常に動作する(スクショには表示されてない)。
› 雑に読み込んだ実際のページを確認(外部サイト)
<div id="js-vegetableDetail"></div>
に読み込まれたHTMLはこんな感じ。
<div id="js-vegetableDetail">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>玉ねぎの特徴</title>
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="format-detection" content="telephone=no">
<link rel="stylesheet" href="/4d1f967eb87903d17447/data/assets/css/common.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="/4d1f967eb87903d17447/data/assets/js/common.js"></script>
<article class="wrap">
<header>
<h1>たまねぎ</h1>
</header>
<section>
<p>
<strong>
この記事は、<a class="textLink" href="http://creativecommons.org/licenses/by-sa/3.0/">クリエイティブ・コモンズ・表示・継承ライセンス3.0</a>のもとで公表されたウィキペディアの項目<a class="textLink" href="https://ja.wikipedia.org/wiki/%E3%82%BF%E3%83%9E%E3%83%8D%E3%82%AE">「たまねぎ」</a>を素材として二次利用しています。
</strong>
</p>
<p>
ネギ属の多年草。園芸上では一年草もしくは二年草として扱われる。ネギ属の中でも大きく肥大した鱗茎(球根)を持つ種で、品種によって色、形状、大きさは様々である。主に鱗茎が野菜として食用とされるほか、倒伏前に収穫した葉(葉タマネギ)もネギと同様に調理できる。
</p>
<div class="screenshot">
<img src="/4d1f967eb87903d17447/data/assets/images/onion.jpg" alt="たまねぎ" width="320" height="240">
</div><!-- /.screenshot -->
<p>
和名であるタマネギの由来は、文字通り鱗茎が玉のように大きくなる葱のなかまという意味からきている。<br>
英名はオニオン(onion)、仏名がオニョン(oignon、または ognon)、伊名ではチポッラ(cipolla)という。英語名オニオンの由来は、古代ローマ時代にローマ人がタマネギを bulbus あるいは unionem と呼んでいたことにちなむ。<br>
学名のアリウム・ケーパ(Allium capa)は、ラテン語で「タマネギ」を意味し、スペイン語のセボーリャ(cebolla)や、ポーランド語のセブラ(cebra)などは、その派生語である。
</p>
︙
</section>
<p>
※このコンテンツは「jQueryを使って外部htmlを読み込む」のサンプルです。
</p>
<footer>
<a class="footerListLink" href="">野菜の目次へ戻る</a>
</footer>
</article>
</div><!-- /#js-vegetableDetail -->
<html lang="ja">〜</html>
の中身を全部読み込んでいる。
<meta>, <title>, <link>, <script>
など、表示先で不要な情報も持ってくるので、ひとことで言うならお節介焼きなメソッドである。
そのお節介ゆえに、双方のCSSやJSが適用されてレイアウトは崩れゆく…。
これではゴールに程遠いので、/4d1f967eb87903d17447/data/index.html
から必要な部分だけを読み込みます。
##セレクタを指定してHTMLを読み込む
$(function(){
$("セレクタ").load("URL セレクタ");
});
そもそも雑にしたのが原因なので、読み込む部分を丁寧に指定する。
では、読み込みたいHTMの構造を見て考える。
<body>
<article class="wrap">
<header>
<h1>たまねぎ</h1>
</header>
<section>
<p>
<strong>
この記事は、<a class="textLink" href="http://creativecommons.org/licenses/by-sa/3.0/">クリエイティブ・コモンズ・表示・継承ライセンス3.0</a>のもとで公表されたウィキペディアの項目<a class="textLink" href="https://ja.wikipedia.org/wiki/%E3%82%BF%E3%83%9E%E3%83%8D%E3%82%AE">「たまねぎ」</a>を素材として二次利用しています。
</strong>
</p>
<p>
ネギ属の多年草。園芸上では一年草もしくは二年草として扱われる。ネギ属の中でも大きく肥大した鱗茎(球根)を持つ種で、品種によって色、形状、大きさは様々である。主に鱗茎が野菜として食用とされるほか、倒伏前に収穫した葉(葉タマネギ)もネギと同様に調理できる。
</p>
<div class="screenshot">
<img src="/4d1f967eb87903d17447/data/assets/images/onion.jpg" alt="たまねぎ" width="320" height="240">
</div><!-- /.screenshot -->
<p>
和名であるタマネギの由来は、文字通り鱗茎が玉のように大きくなる葱のなかまという意味からきている。<br>
英名はオニオン(onion)、仏名がオニョン(oignon、または ognon)、伊名ではチポッラ(cipolla)という。英語名オニオンの由来は、古代ローマ時代にローマ人がタマネギを bulbus あるいは unionem と呼んでいたことにちなむ。<br>
学名のアリウム・ケーパ(Allium capa)は、ラテン語で「タマネギ」を意味し、スペイン語のセボーリャ(cebolla)や、ポーランド語のセブラ(cebra)などは、その派生語である。
</p>
<a class="view" href="">畑を見る</a>
<table class="table">
<tbody>
<tr>
<th>界</th>
<td>植物界 Plantae</td>
</tr>
︙
</tbody>
</table>
</section>
<section class="accordion">
<h2>特徴</h2>
<div class="panel">
<section>
<p>
越年生の草本。鱗茎は径10センチメートル (cm) 前後の球形、または扁球形をしており、特異な刺激性の臭気がある[9]。茎は円筒形で直立し、高さは50 cmくらいまで生長して、下部に2 - 3の葉をつける。葉はネギよりも細く、濃緑色で中空になっている。秋には、茎頂部に花序が大きな球形となってつき、白色の花が密集する。
</p>
<p>
葉が伸びて70 cmくらいに育つと、地中の葉鞘が結球しはじめ肥大化する。結球するには、一定の温度で適切な時間日光を浴びることによって、葉で糖が生成されて、その養分が基部に蓄えられて鱗茎が形成される。鱗茎は鱗片葉が球状に重なったものでできており、多くの層を持っている。鱗茎がある程度肥大すると、地上部の葉鞘が葉を支えきれなくなって倒れ込む倒伏性がある。
</p>
︙
</section>
</div><!--/.panel -->
</section><!-- /.accordion -->
<section class="accordion">
<h2>歴史</h2>
<div class="panel">
<section>
︙
</section>
</div><!--/.panel -->
</section><!-- /.accordion -->
<section class="accordion">
<h2>種類</h2>
<div class="panel">
<section>
<p>
大別すると、東欧系の辛味品種と南欧系の甘味品種があり、日本で栽培されるものは、ほとんど辛味品種である。甘味品種には、紫タマネギの湘南レッドがある。一方で、辛味品種には黄タマネギ、ペコロスなどがある。
</p>
︙
</section>
<section>
<h3>黄タマネギ</h3>
<p>
最もポピュラーで薄皮が赤茶色の品種。秋冬に収穫する秋冬タマネギと、春に収穫する新タマネギがある。秋冬玉ねぎは、保存性を高めるため収穫後に風干しして1か月ほど皮を乾燥して出荷しているため、水分量は少なめで、肉厚で辛味がある。新タマネギは、皮が白っぽい黄タマネギの早採りもので、水分量が多く軟らかい食感で辛味が少なく、生食にも向いている。また、干さずに出荷するため、保存性は悪い。
</p>
</section>
<section>
<h3>サラダオニオン</h3>
<p>
︙
</p>
</section>
︙
</div><!--/.panel -->
</section><!-- /.accordion -->
<section class="accordion">
<h2>重要病虫害</h2>
<div class="panel">
<section>
<ul>
<li>乾腐病 病原菌:Fusarium oxysporum f. sp. cepae</li>
︙
</ul>
</section>
</div><!--/.panel -->
</section><!-- /.accordion -->
<section class="externalLink accordion">
<h2>関連項目(ウィキペディア)</h2>
<div class="panel">
︙
</div><!--/.panel -->
</section><!-- /.accordion -->
<p>
※このコンテンツは「jQueryを使って外部htmlを読み込む」のサンプルです。
</p>
</article>
</body>
主に<section>
で内容が分けられている。
ほとんどはCLASSが付与されていないが、アコーディオンパネルにはaccordion
というCLASSが付与されている。
アコーディオンの中身は<div class="panel">
の中に、CLASS付与なしの<section>
で囲われている。
極力CLASSを付与しない方法でレイアウトを作っているようだ。
header {
display: table;
position: relative;
width: 100%;
height: 60px;
margin: 0 0 15px;
padding: 0 16px;
background: #502304;
}
.wrap header h1 {
display: table-cell;
width: auto;
height: 60px;
text-align: center;
color: #fff;
font-size: 19px;
vertical-align: middle;
}
これをヒントに指定するセレクタを決める。
まず、下記のように2つに分けた。
- ページタイトル
- たまねぎの説明
「ページタイトル」は直書きでも良いが、表示しているタイトルと読み込んだ中身が一致している必要がある。
だが、90ファイルものデーターのコードをいちいち書き換えるのは嫌だ。絶対に間違う自信がある。
「たまねぎ」のページに「キャベツ」のデーターが入っていては目も当てられない。
そのため、あえて「ページタイトル」も読み込むことにした。
読み込むだけなら、下記の書き方で成功する。
$(function(){
const vagetable = "/4d1f967eb87903d17447/data/index.html";
$("#js-vegetableName").load( vagetable + " h1");
$("#js-vegetableDetail").load(vagetable + " header + section, .accordion");
});
/4d1f967eb87903d17447/data/index.html
を2回書くのは面倒くさいから変数にした。
変数宣言: 【JavaScript】var / let / const を本気で使い分けてみた
- 「ページタイトル」は
<h1>
タグを指定 - 「たまねぎの説明」は
<header>
タグ直後に隣接する<section>
タグとclass="accordion"
を付与された要素を指定
さぁー!ばっちこぉーーーーい!!(今日2回目)
レスポンシブWebデザインのCSSを追加したので、ウインドウサイズでレイアウトが変わる。
まあ、ぱっと見た感じは成功してるよね。
アコーディオン、動かないがな 🥺
理由は簡単ですよねー。アコーディオンを動かすJavascriptを読み込んでないんだもん。
それじゃあ、表示ページのHTMLにJavascriptを読み込んだり、書けばいいよね…と考えて追加しても動きません。
アコーディオンパネルを正常に動かしたい場合は下記のように書く。
$(function(){
const vagetable = "/4d1f967eb87903d17447/data/index.html";
$("#js-vegetableName").load( vagetable + " h1");
$("#js-vegetableDetail").load( vagetable + " header + section, .accordion",function(){
//$.getScript でスクリプトを読み込む
$.getScript("/4d1f967eb87903d17447/data/assets/js/common.js");
});
});
› アコーディオンも動く、成功したように見える実際のページを確認(外部サイト)
実際の「読み込みたいHTML」で使われている/4d1f967eb87903d17447/data/assets/js/common.js
には、表示したいページには不要なスクリプトが含まれています。
今回はアコーディオンパネル用のJSしか書いてないのでそのまま読み込んでいますが、必要なJSだけ抜き出して別ファイルにしたほうが良いと思います。
セレクタをheader + section, .accordion
にしている理由
無事動いた。めでたい。
でも、コードは少なくしたいと思うのが人情ってものだ。
HTMLを見ていると「セレクタは<section>
タグだけでもいいんじゃね?」って思うかも。
$(function(){
const vagetable = "/4d1f967eb87903d17447/data/index.html";
$("#js-vegetableName").load( vagetable + " h1");
$("#js-vegetableDetail").load( vagetable + " section",function(){
//$.getScript でスクリプトを読み込む
$.getScript("/4d1f967eb87903d17447/data/assets/js/common.js");
});
});
そう言う疑問があるならやってみよー!(棒)
ぱっと見た感じは正常に読み込まれてるように見える。
『セレクタを指定してHTMLを読み込む』と非常に似ているが、アコーディオンパネルは開きっぱなし。
表示が似通っていてスクリーンショットを撮るのも面倒なので、詳しくは外部サイトを確認されたし。
レイアウトにもおかしなところがあるが、「タイトルをクリックすると右横の矢印の表示は変更されるのに、パネルの開閉は起こらない」のが一番大きい。
JSは正常に読み込んでるのにアコーディオンパネルの挙動はヘンってことだ。
この場合、アコーディオンパネル部分のHTML構造を見ると原因がわかる。
<div id="js-vegetableDetail">
<section class="accordion">
<h2>特徴</h2>
<div class="panel">
<!-- ← 本来<section><p>越年生の草本。鱗茎は…</p></section>が入っているはずなのに空 (゚Д゚ )アヒャ- -->
</div><!-- /.panel -->
</section><!-- /.accordion -->
<section><!-- ← <div class="panel">〜</div>に格納されず<section class="accordion">〜</section>と同階層に読み込まれている (゚Д゚ )アヒャ- -->
<p>
越年生の草本。鱗茎は径10センチメートル (cm) 前後の球形、または扁球形をしており、特異な刺激性の臭気がある。茎は円筒形で直立し、高さは50 cmくらいまで生長して、下部に2 - 3の葉をつける。葉はネギよりも細く、濃緑色で中空になっている。秋には、茎頂部に花序が大きな球形となってつき、白色の花が密集する。
</p>
<p>
葉が伸びて70 cmくらいに育つと、地中の葉鞘が結球しはじめ肥大化する。結球するには、一定の温度で適切な時間日光を浴びることによって、葉で糖が生成されて、その養分が基部に蓄えられて鱗茎が形成される。鱗茎は鱗片葉が球状に重なったものでできており、多くの層を持っている。鱗茎がある程度肥大すると、地上部の葉鞘が葉を支えきれなくなって倒れ込む倒伏性がある。
</p>
︙
</section>
</div><!-- /#js-vegetableDetail -->
このアコーディオンパネルは、<section class="accordion">
を親に持つ<h2>
をクリックすることで<div class="panel"></div>
の表示・非表示を変更します。
そのため、<section><p>越年生の草本。鱗茎は…</p></section>
は<div class="panel"></div>
内に格納されていなければならない。
しかし、<section>
をセレクタに指定しているため、本来の構造を無視して並列に読み込まれています。
基本姿勢は大雑把に行こうぜ! ですが、ここは大雑把にしちゃダメw
今回のHTMLの場合、元の構造通りにするには、<header>
タグ直後に隣接する<section>
タグと、<class="accordion">
を持つ要素をセレクタに指定する必要があります。
否定擬似クラスを使って特定要素を除外
ちょっとしたおまけです。セレクタにはCSSの擬似クラスを使うこともできます。
$(function(){
const vagetable = "/4d1f967eb87903d17447/data/index.html";
$("#js-vegetableName").load( vagetable + " h1");
$("#js-vegetableDetail").load(vagetable + " header + section, .accordion:not('.externalLink')",function(){
//$.getScript でスクリプトを読み込む
$.getScript("/4d1f967eb87903d17447/data/assets/js/common.js");
});
});
›「関連項目(ウィキペディア)」を除外したページ(外部サイト)
.accordion:not('.externalLink')
という書き方にすると、.externalLink
が付いた.accordion
の要素を除外します。
#jQueryのload()だけでは完全解決とはいかなかった
› 作業完了としたページ(外部サイト)
結局は完全解決とはいかなかったwww
なぜかと言いますと、<section>
タグ内にある「これ」を除外できなかったからです。
<a class="view" href="">畑を見る</a>
手っ取り早くCSSで表示を消した。ぐぬぬぬぬ…。
#js-vegetableDetail .view {
display: none;
}
.view
と書いてもいいのですが、CLASSの重複が起こりそうな気配なので、読み込みに使用したIDを重ねることで詳細度を上げています。
ほとんどのユーザーはHTMLコードを見ることはありません。
でも、いらんものは消しておきたい。絶対消したい!というのが、制作者の気持ちだったりします。
ただ、基盤HTMLを読み込むことで構造は変更していないので、修正が入った場合は、修正済み基盤HTMLをdataフォルダに突っ込めば更新が終了します。
ひとまず、お約束は守ったよということで。
参考サイト
- .load() | jQuery API Documentation
- jQuery.getScript() | jQuery API Documentation
- jQuery の load() | Web Design Leaves
- 【JavaScript】var / let / const を本気で使い分けてみた
-
「これからはXHTMLの時代だぜ!」と盛り上がったはいいが、速攻萎んじゃって今では影も形もないということもある。 ↩