JavaScript

提供:senooken JP Wiki
2024年8月27日 (火) 15:49時点におけるSenooken (トーク | 投稿記録)による版 (→‎Expression)

About

JavaScriptはプログラミング言語だ。元はHTMLに埋め込まれウェブブラウザー上で処理をするために作られた言語だが、作業の自動化・GUIの開発・ウェブサーバーの開発など様々な分野に活用されている。

ウェブブラウザーのJavaScriptで可能なことの例を挙げる。

  • HTML文書の要素を追加/変更/削除する。
  • ウェブサーバーに HTTP (HTTPS) で情報を送信する。
  • マウスやキーボードの操作や時間経過に応じて処理を実行する。
  • ウェブブラウザーに情報を記憶させたり読み出したりする (Cookieなど)。

多くの分散SNSのウェブブラウザー版はJavaScriptで実装されており、JavaScriptを実行できない環境 (ターミナル上で動作するテキストブラウザーなど) では利用できない。

GNU socialもQvitterプラグインを使用するならJavaScriptが必要だが、クラシックの場合はJavaScript無しでも閲覧や投稿が可能だ。

JSON

データ記述言語「JSON」は、JavaScriptの構文に多少の制限を加えてデータの記述に特化させたものだ。そのためJavaScriptのパーサーはJSONのコードをそのまま解釈できる。

ECMAScript

JavaScriptの言語仕様はECMAScriptという名前で標準化されている。ECMAScriptには版数があり、仕様改訂を重ねるごとに言語仕様も拡張される。ECMAScriptの版数は古い環境をサポートする上で重要だ。たとえばあるOSの標準ブラウザーがECMAScript 6までの言語仕様しか実装していないなら、それより後に追加された言語仕様はコードに使用できない。

ECMAScript 5.1以前は不定期で仕様改訂されてきたが、2015年に公開されたECMAScript 6以降は年に1度改訂されている。そのため版数はECMAScript 2015のように年で表記されることが多い。

Framework

jQuery

レガシー

jQueryがもういらなくなってきたという意見がある。

  • Webブラウザー間の違いがなくなってきた。
  • 標準のJavaScriptでできることが増えた。

上記2点が大きい。jQueryじゃないとできないことが大幅に減った。標準APIでこなしたほうがいいだろう。

Reference

JavaScript | MDN

Grammar and types

文法とデータ型 - JavaScript | MDN

Basic

宣言

JavaScriptのvarとletの違いとは?

変数宣言の3種類がある。

  • var: 変数宣言。
  • let: ブロックスコープでローカル変数宣言。
  • const: ブロックスコープで読み取り専用。

letはES2015で登場。

varはいくつか問題があった。

  1. 再宣言の許容: var xを何回もできた。let xは1回だけ。2回目はエラー。
  2. 巻き上げ (hoisting): varの宣言は、どこで宣言されていようが、スコープの最上部に巻き上げられる。宣言前に使ってもエラーにならなかった。JavaScriptでは宣言と代入が別扱いで、宣言だけ全体パースを先にする模様。letも同じく巻き上げは発生するが、登場までアクセス不能になっている。
  3. 関数スコープ: PHP同様に、varは関数スコープだけ持つ。波括弧のブロックではスコープを作らない。

基本はletを使ったらいい。

データ型

JavaScript のデータ型とデータ構造 - JavaScript | MDN

Boolean

Boolean - JavaScript | MDN

Falsy (偽値) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN

JavaScriptのBooleanへの変換。非常に重要。falseに変換されるケースのほうが少ないので、そのケースだけ覚えておいて、それ以外trueと思ったらいい。

  • null
  • undefined
  • false
  • NaN
  • 0, 0.0, 0x0, -0, -0.0, -0x0, 0n, 0x0n
  • "", '', ``
  • document.all

例えば、配列は空でもtrueになるし、"false"や"0"も文字列だからtrueになる。

上記の型強制と同じ効果を、論理型コンテキスト以外でも発揮したい場合の方法が、以下の2種類ある。

  1. !! (二重否定)
  2. Boolean関数。

上記が非常に重要。!!は短い。Boolean()は引数が空だとfalseになる。PHPなどの値を使う場合など、変数がそもそも空になる場合を考慮するならBooleanがいい。

Array

Array - JavaScript | MDN

追加

concat

