Document¶
JavaScript を使って Web ページを操作する方法を学ぶ。
Browser environment, specs¶
<https://javascript.info/browser-environment> のノート。
プラットフォーム固有の機能をホスト環境という。JavaScript の標準機能に加えて、何か特別な情報や機能が備わっているということだ。
ブラウザー環境ではグローバルオブジェクト
windowがその一つだ。
DOM (Document Object Model)¶
グローバルオブジェクト document も重要だ。ページ上のあらゆるものにアクセスできる。
BOM (Browser Object Model)¶
ブラウザー環境では BOM という一連のグローバルオブジェクトも用意されている。例えば navigator や location が該当する。
navigator は、ブラウザーと OS に関する情報だ。次のような情報などを含む:
navigator.userAgent: 現在のブラウザーnavigator.platform: プラットフォーム
関数 alert(), confirm(), prompt() もまた BOM の一部だ。
Summary¶
重要な仕様書
調べ物をするときには WHATWG や MDN を関連ワードに加えて検索する。
DOM tree¶
<https://javascript.info/dom-nodes> のノート。
木構造のオブジェクト document から子ノードをたどるようにして、文書の構成要素にアクセスする。
An example of the DOM¶
HTML の構造。各タグはノードだ。
タグでなくてもノードであり得る。例えばテキストノード。
HEADとBODYに関しては、空白文字が一部実際の内容と異なって DOM オブジェクト化されていたり、いなかったりする。
Autocorrection¶
不正な形式の HTML であっても、ブラウザーが適宜修正して DOM オブジェクトを構築する。
以前 scraping をしていてハマったのでここにも記すと、
TABLEのTBODYノードについては、あるものだと決め打った方がいい。
Other node types¶
DOM においてはコメントも独自のノードを形成する。
現場では次の四種のノードを扱うのが普通だ:
文書ノード
documentは DOM の受付のようなオブジェクトだ。要素ノード。HTML タグを表す。
テキストノード
コメントノード
See it for yourself¶
Google Chrome の開発ツール Elements タブで現在の HTML 文書の DOM をチェックできる。そこで確認できるツリーは簡略版であり、テキストノードや空白のみノードは省略されている。このタブおよびサブタブ Styles, Computed, Event Listeners から DOM を直接編集することもできる。
Interaction with console¶
Elements タブで要素を選択した後、Esc キーで Console を開く。変数
$0にそのノードが割り当てられいる。コンソールには
inspect()という関数もある。
Walking the DOM¶
<https://javascript.info/dom-navigation> のノート。
学習者ノート
先にこの節で紹介されるプロパティーをまとめておく。
DOM ノードを与えると、ナビゲーションプロパティーを使って、そのすぐ近くにあるノードにアクセスできる。まずはどのノードに対しても有効なプロパティーである次を憶える:
childNodes直接の子ノード全部。テキストノードなども含む。
firstChildchildNodesの先頭要素に等しい。lastChildchildNodesの末尾要素に等しい。parentNode親ノード。
previousSibling親ノードが自身の親ノードに等しく、自身の直前にあるノード。
nextSibling親ノードが自身の親ノードに等しく、自身の直後にあるノード。
子、親、兄弟関係が基本であるようだ。
ノードにはテキストも要素もコメントも含まれるので、上記のプロパティーでは邪魔なものを拾ってくることがある。要素に限定したプロパティーもある:
childrenfirstElementChildlastElementChildparentElementpreviousElementSiblingnextElementSibling
On top: documentElement and body¶
まずは公式三つを覚える:
<html> = document.documentElement
<body> = document.body
<head> = document.head
Children: childNodes, firstChild, lastChild¶
子ノードは
childNodes,firstChild,lastChildで参照する。子ノードがあるかどうかをテストするにはメソッド
hasChildNodes()を用いる。
DOM collections¶
子ノードコレクションをループで回す実用上の方法は二つ:
for…of文Array.from(childNodes).forEach(), etc.
Siblings and the parent¶
兄弟ノードは
nextSibling,previousSiblingで参照する。あまり使わないかもしれない。親ノードは
parentNodeで参照する。
More links: tables¶
DOM 要素の型によっては、特有の追加的属性が用意されている。例えば TABLE など。「何列目の何行目」のような処理を書きやすい。
Tasks¶
演習問題は実際にブラウザーの開発ツールで HTML を Elements タブで即席で生成して、Console で試せる。
DOM children¶
firstNode と firstElementChild の指すオブジェクト、等々が違うことを理解する。テキストノードが探索の邪魔だということを実感する。
深いノードを document からアクセスしようとすると、ドットが多くなって煩雑に感じる。
The sibling question¶
子が存在することは仮定していい。
Select all diagonal cells¶
私の解答はコメント欄の RGS サンの解答に近い。map() ではなく forEach() を採るしか違いがない。
Searching: getElement*, querySelector*¶
<https://javascript.info/searching-elements-dom> のノート。
木構造に頼らないノード参照方法を習得する。
document.getElementById or just id¶
要素に ID 属性があれば
document.getElementById(elemId)が最も自然な参照方法だ。もしくは ID の値と同じ識別子のオブジェクトを参照することができる。これは
window.elemId,window['elemId']と同じことだ。
querySelectorAll¶
Scraping でおなじみの elem.querySelectorAll(css) も便利だ。戻り値は要素のコレクションだ。
querySelector¶
メソッド elem.querySelector(css) はそのシングル版。全部は要らない場合に採用する。
matches¶
メソッド elem.matches(css) は自身が指定セレクターにマッチするかを判定する。
closest¶
メソッド elem.closest(css) はマッチする要素のうち、先祖方向に最近傍にあるものを返す。elem 自身が合致するならば、それが返る。
学習者ノート
このメソッドはあとでイベント処理を書くときにちょくちょく利用するので覚える。イベントハンドリングにおいて、このメソッドの親へ親へと向かうという性質が利用できる。
getElementsBy*¶
メソッド elem.getElementsBy 系は覚えなくていい。前述の汎用メソッドで事足りる。
Live collections¶
メソッド elem.getElementsBy 系は querySelector 系とは異なり、戻り値の
DOM が live だという性質がある。HTML の構造が変化すると同時に戻り値も変化する。
Summary¶
メソッド elemA.contains(elemB) で、指定要素が自身をルートとする部分木にいるかどうかを判定する。
Tasks¶
Search for elements¶
なるべく querySelector 系を利用する。開発ツールの Console タブで
$() とあわせて $x() でも XPath を指定することで参照できるように能力をつける。
$x('/html/body//table[@id="age-table"]')[0];
$x('/html/body//table//label');
$x('/html/body//table[@id="age-table"]//td')[0];
$x('/html/body//form[@name="search"]')[0];
$x('/html/body//form[@name="search"]/input')[0];
$x('/html/body//form[@name="search"]/input').at(-1);
Node properties: type, tag and contents¶
<https://javascript.info/basic-dom-node-properties> のノート。
DOM node classes¶
要素の型を動的に判定するには constructor.name を調べるなど、これまでに学んだ手法を何か用いればいい。
The nodeType property¶
Node.nodeType の値はノードの型の分類を表す。
1: 要素
3: テキスト
9:
document
Tag: nodeName and tagName¶
Element.tagName は HTML のタグ名。
innerHTML: the contents¶
innerHTML は要素の中身の HTML を文字列として持っている。これを書き換えると、
HTML の構造が対応する内容に変わる。
これはあとでたくさん利用するので覚えておく。
outerHTML: full HTML of the element¶
outerHTML は要素自身+中身の HTML を文字列として持っている。
nodeValue/data: text node content¶
Element ではない Node に対しては nodeValue と data 属性が「中身」に相当する。これらはほとんど同じだ。タイプし易い後者をよく使う。
textContent: pure text¶
textContent は中身のテキスト表現を文字列で表したものとなる。HTML タグが外された内容とでも言おうか。このプロパティーは書き込みが安全であるので好まれる。
More properties¶
標準的な HTML 属性のほとんどは、対応する DOM プロパティーがある。
ブラウザーの Console で
console.dir(elem)を使って要素を出力し、プロパティー一覧を得られる。Elements タブにある Properties を調べてもよい。
Tasks¶
Count descendants¶
項目テキスト(入れ子のそれを含まない)を表示する問題はよく覚えておく。明示的に
firstChild を参照するのがミソ。さらにテキストノードを詳しく見ればいい。
What’s in the nodeType?¶
どう考えても 1 以外の可能性がない。
Tag in comment¶
コメントノードの木構造は一つしかない。
Where’s the document in the hierarchy?¶
最後の小問の __proto__ の連鎖は今までありそうでなかった。こう書くとスーパークラスの名前が順に得られるのか。
Attributes and properties¶
<https://javascript.info/dom-attributes-and-properties> のノート。
HTML 要素に非標準的属性、カスタム属性を定義したときにどうなるか。
DOM properties¶
DOM オブジェクトにはプロパティーやメソッドを自由に追加することができるし、要素の
property に追加することもできる。JavaScript の他のオブジェクトと同じように扱える。
HTML attributes¶
HTML ではタグは属性を持つことができる。あるタグが id などの標準的な属性を持つ場合、それに対応するプロパティが DOM の対応するオブジェクトで作成される。しかし、その属性が非標準のものである場合はそうはいかない。
属性が標準であってもなくても、次のメソッドはそれにアクセスできる方法がある:
Method |
Description |
|---|---|
|
属性があるかどうか |
|
属性の値を得る |
|
属性の値を決める |
|
属性を削る |
elem.attributes で属性を全部参照する。各オブジェクトには name と
value がある。
以上の機能は属性名の大文字小文字を区別しない。値は文字列型だ。
Property-attribute synchronization¶
DOM オブジェクトの属性と JavaScript オブジェクトの属性は同期している。例外的に
input要素のvalue値はそうなっていない。
DOM properties are typed¶
ドット記法で属性にアクセスする場合、値の型がそれらしいものになる。
Non-standard attributes, dataset¶
カスタム属性の標準的な定義方法は、属性名を data- から始めるというものだ。この属性名はユーザーのために予約されている。
elem.datasetでカスタム属性の集合にアクセスでき、さらにここからドットでカスタム属性名のdata-以降の識別子を付けると個々のカスタム属性にアクセスできる。data-order-stateのような属性名はdataset.orderStateのように変名される。
Tasks¶
いろいろな方法を試すこと。
Get the attribute¶
属性値の参照方法は理解を深めて損はない。Scrping のときに選択肢が多いと成功しやすくなるはずだ。
Make external links orange¶
実際にはもっと安定した方法を選びそうだ。コメント欄にある読者の考えも参考にする。
Modifying the document¶
<https://javascript.info/modifying-document> のノート。
HTML 文書を動的に変化させる方法を学ぶ。この章はひじょうに重要だ。
Example: show a message¶
ここに示された静的な HTML にあるボックス要素を動的に表示したい。
Creating an element¶
Method |
Description |
|---|---|
|
新しく要素を生成する |
|
新しくテキストノードを生成する |
呼び出した直後の時点では、生成したノードは文書内に現れていない。
Creating the message¶
先ほどのボックス要素を動的に定義する。生成した DOM オブジェクトのプロパティーを操作する。
プロパティー
classNameで CSS のクラスを割り当てる。プロパティー
innerHTMLに要素の中身部分を HTML コード片で定義する。
Insertion methods¶
何らかの方法で生成したノードを、与えられたノード位置を基準にして挿し込む方法たち。まず先にメソッド一覧を挙げる。それから引数の意味と振る舞いを述べる。
node.append(...nodesOrStrings)末尾ノードとして加える。引数ノードが子になる。
node.prepend(...nodesOrStrings)先頭ノードとして加える。引数ノードが子になる。
node.before(...nodesOrStrings)直前ノードとして加える。引数ノードが兄弟になる。
node.after(...nodesOrStrings)直後ノードとして加える。引数ノードが兄弟になる。
node.replaceWith(...nodesOrStrings)ノードを置き換える。自身を引数ノード全部と取り替える。自身はどこかへ行く。
引数 ...nodesOrStrings はノードか文字列が複数、カンマ区切りで与えられることを示している。文字列の場合には、テキストノードとして node に挿し込まれる。ノードでも文字列でもない場合には、JavaScript の規則に従って文字列に自動変換されたものが扱われる。
テキストを挿入する各方法は、文字をエスケープするのかどうかを確認してから採用すること。
insertAdjacentHTML/Text/Element¶
elem.innerHTML に代入するのと同様の HTML 文字列をノードに挿し込むメソッドもある。
elem.insertAdjacentHTML(where, html)文字列を HTML としてノードに挿し込む
elem.insertAdjacentText(where, text)上のテキスト版
elem.insertAdjacentElement(where, elem2)上の要素版
ここで引数 where は位置を表す。次の文字列のどれか。憶えにくい気がする。
"beforebegin"自身の直前に挿し込む。
"afterbegin"自身の子になるように挿し込む。先頭に来る。
"beforeend"自身の子になるように挿し込む。末尾に来る。
"afterend"自身の直後に挿し込む。
最初のもの以外は、先述のメソッドが手軽な代替手段であるため、ほとんど用いられない。
Node removal¶
ノードの削除には node.remove() を呼び出す。
説明が前後するが、前述の挿入メソッドで引数に既存のノードを指定すると、その既存ノードはページ内で引っ越しをする。この用途では remove() はお呼びでない。
Cloning nodes: cloneNode¶
ノードを複製するには node.cloneNode(deep) を呼び出す。
node.cloneNode(true)は深いコピーを行う:部分木を含めて複製される。node.cloneNode(false)は浅いコピーを行う。node自体しか複製されない。本文の例コードは属性
idも複製してしまうので、何らかの対処をしなくてはならない。
DocumentFragment¶
クラス DocumentFragment はノードを集約したオブジェクトだと考えられる。新規ノードを単体で追加するというより、複数を一気に扱うのに利用する。
Old-school insert/remove methods¶
以下のメソッドは古風なので、自作のコードでは採用を避ける:
parent.appendChild(node)parent.insertBefore(node, nextSibling)parent.replaceChild(node, oldChild)parent.removeChild(node)
A word about document.write¶
これも旧式のメソッドだが、document.write() はロード時にしか意味がない。
Tasks¶
createTextNode vs innerHTML vs textContent¶
innerHTML 以外の方法はテキストを安全に扱うことに注目。
createTextNode() や innerText が何らかのタグ要素を生じることはない。
Clear the element¶
clear(elem) は覚えておくこと。どうしてもループでノードを消すときには、先頭を繰り返し参照する。
Why does “aaa” remain?¶
重要度 1 の問題だが、テーブルはスクレイピングでハマりがちな要素だ。見るほうがいい。この解答の説明が正しいことを開発ツールで確認できる。
Create a list¶
textContent はこの場合は innerText でも同じ。
Create a tree from the object¶
オブジェクトから ul 要素を生成するコードのフォームを覚えておく。
Show descendants in a tree¶
再帰関数と reduce() の相性が悪いことはわかった。
Create a calendar¶
カレンダーの問題を DOM の構築で実装する。Chrome のクセなのか、日曜と月曜に隙間が空く。本書の解答は innerHTML に HTML コード片を文字列で連結していくものだ。
Colored clock with setInterval¶
時計の問題はボタンの有効化・無効化処理も入れておきたい。どちらのボタンも
elem.disabled をそれぞれ更新する。
Insert the HTML in the list¶
リストの問題は after() でコツコツ挿し込むようではダメだ。
insertAdjacentHTML() をすぐに思い付かないとダメだ。
Sort the table¶
テーブルのソート問題。急所が複数あり難しい。
テーブルの全行を参照するには
table.tBodies[0].rowsとする。このテーブルにはヘッダーがある。いったん配列に変換してソートする。ソート済み配列を
table.tBodies[0]にappend()するのが直感的でない。文字列のソートは丁寧に
string.localeCompare()を用いる。これはオブジェクトメソッドだ。
Styles and classes¶
<https://javascript.info/styles-and-classes> のノート。
JavaScript の話題に入る前に、HTML 要素にスタイルを与えるには次の二つの方法があることを確認しておく:
スタイルシートでクラスを定義し、要素の属性
classにそれを指定する。要素の属性
styleにスタイル定義を直接指定する。
JavaScript ではどちらの方法も採れるが、クラスを扱うのが望ましい。それができない場合にのみ elem.style から設定するようにする。
className and classList¶
elem.classNameで CSS クラスの全体にいっぺんにアクセスできる。HTML タグの属性classに対応する。elem.classListで CSS クラスのリストにアクセスすることができる。次の操作用メソッドがある:elem.classList.add("class"),elem.classList.remove("class")クラス
"class"を追加、削除する。削除の場合、指定したクラスがない場合には単に無視されるようだ。elem.classList.toggle("class")クラス
"class"を既に含む場合には削除を、ない場合には追加をする。戻り値の真偽値で実際にはどう処理されたかを区別できる。このメソッドが用意されているという事実は興味深い。
elem.classList.contains("class")クラス
"class"を含んでいるかどうかを判定する。
学習者ノート
elem.className に CSS 内容を代入すると、クラスを定義する文字列全体が置き換わる。しかし、たいていの場合にはクラス一つを追加なり削除なりしたい。この用途にプロパティー elem.classList を利用できる。elem.classList はクラス単位で操作するためのメソッドがある特別なオブジェクトだ。
Element style¶
elem.style で適用されているスタイルにアクセスできる。このオブジェクトはキーがCSS の width, background-color, font-family などに対応する文字列を有する。ただし、名前は camel case 化されている。
Resetting the style property¶
CSS の特定の属性を既定値に戻すには、空文字列を割り当てる。
document.body.style.display = "";
elem.style 自体は read-only であることに注意。全体を上書きしたいときには
elem.cssText を用いる。これは elem.setAttribute('style', ...) と同じことだ。スタイル全体の文字列そのものを割り当てる。
Mind the units¶
単位付きの属性には、単位付きの値を文字列として設定する必要がある。
document.body.style.margin = '20px';
Computed styles: getComputedStyle¶
window.getComputedStyle(elem) で最終的なスタイルを得ることになる。本書の例のような状況だと、属性を直接参照してもまともな値が得られない。次のようにすることで、意味のある値が得られる:
let computedStyle = getComputedStyle(document.body);
alert(computedStyle.marginTop);
alert(computedStyle.color);
学習者ノート
このメソッドは read-only な値を参照するだけなので、上手く使うということは考えないでいい。
getComputedStyle(elem) の用途は具体的なスタイル定義を参照することだ。例えば、空のドキュメント上で次の二つの値を比較するといい:
getComputedStyle(document.body).background;
document.body.style.background;
JavaScript とは離れて、CSS における computed style value と resolved style value の概念の説明がある。
getComputedStyle()は完全な名前を指定する必要がある。
Tasks¶
演習問題は一題だけある。
Create a notification¶
CSS のクラスをオプションで結果的に複数指定できることに注意。前章の演習問題に使われなかった elem.remove() もある。
Element size and scrolling¶
<https://javascript.info/size-and-scroll> のノート。
HTML 要素の width や height など、測量情報を扱いたい。 JavaScript で要素を移動したり配置したりする際に、これらのプロパティーを必要とすることがよくある。要素はすべて矩形であると考えていい。
Sample element¶
冒頭の
DIV#example要素のスタイル定義を見て、ボックスの簡単なスケッチを脳裡に描くようにする。width: 要素の水平方向の長さheight: 要素の垂直方向の長さborder: 要素の枠の幅の長さpadding: 要素の枠と中身の間の空間の幅overflow: スクロールバーを議論するために、このプロパティーを扱う。
マージンは要素自身の部分ではないので考慮しない。
この状況では
widthが垂直スクロールバーの幅を含んた長さであることを覚えておく。一般的には、heightは水平スクロールバーの幅を含むのだろう。中身が多い場合には
padding-bottom部分にあふれることがある。
この章を学習するときにはサンドボックスのページを別ウィンドウで開いて、いちいち確認するといい。
Geometry¶
ボックスに対する測量要素は外側から offset, client, scroll なんとかという呼称になる。
学習者ノート
一瞬だけ JavaScript というより CSS の理解に集中する。 CSS プロパティー
position に固有の値指定をまずは理解しておく。
staticこの値のいちばんの特徴は、プロパティー
positionの既定値だということだ。top,right,bottom,left,z-indexが意味をなさない。relativestaticと比べるとわかりやすい。top,right,bottom,leftの値に基づいて自分自身からの相対オフセットで配置される。z-indexも考慮されるようになる。absolute配置基準が、直近の配置されている祖先要素、または初期の包含ブロックとなる。それから最終的な位置が
top,right,bottom,leftの値により決定する。z-indexも考慮される。fixedabsoluteと似ている。配置基準がもっと外側の祖先になる。ビューポートやページだと考えられる。sticky直近のスクロールする祖先および包含ブロックに対して
top,right,bottom,leftの値に基づいて相対配置される。
position - CSS: Cascading Style Sheets | MDN のコードを変更して感覚をつかむといい。
offsetParent, offsetLeft/Top¶
elem.offsetParent は elem を含む最も近くにある要素への参照であって、ブラウザーが elem の座標を計算するのに用いるものだ。次の規則で決まる:
CSS の
positionがabsolute,relative,fixed,stickyならば、関連要素テーブルの内側にあれば
TD,TH,TABLEのいちばん近い先祖要素それ以外の場合は
BODY
これは null である場合がある。特に display: none なスタイルがならばそうなる。
elemの CSS がdisplay: noneであるか、ページ内にない場合elemがBODY要素またはHTML要素である場合elemの CSS がposition: fixedである場合
elem.offsetLeft および elem.offsetTop はドット量であって、
elem.offsetParent の原点から elem の原点への変位量の水平成分および垂直成分だ。
オフセット量は単位が px である
number型の値だ。
これらのプロパティーが JavaScript を書くときに必要になることはほとんどない。
offsetWidth/Height¶
offsetWidth と offsetHeight はボックスの枠まで含んだ領域に対しての測量だ。冒頭の例でいうと、
offsetWidthは次の量の和となる:CSS の
widthCSS の
paddingの二倍CSS の
borderの二倍
offsetHeightも同様に:CSS の
heightCSS の
paddingの二倍CSS の
borderの二倍
オフセット系測量値がまともに得られるのは、要素やその先祖要素が display: none
以外である必要がある。さもなければ、先祖要素と各測量はそれぞれ null やゼロに評価される。
clientTop/Left¶
elem.clientLeft は elem の左境界の幅を px 単位で表した値と解釈できる。ただし、アラビア語やヘブライ語などの環境では垂直スクロールバーを加味する。つまり、この値は枠幅+バーの幅となる。
elem.clientTop は elem の上境界の幅を px 単位で表した値と解釈できる。水平スクロールバーがボックス上部に現れることはないはずなので、こちらのほうがわかりやすい。
clientWidth/Height¶
elem.clientWidth は elem の両側の詰め物幅+正味の幅の値。垂直スクロールバー幅は除外される。冒頭の例でいうと次の和だ:
CSS の
widthから垂直スクロールバーの幅の長さを引いた長さCSS の
paddingの二倍
elem.clientHeight はその垂直方向版。冒頭の例でいうと次の和だ:
CSS の
heightCSS の
paddingの二倍
scrollWidth/Height¶
elem.scrollWidth は elem.clientWidth に似ているが、水平方向のスクロールアウト部分も込めた全体の長さを表す。冒頭の例ではスクロールアウトがないので
elem.clientWidth に等しい。
elem.scrollHeight: 垂直方向版。
スクロール系の値を elem.style.width や elem.style.height に代入すると、ボックス寸法をその量に拡大してスクロールバーが消える。
scrollLeft/scrollTop¶
elem.scrollLeft はスクロールアウトされて表示されていない部分の水平方向の寸法。冒頭の例ではそれが生じない。
elem.scrollTop は垂直方向版。スクロールバーを下に動かすたびに値が増える。つまり、スクロールアウトされているうちの上の方の高さということだ。
ここまで見てきた測量プロパティーは read-only なのだが、これら二つは書き込み可。例えば elem.scrollHeight を増やせば増やすほど、中身の下の方が画面に出てくるようになる。言い換えると、コードによりスクロールする。
Don’t take width/height from CSS¶
CSS から width, height を直接得てはならない。getComputedStyle() もダメだ。この方法が悪い理由が三つ挙げられている。代わりに本章で見てきたプロパティーを利用することだ。
Tasks¶
What’s the scroll from the bottom?¶
elem.scrollBottom を定義する問題。明らかに枠の寸法の取り扱いが主題になる。
elem.scrollTop の図を見ると、これは枠の寸法を含んでいる。したがって
offsetTop か clientTop のどちらを採用するのかを理解すればいい。
What is the scrollbar width?¶
「十分大きなボックス要素を生成したら、その環境ではスクロールバーの寸法はいくらか」という意味だ。
Place the ball in the field center¶
描画コードをよく書く人間なので、こういう問題は難しくないと思ったら、変な落とし穴がある。
また、Math.round() を使って px 幅にわざわざ変換するのが丁寧らしい。
与えられたデータはたまたまサイズが偶数なので 2 で割るだけでも動くということか?
ボールのほうには client ではなく offset を採用しているのも細かいようだが大事だ。
コメント欄の
field.style.cssTextの解答は観点がいい。
The difference: CSS width versus clientWidth¶
模範解答は後へ行くほど理解の程度が高いことを示しているのだろう。
Window sizes and scrolling¶
<https://javascript.info/size-and-scroll-window> のノート。
ページ全体やウィンドウの寸法を得るにはどうすればよいか。
Width/height of the window¶
スクロールバー部分は寸法として要らない。document.documentElement の client
サイズを得る。
window.innerWidth などはスクロールバーの寸法を含むので、ふつうはありがたくない。
Width/height of the document¶
これらの値については泥臭い手法を採らねばならない。
document.body と document.documentElement の scroll, offset, client 寸法の Math.max() を得る。
Get the current scroll¶
読み取り専用の window.pageXOffset, window.pageYOffset でスクロール位置を得る。
Scrolling: scrollTo, scrollBy, scrollIntoView¶
通常の要素をスクロールさせる方法は前章で述べられたとおり。文書全体についても
documentElement に対して同様の方法でスクロールできるが、次のメソッドを利用する方がいい:
Method |
Description |
|---|---|
|
現在からの相対的な位置を与えてスクロールする |
|
表示部分の左上座標が文書の原点からの変位になるようにスクロールする |
scrollIntoView¶
elem.scrollIntoView(alignToTop) は先頭か末尾にスクロールする。
コメント欄で引数がオブジェクト版のものが存在することが指摘されている。MDN で調べると、スクロールのアニメーション有無を指定することができたりするようだ。
Forbid the scrolling¶
ページ全体でのスクロールを禁止するには次のようにする:
document.body.style.overflow = "hidden";
この手法は利用することがあるので覚えておく。
Coordinates¶
<https://javascript.info/coordinates> のノート。
座標系にはウィンドウ座標系とドキュメント座標系の二つがあると、まず述べている。それぞれ CSS の position: fixed と position: absolute に類似しているという。前者での座標系を clientX, clientY と言い、後者での座標系を
pageX, pageY と言う。
Element coordinates: getBoundingClientRect¶
elem.getBoundingClientRect() はボックスがウィンドウ座標系で得られる。それゆえ、値が負である成分を含むことがある。
実際には
width,heightは負にはならないとある。明らかな注意点として、
rightとbottomの意味は CSS でのそれと異なることがある。
elementFromPoint(x, y)¶
document.elementFromPoint(x, y) はウィンドウ座標系でその位置にある要素を返す。要素が複数ある場合(それは普通のことだ)には、ノードのより内側にある要素が得られる。
視界外の座標を与えると
nullが返る。このメソッドは後の章の演習問題でよく使うので覚えておく。
Using for fixed positioning¶
今回は座標を厳密に指定できるので、新規要素の親子関係はどうでもいい。したがって
document.body.append()を使える。小さな要素を動的に生成する場合には
cssText = "position: fixed; color: red";のような即席の設定方法がよく馴染む。仮に
innerHTMLではなくinnerText版を実装しても十分利用価値がある。断ってあるように、Hello world が表示されている間にスクロールすると、これもズレる。
Document coordinates¶
ウィンドウ座標系とドキュメント座標系の関係は単純な公式で表される。スクロールアウトされた部分の寸法が、座標変換の変位になっている。
Tasks¶
良問と思われる。
Find window coordinates of the field¶
サッカーフィールドの問題はシンプルにやればいいと思う。ここで
getComputedStyle() を思い出せる人ならば、このチュートリアルをそもそも今頃習っていないと思う。
const rect = field.getBoundingClientRect();
`
Upper-left, outer corner: ${rect.x}, ${rect.y}
Bottom-right, outer corner: ${rect.right}, ${rect.bottom}
Upper-left, inner corner: ${rect.x + field.clientLeft}, ${rect.y + field.clientTop}
Bottom-right, inner corner: ${rect.x + field.clientLeft + field.clientWidth}, ${rect.y + field.clientTop + field.clientHeight}
`;
Show a note near the element¶
positionAt(anchor, position, elem) の最初のバージョン。急所が二つある。
topのケースではノート自身の高さが必要になる(その長さだけ上にずらす)。これが大事なのだが、アンカー要素の寸法を client ではなく offset から得ること。与えられたテストデータでは client でもきれいに表示されるが、題意からしてこちらのほうが相応しい。
Show a note near the element (absolute)¶
ドキュメント座標系バージョンはヒントにある手順に従えばいい。問題はそこではなく、サンドボックスを同時に開くとどういうわけか表示が乱れる。
Position the note inside (absolute)¶
前問と合体させても良かったのでは?