Javascriptでドキュメント上にない要素を指定するとエラーが表示される

[code]

    var a = document.getElementById('id');
    var b = document.getElementsByClassName('p');
    
[/code]

このようにドキュメント上に存在しない要素を取得するとき、documentを親として要素を取得する場合は特にエラーは表示されません。

[code]

    var a = document.getElementById('id');
    var b = a.getElementsByClassName('p');
    
[/code]

ところが、このように親要素に#idがあってその中のp要素を取得したいとき、ChromeにてUncaught TypeError: Cannot read
property 'getElementsByClassName' of nullと表示されます。  
要素が見つからなかった場合は空の集合が返るのではないのでしょうか？

# 追記

たとえば、Webサイトのすべてのページに共通なJSとして

[code]

    var a = document.getElementById('id');
    var b = a.getElementsByClassName('p');
    
[/code]

を読み込む場合、すべてのページに#id要素が存在するとは限りません。そのような場合は、`b` に代入する前に `null`
かどうかをチェックしてから代入するといったような方法が普通でしょうか？普通はどのような方法が使われますか？




> 要素が見つからなかった場合は空の集合が返るのではないのでしょうか？

という部分から、最終的にはあるルールにマッチしたDOM要素に対してイテレーションでなにか処理をするのだと解釈させていただくと、この意味では[element.querySelectorAll](https://developer.mozilla.org/ja/docs/Web/API/element/querySelectorAll)が使いやすいかもしれません。このAPIは

> ## element.querySelectorAll
>
> 対象要素の子孫の内、引数に指定した CSS セレクタのグループにマッチする要素群の（ライブなものでない） NodeList を返します。

第一引数にはCSSセレクタを文字列として渡すことで、DOM要素をNodeList（配列Arrayに似たオブジェクトで、イテレーション可能です）として抽出することが可能です。今回の場合は、`#idのなかのp要素`ですから、

[code]

    const resultNodeList = document.querySelectorAll('#id p');
    
[/code]

などとして取得ができます。これはマッチする要素がない場合「空の集合」すなわち`length ===
0`のNodeListオブジェクトを返しますので、通常の配列のように処理を続ければ期待する動作になるかと思われます。[NodeListの詳細についてはドキュメント](https://developer.mozilla.org/en-
US/docs/Web/API/NodeList)をご確認ください。

[code]

    const resultNodeList = document.querySelectorAll('#id p');
    
    // もし必要ならlengthプロパティをチェック
    //if (resultNodeList.length === 0) {
    //    return;
    //}
    
    // NodeListは基本的に配列と同様の操作が可能です。
    // マッチする要素が見つからないとき（length === 0）は、
    // このループはもちろん実行されません
    for (const elm of resultNodeList) {
        doSomething(elm);
    }
    
[/code]

* * *

追記：  
私の回答ではないですがコメントにて速度について言及がありましたので述べておくと、確かにquerySelectorは高機能なぶん従来のgE系APIに対して低速である場合が多いです。ただしこれはベンチマークスコアの話であって、たとえば巨大なDOM構造に対して探索を行う場合は考慮するべきですが、一方通常のwebページに対して用いるのであれば誤差の範囲です。もしquerySelectorを使うと重すぎてだめだ、となった場合は、どちらかといえばページを分割したりjsのシステムを改善したりする必要があるかと思います。こちらは具体的な話ではないので参考程度に。




> 要素が見つからなかった場合は空の集合が返るのではないのでしょうか？

という部分から、最終的にはあるルールにマッチしたDOM要素に対してイテレーションでなにか処理をするのだと解釈させていただくと、この意味では[element.querySelectorAll](https://developer.mozilla.org/ja/docs/Web/API/element/querySelectorAll)が使いやすいかもしれません。このAPIは

> ## element.querySelectorAll
>
> 対象要素の子孫の内、引数に指定した CSS セレクタのグループにマッチする要素群の（ライブなものでない） NodeList を返します。

第一引数にはCSSセレクタを文字列として渡すことで、DOM要素をNodeList（配列Arrayに似たオブジェクトで、イテレーション可能です）として抽出することが可能です。今回の場合は、`#idのなかのp要素`ですから、

[code]

    const resultNodeList = document.querySelectorAll('#id p');
    
[/code]

などとして取得ができます。これはマッチする要素がない場合「空の集合」すなわち`length ===
0`のNodeListオブジェクトを返しますので、通常の配列のように処理を続ければ期待する動作になるかと思われます。[NodeListの詳細についてはドキュメント](https://developer.mozilla.org/en-
US/docs/Web/API/NodeList)をご確認ください。

[code]

    const resultNodeList = document.querySelectorAll('#id p');
    
    // もし必要ならlengthプロパティをチェック
    //if (resultNodeList.length === 0) {
    //    return;
    //}
    
    // NodeListは基本的に配列と同様の操作が可能です。
    // マッチする要素が見つからないとき（length === 0）は、
    // このループはもちろん実行されません
    for (const elm of resultNodeList) {
        doSomething(elm);
    }
    
[/code]

* * *

追記：  
私の回答ではないですがコメントにて速度について言及がありましたので述べておくと、確かにquerySelectorは高機能なぶん従来のgE系APIに対して低速である場合が多いです。ただしこれはベンチマークスコアの話であって、たとえば巨大なDOM構造に対して探索を行う場合は考慮するべきですが、一方通常のwebページに対して用いるのであれば誤差の範囲です。もしquerySelectorを使うと重すぎてだめだ、となった場合は、どちらかといえばページを分割したりjsのシステムを改善したりする必要があるかと思います。こちらは具体的な話ではないので参考程度に。