一括関数

配列要素に対して一括処理するメソッドがいくつかある。非常に重要。

  • forEach: 適用結果を返さない。
  • map: 適用結果を返す。
  • find
  • every: 全て満たす場合true。
  • some: 1個でも満たす場合true。
Object

Object (オブジェクト) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN

プロパティー存在判定

jsでのプロパティの存在チェック方法をまとめてみる #JavaScript - Qiita

in演算子がよさそう。

プロパティーのマージ

JavaScriptでオブジェクトをマージ(結合)する方法、JSONのマージをする方法 #Deepcopy - Qiita

Object.assignかスプレッド演算子...で展開・割り当てするとよい。

Expression

this

JavaScriptのthisは文脈 (実行コンテキスト) に応じて対象が異なるので注意が必要。

  • DOM イベントハンドラー: 配置要素。
  • インラインイベントハンドラー: リスナーのDOM要素。
  • グローバル: グローバルオブジェクト。Webブラウザーならwindow。
  • 関数: call/applyで設定しない限り、グローバルオブジェクトかstrictモードならundefined。
  • クラス: constroctor内はただのオブジェクト。他のstaticでないメソッドは、thisのプロトタイプに追加される。
  • メソッド: 呼び出し元のインスタンス。
  • constructor: コンストラクターとしてnewキーワードとセットで使われた場合、インスタンス。

typeof

typeof - JavaScript | MDN

typeof <operand>
typeof <operand>

<operand> の型を示す文字列を返す。

結果
Undefined "undefined"
Null "object" (下記参照)
論理値 "boolean"
Number "number"
BigInt (ECMAScript 2020 の新機能) "bigint"
文字列 "string"
シンボル (ECMAScript 2015 の新機能) "symbol"
関数 オブジェクト (ECMA-262 の用語では [[Call]] を実装) "function"
その他のオブジェクト "object"

注意が必要なものがいくつかある。

  • Null="object"
  • NaN="number"

特にnullに注意が必要。nullの判定が必要なら、=== nullが必要と思われる。

function

IIFE/即時実行関数式

IIFE (即時実行関数式) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN

JavaScriptで即時関数を使う理由 #JavaScript - Qiita

(function () {
    //処理
    console.log('処理1');
}());

(function () {
    //処理
    console.log('処理2');
})();

丸括弧の位置はどちらでもOK。ただ、後者の波括弧直後のほうがイメージとしてはあっている。関数オブジェクト・コールバックを作って、それに引数を渡して使う感じだから。

class

出典: クラス - JavaScript | MDN

JavaScriptのclassは実際には特別な関数。

constructor

constructorメソッドは、オブジェクト生成時の初期化用の特別なメソッド。1個しか定義できない。

コンストラクター内で、インスタンスプロパティーを設定する。newで呼び出す際の引数を受け取る。

Topic

JavaScriptの読込場所

HTMLでのJavaScriptの記述場所が何か所かある。記述方法によって、読込タイミングなどが異なる。

  • head要素内: body要素のDOM構築前なので、大半のDOMアクセス不能。
  • body要素末尾: DOM構築後なので大半のDOMアクセス可能。
  • window.onload=: 読込完了後。イベントハンドラーなので1個しか設定できず上書きで消える。
  • window.addEventListener('load', ): 複数設定可能。
  • document.addEventListener('DOMContentLoaded', ): loadより先。DOM構築後画像類の読込前。

基本はwindow.addEventListener('load', )で設定。これをhead要素内などの冒頭に記述するなどするといい。どこに記述しても問題ない書き方。

Table

テーブルの全チェックオン・オフ

Ref: HTMLのテーブルでチェック行の情報だけ処理するサンプル/PHP・JavaScript - SE_BOKUのまとめノート的ブログ.

テーブルの1列目にチェックボックスを配置して、1行目のチェックボックスで全チェックオン・オフしたいことがある。

<script>
            function AllChanged(){
                var check =  document.getElementById('aaa');
                var checkarr = document.getElementsByName('bbb[]');

                for (var i=0; i<checkarr.length; i++){
                    if(check.checked === true){
                        checkarr[i].checked = true;
                    }else{
                        checkarr[i].checked = false;
                    }

                }
            }
