HTML上に複数の同じようなコンテンツを置かざるを得ないケースに遭遇することってたまにあると思います。
たとえば
- PCとスマホのコンテンツを同一ページ別DOMで用意するとき
- 多言語のコンテンツを同一ページに複数DOMで用意するとき
- テンプレートを作って中身を変えずにクローンしまくるとき
本来サーバーサイドで出し分けるべきものの場合が多いですが、
訳あってフロントサイドしか触れないなど、やむを得ないケースはあるかと思われます。
そして、何かの手違いだったりやむを得ない事情で、IDも一緒にコピーしてしまうことがあるかと思われます。
要素のIDは重複してはならないのがルールですが、
実はCSSやJavaScriptではある程度許容されているような動作をします(おそらく仕様にない挙動ですので濫用はしないように)。
複数IDのフィルタリング
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>has duplicated</title>
<style>
.env-1 {
display: none;
}
</style>
</head>
<body>
<a href="#p1"></a>
<a href="#p2"></a>
<div class="env-1">
<p id="p1">この親のdivはPCだとか。</p>
<p id="p2">この親のdivはenだとか。</p>
</div>
<div class="env-2">
<p id="p1">この親のdivはSPだとか。</p>
<p id="p2">この親のdivはjaだとか</p>
</div>
<script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
</body>
</html>
このHTMLの2番目の<p id="p1">
を取得する方法をいくつか。(※要jQuery)
↓こういうのは無理です。
$('.env-2#p')
↓これなら大丈夫みたいです。
$('.env-2').find('#p2');
↓これなら大丈夫(重複しているIDの2番目)
$('[id="p2"]').eq(1)
↓これでも大丈夫(重複しているIDの最後)
$('[id="p2"]').eq(-1)
↓これが実用的(DOMの可視/不可視をまるごと切り替えてるとき)
$('[id="p2"]:visible')
ページ内ジャンプの実装
最後の:visible
セレクタを用い、見えている方のIDの元へ飛んでいってくれるページ内ジャンプを書いてみました。
$('a[href^=#]').on('click', function() {
var targetId = $(this).attr('href').slice(1);
var selector = targetId ? '[id="' + targetId + '"]:visible' : 'html';
var $target = $(selector);
var dist = $target.offset().top;
$('html, body').animate({scrollTop: dist});
});
補足
このように、属性セレクタ等を用いて重複したIDを取得できることがわかりましたが、
重ね重ねIDの重複はHTMLとしてはinvalidのため、出来る限り別IDにするか、クラスやデータ属性を使って回避しましょう。
(僕は基本的に実務でのIDの使用は極力避けてます。)
ちなみに、IDの重複チェッカーを作ったので、Validatorに通す前に軽くチェックするフローを挟むなど活用して頂ければ幸いです。
JavaScript - DOM上のIDの重複を検出する - Qiita