34506

[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`
かどうかをチェックしてから代入するといったような方法が普通でしょうか？普通はどのような方法が使われますか？


CC BY-SA 3.0
2017-05-07T02:10:51.320
34501
2017-05-09T07:33:10.583
2017-05-07T02:30:06.837
19687
19687
question
javascript
Javascriptでドキュメント上にない要素を指定するとエラーが表示される


> Uncaught TypeError: Cannot read property 'getElementsByClassName' of null

とは`a`が`null`であることを指摘しています。

> 親要素に#idがあってその中のp要素を取得したいとき

とのことですが、実際には#idが存在しないことを意味します。ドキュメントの内容をよく確認してください。例えば`<iframe>`などで読み込まれた別ドキュメントの可能性があります。

* * *

「親要素に#idがあって」から「#id要素が存在するとは限りません」と質問の趣旨が正反対になっていませんか？

> 普通はどのような方法が使われますか？

存在するとは限らない要素に対して、どのような要素選択を行いたいのか、目的によってコードの記述内容は変わってきますので、普通を語ることはできません。例えば早々に`return`してもよい場合もあれば、別の要素を選択したい場合もあり得ます。

* * *

>
> 全ページに共通‌​のJSを読み込んでいると今回のようなエラ‌​ーが出るページがたくさん出てしまうので、‌​そういった場合どうすればいいかわからない‌​のです。

そうではなく、#idが存在しない場合にどのような結果を求めているか次第です。「エラーが起きなければそれでよい」といった後ろ向きな考え方ではなく、「どのような結果を必要としているか」を考えてください。

> document.getElem‌​entsByClassName()してし‌​まえばエラーはでない

「エラーはでない」ではなく「空リストが得られる」です。#idが存在しない場合には空リストで構わないので[`document.querySelectorAll`](https://developer.mozilla.org/ja/docs/Web/API/Document/querySelectorAll)を使用して

[code]

    var b = document.querySelectorAll("#id p");
    
[/code]

と表現できます。


CC BY-SA 3.0
2017-05-07T02:22:35.193
34502
2017-05-07T06:20:21.017
2017-05-07T06:20:21.017
4236
4236
34501
answer


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

という部分から、最終的にはあるルールにマッチした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のシステムを改善したりする必要があるかと思います。こちらは具体的な話ではないので参考程度に。


CC BY-SA 3.0
2017-05-07T06:24:15.730
34506
2017-05-07T07:35:22.097
2017-05-07T07:35:22.097
34501
answer


なにがいけないのか、という点がはっきり書かれた回答がないようなので...。

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

いいえ、違います。[`getElementById()`](https://developer.mozilla.org/ja/docs/Web/API/Document/getElementById)
は指定したIDを持つ要素が存在すればその要素（集合ではありません）を、 **存在しない場合は null を返します** 。空の集合ではありません。  
ちなみにメソッド名をよく見ると getElementsByClassName と getElementById では複数形かどうかが異なります。

~~また、[`getElementsByClassName()`](https://developer.mozilla.org/ja/docs/Web/API/Document/getElementsByClassName)
は Document オブジェクトにしか存在しないメソッドです。~~  
他の要素に対しても使えるって書いてありますね...失礼しました。

ちなみに速度面ですが、ベンチマークスコアとしてみても、親要素を指定してもしなくても大差ないようです。(例
<https://jsperf.com/q34501-3/1>)


CC BY-SA 3.0
2017-05-07T08:04:55.460
34509
2017-05-09T07:33:10.583
2017-05-09T07:33:10.583
8000
8000
34501
answer


### クラス名とタグ名の区別

「`p` というクラスが設定されている要素」と「`p` 要素」は異なります。

前者であれば、質問のコードのように、[`getElementsByClassName`](https://developer.mozilla.org/ja/docs/Web/API/Document/getElementsByClassName)
で検索して大丈夫です。  
しかし後者の「`p` 要素」というと通常はタグ名 `p`
で作成される要素を指しますので、これを検索するには[`getElementsByTagName`](https://developer.mozilla.org/ja/docs/Web/API/Element/getElementsByTagName)
を使います。

`querySelectorAll` を使う場合、前者に対応するセレクタは `#id .p` になります。  
既存の回答で提案されている `#id p` は後者に対応するセレクタです。  
やろうとしている事がどちらなのか確認し使い分けて下さい。

### `null` の問題

`getElementById` が `null` を返すことは既存の回答にありますね。  
`querySelectorAll`
でスマートに書けるというのはその通りだと思いますが、置き換えられない場合もありますし、チェックする形でもよいのではないでしょうか。

[code]

    // 質問に合わせ、id 名に "id"、クラス名に "p"、宣言に var を使用します
    var a = document.getElementById("id");
    var b = [];
    
    if (a) {
        b = a.getElementsByClassName("p");
    }
    
[/code]

自分の管理外に変数 `a` を参照しているコードがあるなど、全部に `null` のチェックを追加できない場合は、`null`
の代りに空の要素を代入しておく手もあります。

[code]

    var a = document.getElementById("id") || document.createElement("div");
    var b = a.getElementsByClassName("p");
    
[/code]


CC BY-SA 3.0
2017-05-09T07:09:02.620
34577
2017-05-09T07:20:59.750
2017-05-09T07:20:59.750
3054
3054
34501
answer