</script>
<body>
    <body>
        <form action="" name="form" method="POST">
        <table>
            <tr>
                <th><input type="checkbox" name="aaa" id="aaa" onClick="AllChanged();" /> </th>
                <th>KEY</th>
                <th></th>
            </tr>
            <tr>
                <td><input type="checkbox" name="bbb[]" value="AAAAAA,100000" onClick="OneChanged();" /></td>
                <td>AAAAAA</td>
                <td>100000</td>
            </tr>
            <tr>
                <td><input type="checkbox" name="bbb[]" value="BBBBBB,200000" onClick="OneChanged();" /></td>
                <td>BBBBBB</td>
                <td>200000</td>
            </tr>
        </table>
        <div>
            <button type="submit" name="b1" id="b1" disabled>ボタンサンプル</button>
        </div>
        </form>
</body>

こんな感じでJavaScriptでチェックボックスを抽出してやる。PHP送信用にvalueに値を入れておく。

集計

javascriptでtableタグのtd内の数値の合計を自動で計算 #JavaScript - Qiita

<html>
<body>
  <table>
    <tr>
      <td>りんご</td>
      <td>300</td>
    </tr>
    <tr>
      <td>バナナ</td>
      <td>400</td>
    </tr>
    <tr>
      <td>いちご</td>
      <td>500</td>
    </tr>
    <tr>
      <td>合計</td>
      <td id='goukei'></td>
    </tr>
  </table>

  <script>
    goukei.textContent = [...document.querySelectorAll('td:not(#goukei)')]
      .map(e => isNaN(e.textContent) ? 0 : +e.textContent)
      .reduce((a, b) => a + b);
  </script>
</body>
</html>

querySelectorAllでやるとスマート。

Web API

DOM

ドキュメントオブジェクトモデル (DOM) - Web API | MDN

Basic

Interface

ドキュメントオブジェクトモデル (DOM) - Web API | MDN

  • NodeList: Node.childNodesか document.querySelectorAll()で取得。Arrayとは異なるが、forEachメソッドで反復処理可能。Array.fromや[...]でArrayに変換可能。
    • Node: インターフェイス。抽象クラス。
      • Document
      • Element: いわゆる要素。
      • DocumentFragment
DOM/Node/Element

DOM、Node、Elementを理解する #JavaScript - Qiita

  • DOM: WebブラウザーAPI。文書をJavaScriptなどで操作するためのAPI。
  • Node: 要素やコメントなどを含む、DOMのObjectの単位。
  • Element: Nodeの一種 (Nodeを継承)。いわゆる要素のオブジェクト。
取得

いくつか方法がある。

  • Document.getElementById(): id属性の値で取得。
  • getElementById(): Elementを取得。
  • getElementsByClassName()
  • getElementsByName()
  • getElementsByTagName()
  • getElementsByTagNameNS()
  • querySelector: Elementを取得。
  • querySelectorAll: NodeListを返す。

querySelectorが使えるならこれを使うのがシンプル。だが、querySelectorより上記のほうが速い。

追加

Node: insertBefore() メソッド - Web API | MDN

要素を追加する方法がいくつかある。

  • Element.append: Nodeか文字列をElementの子の末尾に挿入。文字列の場合、Textノード扱い。
  • Element.prepend: Nodeか文字列をElementの子の先頭に挿入。
  • Element.before: Elementの前に挿入。
  • Element.after: Elementの後に挿入。
  • Element.insertAdjacentElement: append/prepend/before/afterを引数で指定して実行。
  • Node.appenChild: Nodeのみ。追加されたNodeを返却。1個だけ。
  • Node.insertBefore: appendChildの逆。
属性

いくつかある。

  • getAttribute
  • setAttribute(name, value)
  • removeAttribute
クラス

classも属性でもあるが、複数指定したり、よく使うので専用のclassList APIがある。

メソッド名 機能
add クラスを追加する
remove クラスを削除する
contains クラスが含まれているか確認する
toggle クラスが含まれていれば削除、

含まれていなければ追加する

const div = document.createElement("div");
div.className = "foo";

// 最初の状態: <div class="foo"></div>
console.log(div.outerHTML);

// classList API を用いてクラスを除去、追加
div.classList.remove("foo");
div.classList.add("anotherclass");

// <div class="anotherclass"></div>
console.log(div.outerHTML);

// visible が設定されていれば除去し、なければ追加
div.classList.toggle("visible");

// i が 10 未満であるかどうかの条件によって visible を追加または除去
div.classList.toggle("visible", i < 10);

// false
console.log(div.classList.contains("foo"));

// 複数のクラスを追加または除去
div.classList.add("foo", "bar", "baz");
div.classList.remove("foo", "bar", "baz");

// スプレッド構文を使用したクラスの追加または除去
const cls = ["foo", "bar"];
div.classList.add(...cls);
div.classList.remove(...cls);

// "foo" クラスを "bar" クラスで置き換え
div.classList.replace("foo", "bar");

Topic

DOM反復

Ref: javascript - For loop for HTMLCollection elements - Stack Overflow.

Web上のデータの取得でコンソール画面で簡単にやりたいことがある。

Inspectorで右クリック-セレクターのコピー。

https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/renderer_context_menu/link_to_text_menu_observer.cc;bpv=1

let dom = document.querySelector('#mat-tab-content-0-0 > div > history-panel > div > history-table > mat-table')
for (let i = 0; i < dom.children.length; ++i) {
  let child =  dom.children[i].children[1];
  if (child && child.querySelector('a')) {
   console.log(child.querySelector('a').getAttribute('href').replace(/^.*\//, ''));
  }
}

なお、domの反復ではfor (const key in dom)は使えないので注意する。lengthで添字を見ないと、関係ないプロパティーもkeyに入ってくるため。

テーブルの追加

table要素に行(tr要素)を追加 | JavaScript逆引き | Webサイト制作支援 | ShanaBrian Website

// table要素を取得
var tableElem = document.getElementById('sample-table');

// tbody要素にtr要素(行)を最後に追加
var trElem = tableElem.tBodies[0].insertRow(-1);

// td要素を追加
var cellElem = trElem.insertCell(0);

// td要素にテキストを追加
cellElem.appendChild(document.createTextNode('セル'));

なお、th要素を挿入するメソッドなどはない。

outerHTML/innerHTMLかcreateElement/appendChildでやるしかない。

var tr = document.getElementById('table').tHead.children[0];
    tr.insertCell(1).outerHTML = "<th>Second</th>"  // some browsers require the index parm -- 1

outerHTMLがスマート。

子要素の一括削除

いくつか方法がある。

  • innerHTML = "": 簡単だが遅い。
  • textContent = "": HTMLのパースがない分innerHTMLより速い。
  • lastChild: 速い。
  • replaceChild/replaceChildren: 2022年以後はこれ。replaceChildren()で削除になる。

Event

Handler

インラインイベントハンドラーの場合、デフォルトでevent変数が引き渡されている (How to pass event as argument to an inline event handler in JavaScript? - Stack Overflow)。

function name(/*args*/) {body}

上記のbodyが値 (value) になる。argsは通常eventの1個だが、onerrorのみevent/souce/lineno/colno/errorの5引数。

他のイベント処理は自分で指定した引数にEventオブジェクトが渡される。

そして、値部分でのthisはリスナーのDOM要素になる (this - JavaScript | MDN)。

List

イベントリファレンス | MDN

HTMLのフォームと連携する場合に重要なのがイベント。イベントやHTML要素別に発動するイベントが決まっている。「HTML Standard」がその対応。

特に重要なものを抜粋する。

addEventListener

EventTarget: addEventListener() メソッド - Web API | MDN

addEventListener(type, listener)
addEventListener(type, listener, options)
addEventListener(type, listener, useCapture)

一般的にはuseCaptureの書式。デフォルトでは第3引数はfalse。1番目の引数2個の書式で書いておけば基本はOK。

  • useCapture: 論理値。イベント伝播の方式。通常はfalse。trueにするとたぶんイベントを捕捉して終わり。
  • options
    • capture
    • once: 初期値=false。trueにすると1回で自動削除。
    • passive
    • signal

入力判定

フォームの入力後に処理を行いたいことがよくある。例えば、自動補完など。

いくつか使えるイベントがある。

HTMLElement - Web API | MDN

  • beforeinput: inputの前。
  • input: valueの変更時。
  • change: valueが変更され、ユーザーの確定時 (HTMLElement: change イベント - Web API | MDN)。変更されてフォーカスが外れた場合。input+blurのようなイメージ。
  • blur: フォーカスが外れた場合。
  • keyup:

例えば、ユーザーの入力完了後に、そのデータを使って何かを行うならchangeでやるのがいい気がする。

JavaScript操作のイベント

jquery - Activate onchange after changing content with javascript - Stack Overflow

JavaScriptでフォームの値を操作した場合、changeイベント類は発動しない。必要なら、操作後に自分でイベントを発動させる必要がある。

イベントの発動

function triggerEvent(element, event) {
   if (document.createEvent) {
       // IE以外
       var evt = document.createEvent("HTMLEvents");
       evt.initEvent(event, true, true ); // event type, bubbling, cancelable
       return element.dispatchEvent(evt);
   } else {
       // IE
       var evt = document.createEventObject();
       return element.fireEvent("on"+event, evt)
   }
}

こんな古臭い書き方じゃなくて、今は以下でOK。

element.dispatchEvent(new Event(event));

eventは 'change' などのイベント名。

発動順序

"要素Amousedownイベント"
"要素Afocusイベント"
"要素Amouseupイベント"
"要素Aclickイベント"
"要素Abeforeinputイベント"
"要素Bmousedownイベント"
"要素Achangeイベント"
"要素Ablurイベント"
"要素Bfocusイベント"
"要素Bclickイベント"

複数要素ある場合の、イベントの順序。2個目のmousedownが発動してから、1個目のchange/blurが発動する。

localStorage

JavaScriptでのデータ保存。使い勝手がいい。

基本はwindowのloadで復元させる。

localStorage.setItem("myCat", "Tom");

const cat = localStorage.getItem("myCat");

window.addEventListener('load', () => {
  document.getElementById('MaMiShare.host').value =
    localStorage.getItem('MaMiShare.host');
});

Fetch

Ref: フェッチ API - Web API | MDN.

JavaScriptでHTTP通信するための標準的なAPI。

async function logMovies() {
  const response = await fetch("http://example.com/movies.json");
  const movies = await response.json();
  console.log(movies);
}

fetchは引数を2個とる。1個目はリクエストURL。2個目はリクエストパラメーター。

// POST メソッドの実装の例
async function postData(url = "", data = {}) {
  // 既定のオプションには * が付いています
  const response = await fetch(url, {
    method: "POST", // *GET, POST, PUT, DELETE, etc.
    mode: "cors", // no-cors, *cors, same-origin
    cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
    credentials: "same-origin", // include, *same-origin, omit
    headers: {
      "Content-Type": "application/json",
      // 'Content-Type': 'application/x-www-form-urlencoded',
    },
    redirect: "follow", // manual, *follow, error
    referrerPolicy: "no-referrer", // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
    body: JSON.stringify(data), // 本体のデータ型は "Content-Type" ヘッダーと一致させる必要があります
  });
  return response.json(); // JSON のレスポンスをネイティブの JavaScript オブジェクトに解釈
}

postData("https://example.com/answer", { answer: 42 }).then((data) => {
  console.log(data); // `data.json()` の呼び出しで解釈された JSON データ
});

Frame/Window

フレームとウィンドウ

Cross Window

ウィンドウを跨いだやり取り

複数のウィンドウをまたいで、情報や処理を連携する方法がいくつかある。

  • iframe
  • postMessage

検索画面のように、新規タブを開いて、そこでの実行結果を受け取るような場合に使う。

windows.onmessageにコールバックを登録。

新規タブでは、window.opner.postMessageでデータを送信。これでいける。

window.addEventListener("message", function(event) {
  if (event.origin != 'http://javascript.info') {
    // 未知のドメインからの場合は無視しましょう
    return;
  }

  alert( "received: " + event.data );
});
<iframe src="http://example.com" name="example">

<script>
  let win = window.frames.example;

  win.postMessage("message", "*");
</script>

別ウィンドウには以下の2属性でアクセスできる。

  • window.open: 開き先。
  • window.opener: 開き元。

Form

HTMLと連動したForm関係の処理が入出力で非常に重要。よくあるパターンをいろいろメモしておく。

formの送信データ

Ref: JavaScript オンリーで動的に form タグを作ってデータを送信する方法.

フォームから送信時に、自分でデータを作ったりできる。form要素のformdataイベントがポイント。ここで送信直前に送信データを追加したりできる。送信データのデフォルト値や、JavaScriptでいろいろ収集したデータを自動設定できる。