まず前提として、この記事を書いているのは未経験からプログラミングに挑戦中の者になります。学習はフィヨルドブートキャンプに参加しながら取り組んでいて、現役のエンジニアの方からレビューをもらいながら進めています。 現在の学習状況は以下を随時更新しているので興味ある方はご覧ください。 筆者は執筆時点で、Ruby(Ruby on Rails)のプラクティスが終わったあとにJavaScriptに取り組んでいるので、完全に0からではありません。JavaScriptを多言語から学ぶにあたって、まず覚えるべき要点をまとめました。 詳しい解説ではなく要点をまとめた学習記録・ノートになりますのでご注意ください。 記事の本体は以下にになります。長くなってしまったので別記事として書いているものとなります。ご注意ください。 JavaScriptを使うとWebページ上でイベントが発生したときに実行する処理や関数を実行させることができる。イベントが発生した時に実行する処理や関数のことを イベントは Web ページ上でマウスをクリックしたりキーボードのキーを押したときや、表示しようとしている Web ページの読み込みが完了した場合などに発生 input 要素でマウスでクリックしたときに実行するイベントハンドラを登録する場合
ウェブページ上にユーザーが何かイベントをトリガーしたとき(例:ボタンをクリック・要素にマウスを載せるなど)、そのイベントはHTML要素のツリー構造をたどって(例:イベントが発生した要素の親要素である div 要素から、 body 要素、 html 要素、 Document オブジェクト、 Window オブジェクトまで DOM ツリーを上へ向かって順番に click イベントが発生)下から上へ伝播することを「バブリング」と呼ぶ。 これまでの例ではHTMLの中でイベントハンドラを設定していたが、DOMを使用して任意の要素オブジェクトとして取得。その要素オブジェクトに対してイベントハンドラを設定できる。
イベントハンドラとして登録するコールバック関数は、無名関数を使って記述することもできる。 こうすると1度アラートが表示され、二回目以降は何も起こらなくなる。 ※無名関数の場合、関数内で this を参照するとイベントが発生した要素が格納されている 使用例 idテストのボタンをユーザーがクリックしたあら、コールバック関数であるbutotnClickが呼び出されます。 HTML の属性値や DOM で取得した要素のプロパティに対してイベントハンドラを登録した場合、属性値やプロパティは一つの値しか保管できないため、同じイベントに対するイベントハンドラは一つしか登録することができない。 しかし、 addEventListener メソッドを使ってイベントリスナーを登録する場合には、同じターゲットの同じイベントに対して複数のイベントリスナーを登録することができる。 ベントが発生すると登録されている順番に複数のイベントリスナーが呼び出される。 一度だけイベントリスナーが呼びだされると、かんすないでイベントリスナーが解除される。 イベントが発生して登録されたイベントハンドラやイベントリスナーが呼び出されると、一番目の引数に発生したイベントの情報が格納された Event オブジェクトが渡されてくる。 ターゲットとなる要素のプロパティに対してイベントハンドラを登録する場合と、 addEventListener メソッドを使ってイベントリスナーを登録した場合、イベントが発生すると登録したコールバック関数が呼び出され、その時、第一引数に発生したイベントの情報が格納されたEventオブジェクトが渡される。 関数内でEventオブジェクトの情報を使いたい場合に活用できる。 コールバック関数が呼び出されるときに引数として渡されてきた Event オブジェクトのプロパティを参照することで、発生したイベントに関する情報を取得できる。 以下のように使う 使用例 リンクをクリックすれば、リンク先のページに移動したり、チェックボックスをクリックすればチェックが入るなどのデフォルトの動きが設定されているが
「はい」にはチェックがいれられるが、「いいえ」をクリックするとアラートが出現してチェックが入らなくなる。 イベントは HTTP ページをブラウザで見ているユーザーの操作によって発生するが、プログラムの中で新しいイベントを作成し指定した対象でイベントを発生させることがでできる。
はじめに
前提
イベント処理
イベントハンドラ
と呼ぶHTML要素の属性としてイベントハンドラを登録
<input type="button" value="button" onclick="イベントハンドラ">
<body>
<p>ボタンをクリック</p>
<input type="button" value="button" onclick="alert('こんな感じ')" />
</body>
イベントの種類の一覧
属性
イベントの種類
onclick
マウスのボタンが要素の上でクリックされたとき
oncontextmenu
マウスが右クリックされたとき
ondblclick
マウスのボタンが要素の上でダブルクリックされたとき
onmousedown
マウスが要素の上で押されているとき
onmouseup
マウスのボタンが要素の上で離されたとき
onmousemove
マウスが要素の上を移動しているとき
onmouseover
マウスがリスナーが接続されている要素または子要素に移動したとき
onmouseout
マウスがリスナーが接続されている要素または子要素から外れたとき
onselect
テキストが選択されているとき
onwheel
マウスホイールが回転しているとき
onauxclick
マウスのメイン以外のボタン(中央のボタンなど)が要素の上でクリックされたとき
onkeydown
いずれかのキーが押されたとき
onkeyup
いずれかのキーが離されたとき
onkeypress
いずれかのキーが押された状態にあるとき
onblur
要素がフォーカスを失ったとき(バブリングなし)
onfocus
要素がフォーカスを受け取ったとき(バブリングなし)
onchange
要素の値の変更が確定したとき(
<input>, <select>, <textarea>
)
oninput
要素の値が変更したとき(
<input>, <select>, <textarea>
)
onsubmit
サブミットボタンが押されたとき
onreset
リセットボタンが押されたとき
onload
ページ全体がスタイルシートや画像などのすべて読み込まれたとき
onabort
エラー以外の原因で読み込みが中止されたとき
onerror
エラーが発生して読み込みが失敗したとき
バブリング・キャプチャリングとは?
DOMで取得した要素のプロパティにイベントハンドラを登録
要素オブジェクトのプロパティにイベントハンドラを登録
<body>
<input type="button" value="button" id="test" />
<script>
const buttonClick = () => {
alert("これを表示させる");
};
let button = document.getElementById("test");
button.onclick = buttonClick;
</script>
</body>
イベントハンドラにコールバック関数を設定する
<script>
const buttonClick = () => {
let day = new Date();
alert(day);
};
let button = document.getElementById("test");
button.onclick = buttonClick;
</script>
</body>
イベントハンドラを無名関数やアロー関数を使って記述
<script>
let button = document.getElementById("test");
button.onclick = function () {
let day = new Date();
alert(day);
};
</script>
イベントハンドラを解除する
button.onclick = null;
<script>
let button = document.getElementById("test");
button.onclick = function () {
let day = new Date();
alert(day);
// 1度呼び出したら解除する
this.onclick = null;
};
</script>
コールバック関数が呼び出される時にイベントの情報を受け取る
<script>
function butotnClick(event){
// 何かしらの処理
}
let button = document.getElementById('test');
button.onclick = butotnClick;
</script>
<body>
<p>ボタンをクリックしてください。</p>
<input type="button" value="button" id="test" />
<script>
let button = document.getElementById("test");
button.onclick = function (event) {
console.log("eventのタイプ " + event.type);
console.log("eventのターゲット " + event.target);
};
</script>
</body>
// eventのタイプ click
// eventのターゲット [object HTMLInputElement]
addEventListenerメソッドを使ってイベントリスナーを登録
addEventListener
メソッドを使用してイベントに対して登録した関数のことを特にイベントリスナーと呼ぶtarget.addEventListener(type, callback [, options])
<input type="button" value="button" id="test">
<script>
function butotnClick(){
console.log('お買い上げありがとうございます');
}
let button = document.getElementById('test');
button.addEventListener('click', butotnClick);
</script>
<input type="button" value="button" id="test" />
<script>
let button = document.getElementById("test");
button.addEventListener("click", function () {
console.log("テストします");
});
</script>
<input type="button" value="button" id="test" />
<script>
let button = document.getElementById("test");
button.addEventListener("click", () => {
console.log("テストします");
});
</script>
同じターゲットの同じイベントに対して複数のイベントリスナーを登録
<input type="button" value="button" id="test" />
<script>
function dispGreeting() {
console.log("こんにちは");
}
function dispGoodNight() {
console.log("こんばんは");
}
let button = document.getElementById("test");
button.addEventListener("click", dispGreeting);
button.addEventListener("click", dispGoodNight);
</script>
// こんにちは
// こんばんは
イベントリスナーを解除
target.removeEventListener(type, callback [, options])
<input type="button" value="button" id="test" />
<script>
function buttonClick() {
console.log("こんにちは!これで最後です。");
this.removeEventListener("click", buttonClick);
}
let button = document.getElementById("test");
button.addEventListener("click", buttonClick);
</script>
arguments.callee
を指定する発生したイベントの情報を取得する(Event)
Eventオブジェクトの受け取り
<script>
function butotnClick(event){
console.log('こんにちは');
}
let button = document.getElementById('test');
button.addEventListener('click', butotnClick);
</script>
Eventオブジェクトで取得できる情報
プロパティ名
説明
Event.eventPhase
イベントのフェーズ(キャプチャリングフェーズ、ターゲットフェース、バブリングフェーズ)
Event.type
イベントの種類を表す文字列
Event.target
イベントが発生した要素
Event.isTrusted
イベントがユーザーの操作によって発生したかどうか
Event.bubbles
イベントがバブリングするかどうか
Event.timeStamp
要素が作成されてからイベント発生までの経過時間(ミリ秒)
Event.cancelable
イベントがキャンセル可能かどうか
Event.currentTarget
イベントハンドラを登録した要素
event.type
<p>ボタンをクリックしてください。</p>
<input type="button" value="button" id="xxx" />
<script>
let button = document.getElementById("xxx");
button.addEventListener("click", function (event) {
console.log("bubbles :" + event.bubbles);
console.log("cancelable :" + event.cancelable);
console.log("currentTarget :" + event.currentTarget);
console.log("eventPhase :" + event.eventPhase);
console.log("target :" + event.target);
console.log("timeStamp :" + event.timeStamp);
console.log("type :" + event.type);
console.log("isTrusted :" + event.isTrusted);
});
</script>
// bubbles :true
// cancelable :true
// currentTarget :[object HTMLInputElement]
// eventPhase :2
// target :[object HTMLInputElement]
// timeStamp :1392.199999988079
// type :click
// isTrusted :true
Event.currentTargetプロパティとEvent.targetプロパティの違い
プロパティ名
説明
Event.currentTarget
イベントハンドラを登録した要素
Event.target
イベントが発生した要素
Event.curentTarget
は以下のようなイベントハンドラを登録したターゲットの要素を指す。let button = document.getElementById('xxx');
button.addEventListener('click', butotnClick);
Event.target
は実際にイベントが発生した要素を指します。イベントに対するデフォルトの動作をキャンセルする(preventDefault)
preventDefault
を使用するとキャンセルすることができる。event.preventDefault()
Event.cancelable
プロパティの値が true のイベントのみキャンセル可能 <body>
<div>
<input type="checkbox" id="check1" />
<label for="check1">はい</label>
<input type="checkbox" id="check2" />
<label for="check2">いいえ</label>
</div>
<script>
let checkBox = document.getElementById("check2");
checkBox.addEventListener("click", function (event) {
alert("現在変更できません");
event.preventDefault();
});
</script>
</body>
イベントをコードから発生させる(dispatchEvent)
new Event(eventtype[, option])
let e = Event('click');
イベントを発生させる
target.dispatchEvent(event)
let e = Event('click');
let target = document.getElementById('test');
target.dispatchEvent(e);
Event.isTrusted
プロパティを参照することで判別できる <body>
<div>
<input type="button" id="btn1" value="button1" />
<input type="button" id="btn2" value="button2" />
</div>
<script>
let btn1 = document.getElementById("btn1");
let btn2 = document.getElementById("btn2");
btn1.addEventListener("click", function (event) {
console.log("buttonA:" + event.isTrusted);
let newEvent = new Event("click");
btn2.dispatchEvent(newEvent);
});
btn2.addEventListener("click", function (event) {
console.log("buttonB:" + event.isTrusted);
});
</script>
</body>
// ボタン1を押した場合
//buttonA:true
//buttonB:false
// ボタン2を押した場合
//buttonB:true
buttonA:true
が表示され、そのあとにクリックイベントを作成し、 dispatchEvent メソッドによってボタン2もユーザーの行動によらず押される。そのためボタンBのisTrusted
ではfalseとなっている。
【初学者用まとめ(DOM操作編)】まず覚えるべきJavaScriptのコードの書き方(自習記録)
まず前提として、この記事を書いているのは未経験からプログラミングに挑戦中の者になります。学習はフィヨルドブートキャンプに参加しながら取り組んでいて、現役のエンジニアの方からレビューをもらいながら進めています。 現在の学習状況は以下を随時更新しているので興味ある方はご覧ください。 筆者は執筆時点で、Ruby(Ruby on Rails)のプラクティスが終わったあとにJavaScriptに取り組んでいるので、完全に0からではありません。JavaScriptを多言語から学ぶにあたって、まず覚えるべき要点をまとめました。 詳しい解説ではなく要点をまとめた学習記録・ノートになりますのでご注意ください。 この記事の本体は以下のなります。この記事はその記事の一部になりますので、そちらからご覧ください。 Document オブジェクトの 例:id属性の値として'button'が設定された要素ノードを取得する場合 Document オブジェクトの getElementsByTagName メソッドは、要素のタグ名を指定して一致する要素ノードをすべて取得する。 練習してみた HTML ボタンを押してみると実行できました😄
例
idの属性の値が しかしまとめて記述することもできる。
犬・猫だけ表示ができました〜 ボタンを押すと以下の要素が取得できる
このようにnameを指定して対象の要素ノードを操作できる。 CSS(Cascading Style Sheet) セレクタは、 HTMLページに対してスタイルをどの要素に設定するのかを指定する時に使用する。 全要素を対象とする(*)
- すべての要素ノードを対象とする場合は タグ名を指定する
pタグを全て取得する id属性の値を指定(#id)
要素に設定された id 属性の値を指定する場合は、 class属性の値を指定(.class)
要素に設定された class 属性の値を指定する場合 複数のセレクタのいずれかに一致する(A B C)
セレクタをカンマ(,)で区切って記述することで、複数のセレクタの少なくともいずれか一つに一致する要素ノードを取得できる。 以下の例では、class属性がmy_class2・id属性がmy_idいずれかに該当する要素を取得しています。 Aのセレクタに一致する要素の子孫要素の中でBのセレクタに一致する(A B)
セレクタを空白で区切ることで、最初に一致した要素の子孫要素の中で、さらに要素を指定して選択することができる。 このようにid属性のmainの中のclass属性greetingだけを取得できている。 Aのセレクタに一致する要素の子要素の中でBのセレクタに一致する(A > B)
セレクタを > で区切って記述することで、最初のセレクタに一致する要素の子要素の中で、次に記述したセレクタに一致する要素ノードを取得する。 Aのセレクタに一致する要素の中で属性値Bを持つ要素に一致する(A[B]) getElementById メソッドや parentNode プロパティを参照することで、このノードの親ノードを取得できる。 id属性がtestの要素を取得して変数に格納、その変数に対して 戻り値はノードの種類を表す unsigned short 型の値(数字)が返ってくる。 odeName プロパティを参照するとノードの名前を取得することができる。 Node オブジェクトの nodeValue プロパティを参照するとノードの値を取得することができる。 これらを 新しいテキストを設定する
また参照するだけでなく新しい値を設定できる。 使い方 これを nnerText プロパティは参照するだけではなく新しい値を設定することができる。 表示されているpタグの文字列をボタンを押して、新しい文字を設定して変化させる。 こんな感じで変化する
innerHTML プロパティを参照すると要素に含まれる HTML 文を取得することができる。 上記のHTMLは以下のように取得できる innerHTML プロパティは参照するだけではなく新しい値を設定することもできる。 実演 実行したらこんな感じ。文字だけでなくタグも変わっている。
これを 要素に設定された属性値は以下のようにプロパティ名として属性名を指定して参照することができる。 例 属性名を表すプロパティに対して値を設定することで、属性値に新しい値を設定することができる。 要素の属性名を取得するもう一つの方法として setAttribute メソッドは指定した属性名の属性値に新しい値を設定する。
- 第一引数に属性名
- 第二引数に属性値を指定
- 属性名に対して値が設定されていた場合は新しい属性値が設定される
- 属性名に値が設定されていなかった場合は、新しく属性名に対して属性値が設定される
emoveAttribute メソッドを使用することで要素に設定されている属性を削除することができる 例 上記の例は新しくpタグを作成して先頭にテストという文字列を設定して変数に入れる。次にidのtestを取得し、そこの先頭に先ほど作った文字列を入れている。 同じように最後にも追加ができる。 insertBefore メソッドはノードを子ノードの中の指定ノードの前に追加する。
currentScriptプロパティを参照すると現在実行している これをブラウザで実行するscriptタグがコンソールに表示される。 以下のように取得したScriptタグに要素を追加するなどの操作が行える。 実行するとブラウザに 文字列の形式で HTML コードを書き込むことができるが、現在 HTML に関する仕様を定めている HTML Living standard ではこのメソッドの使用について推奨はしていない。 Document.currentScript プロパティと insertAdjacentHTML メソッドを組み合わせて代替するなどの方法がよさそう。
はじめに
前提
DOM操作
DOMとは
DOMの階層構造
ノード間の関係
id属性の値を指定して要素ノードを取得(getElementById)
getElementById
メソッドは、要素の id 属性の値を指定して一致する Element オブジェクトを取得するdocument.getElementById(id)
let element = document.getEkementById('button');
タグ名を指定して要素ノードを取得(getElementsByTagName)
document.getElementsByTagName(tagname)
div
や p
など)*
を指定した場合はすべての要素に一致
length
プロパティと item
メソッド、namedItem
メソッドが用意されているため取得した要素ノードの数を取得したり、指定した要素ノードを取したりできるlet elements = document.getElementsByTagName("div");
// 取得した要素の数を取得
let count = elements.length;
// インデックスを指定して要素を取得
let element = elements.item(0);
// インデックスを指定して要素を取得その2
let element = elements[0];
// ID属性またはname属性を指定して要素を取得
let element = elements.item("user");
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>getElementsByTagNameの練習!!</title>
</head>
<body>
<h1>わいの好きなもの</h1>
<div>
<p>ラーメン</p>
<p>寿司</p>
</div>
<div>
<p>猫</p>
<p>犬</p>
</div>
<button onClick="getElements();">要素をコンソールに表示させる</button>
<script>
function getElements() {
let elements = document.getElementsByTagName("p");
for (let i = 0; i < elements.length; i++) {
console.log("Text: " + elements.item(i).textContent);
}
}
</script>
</body>
</html>
特定の要素の子孫要素に限定して要素ノードを取得
list
の要素ノードの子孫の中からdivタグの要素ノードを取得する場合は通常以下のように2行にわたって記述が必要let element = document.getElementById('list');
let elements = element.getElementsByTagName('div');
let elements = document.getElementById('list').getElementsByTagName('div');
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>getElementsByTagNameの練習!!</title>
</head>
<body>
<h1>わいの好きなもの</h1>
<div id="food">
<p>ラーメン</p>
<p>寿司</p>
</div>
<div id="other">
<p>猫</p>
<p>犬</p>
</div>
<button onClick="getElements();">犬・猫だけを表示</button>
<script>
function getElements() {
let elements = document
.getElementById("other")
.getElementsByTagName("p");
for (let i = 0; i < elements.length; i++) {
console.log("Text: " + elements.item(i).textContent);
}
}
</script>
</body>
</html>
class属性の値を指定して要素ノードを取得(getElementsByClassName)
getElementsByClassName
メソッドは、要素の class 属性の値を指定して一致する要素ノードをすべて取得する。document.getElementsByClassName(classnames)
length
メソッドやインデックスを指定しtえ要素を取得することができる)length
が0のHTMLCollection オブジェクトが返ってくる<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>getElementsByClassNameの練習</title>
</style>
</head>
<body>
<p>[適当な日記]</p>
<div class="content">
<p>今日はこんな場所に行きました〜</p>
<p class="content">行き先1:公園</p>
<p class="content">行き先2:スーパー</p>
</div>
<button onClick="getElements();">要素を取得</button>
<script>
function getElements(){
let elements = document.getElementsByClassName('content');
// 取得した値を使ってやりたい処理を書く
</script>
</body>
</html>
<div class="content">
<p>今日はこんな場所に行きました〜</p>
<p class="content">行き先1:公園</p>
<p class="content">行き先2:スーパー</p>
</div>
<p class=<200b>"content"><200b>行き先1:公園<200b></p><200b>
<p class=<200b>"content"><200b>行き先2:スーパー<200b></p><200b>
name属性の値を指定して要素ノードを取得(getElementsByName)
getElementsByName
メソッドは、要素の name 属性の値を指定して一致する要素ノードをすべて取得する。document.getElementsByName(name)
length
プロパティと item
メソッドが使える<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>getElementsByNameの練習</title>
</head>
<body>
<p>メニュー</p>
<label><input type="radio" name="food" value="Coffee" />coffee</label>
<label><input type="radio" name="food" value="Cake" checked />Cake</label>
<label><input type="radio" name="food" value="Tea" />Tea</label>
<button onClick="getElements();">要素を取得</button>
<script>
function getElements() {
let elements = document.getElementsByName("food");
console.log(elements);
for (let i = 0; i < elements.length; i++) {
if (elements.item(i).checked) {
console.log(elements.item(i).value + " is checked");
} else {
console.log(elements.item(i).value + " is not checked");
}
}
}
</script>
</body>
</html>
CSSセレクタ形式の条件に一致する要素ノードを取得(querySelector, querySelectorAll)
querySelector
メソッド・querySelectorAll
メソッドはCSSのセレクタ形式で条件を指定して一致する要素を取得できる。document.querySelector(selectors)
document.querySelectorAll(selectors)
CSSセレクタの種類と指定方法
*
と記述
- HTML全体を指定するlet elements = document.querySelectorAll('*');
div
p
h
タグなどを指定する場合は、タグ名をそのまま記述。let elements = document.querySelectorAll('p');
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>getElementsByNameの練習</title>
</head>
<body>
<h2>メニュー</h2>
<p>要素1</p>
<p>要素2</p>
<p>要素3</p>
<button onClick="getElements();">要素を取得</button>
<script>
function getElements() {
let result = document.querySelectorAll("p");
console.log(result); // NodeList(3) [p, p, p]
console.log(result[0]); // <p>要素1</p>
console.log(result[1]); // <p>要素2</p>
console.log(result[2]); // <p>要素3</p>
}
</script>
</body>
</html>
#値
と記述する <body>
<h2>メニュー</h2>
<div id="content">
<p>ここを取得</p>
</div>
<div>
<p>ここは取得しない</p>
</div>
<button onClick="getElements();">要素を取得</button>
<script>
function getElements() {
let result = document.querySelectorAll("#content");
console.log(result); // NodeList [div#content] <p>ここを取得</p>の要素が取得できる
}
</script>
</body>
<body>
<h2>メニュー</h2>
<div class="my_class">
<p>ここを取得</p>
</div>
<div>
<p>ここは取得しない</p>
</div>
<button onClick="getElements();">要素を取得</button>
<script>
function getElements() {
let result = document.querySelectorAll(".my_class");
console.log(result); // NodeList [div.my_class]
}
</script>
</body>
<body>
<h2>メニュー</h2>
<div id="my_id">
<p>ここを取得</p>
</div>
<div>
<p class="my_class">ここは取得しない</p>
</div>
<button onClick="getElements();">要素を取得</button>
<script>
function getElements() {
let result = document.querySelectorAll("#my_id, .my_class");
console.log(result); // NodeList(2) [div#my_id, p.my_class]
}
</script>
</body>
<body>
<div id="main">
<p class="greeting">こんにちは</p>
<div>
<p class="other">元気です</p>
</div>
</div>
<button onClick="getElements();">要素を取得</button>
<script>
function getElements() {
let result = document.querySelectorAll("#main .greeting");
console.log(result); // NodeList [p.greeting]
}
</script>
</body>
</head>
<body>
<div id="main">
<p class="greeting">こんにちは</p>
<div>
<p class="greeting">こんばんは</p>
</div>
</div>
<button onClick="getElements();">要素を取得</button>
<script>
function getElements() {
let result = document.querySelectorAll("#main > p");
console.log(result); // NodeList [p.greeting] こんにちはだけ取得できた
}
</script>
</body>
<body>
<div>
<p style="font-size: 10px">こんにちは</p>
</div>
<div>
<p class="other">その他</p>
</div>
<button onClick="getElements();">要素を取得</button>
<script>
function getElements() {
let result = document.querySelectorAll("p[style]");
console.log(result); // NodeList [p] こんにちはだけ取得できた
}
</script>
</body>
ノードの子・親・兄弟ノードを取得
すべての子ノードを取得
querySelector
メソッドなどを使って要素ノードを取得したあと、 Node オブジェクトの childNodes
プロパティを参照することで、このノードの 1 つ下の階層にある子ノードをすべて取得することができる
使い方
node.childNodes
<body>
<div id="element">
<p>要素1</p>
<p>要素2</p>
</div>
<button onClick="getElements();">要素を取得</button>
<script>
function getElements() {
let result = document.getElementById("element");
console.log(result.childNodes); // NodeList(5) [text, p, text, p, text]が取得できる
console.log(result.childNodes[1]); // <p>要素1</p>が取得できる
console.log(result.childNodes[3]); // <p>要素2</p>が取得できる
}
</script>
</body>
最初の子ノードと最後の子ノードを取得
node.firstChild
firstChild
プロパティを参照することで、このノードの 1 つ下の階層にある子ノードの中の最初のノードを取得することができる(参照専用)node.lastChild
同じ階層の次のノードと前のノードを取得
node.nextSibling
node.previousSibling
previousSibling
プロパティを参照することで、このノードと同じ階層にある一つ前のノードを取得できる(参照専用)親ノードを取得する
node.parentNode
<body>
<div>
<p>要素1</p>
<p id="test">要素2</p>
</div>
<button onClick="getElements();">要素を取得</button>
<script>
function getElements() {
let result = document.getElementById("test");
let parent = result.parentNode;
while (parent) {
console.log(parent.nodeName); // NodeList(5) [text, p, text, p, text]が取得できる
parent = parent.parentNode;
}
}
</script>
</body>
parentNode
で親ノードを取得。
それを繰り返し分の中で再度変数に入れ直すことで親の親の親と取得でき最終的に#document
まで取得できた。実行結果
DIV
BODY
HTML
#document
ノードの種類を確認する
nodeType
プロパティを参照するとノードの種類を確認することができる。node.nodeType
1 ELEMENT_NODE 要素ノード
2 ATTRIBUTE_NODE 属性ノード
3 TEXT_NODE テキストノード
4 CDATA_SECTION_NODE CDATAセクション
5 ENTITY_REFERENCE_NODE 実体参照
6 ENTITY_NODE 実体宣言
7 PROCESSING_INSTRUCTION_NODE 処理命令
8 COMMENT_NODE コメントノード
9 DOCUMENT_NODE ドキュメント
10 DOCUMENT_TYPE_NODE ドキュメントタイプ
11 DOCUMENT_FRAGMENT_NODE ドキュメントの断片
12 NOTATION_NODE 記法
<p>今日は外でランチを食べました</p>
<div id="blog">
<p>朝から外出していたのでランチは外で頂きました。</p>
<p>場所:港区南青山</p>
<p>店名:リストランテ南青山</p>
</div>
<button onClick="getElements();">要素を取得</button>
<script>
function getElements() {
let elements = document.getElementById("blog");
let children = elements.childNodes;
let len = children.length;
for (let i = 0; i < len; i++) {
console.log(children.item(i).nodeType);
}
}
</script>
</body>
// 3
// 1
// 3
// 1
// 3 このように取得できる。必要に応じてこれを活かして文字列に変換する
ノードの名前を取得
node.nodeName
<!DOCTYPE html>
<html>
<body>
<p>This is a paragraph.</p>
<h1>This is a heading.</h1>
</body>
</html>
// ページ内の要素を取得
let paragraphElement = document.querySelector("p");
let headingElement = document.querySelector("h1");
// nodeNameプロパティを使用して要素の名前を取得
let paragraphName = paragraphElement.nodeName;
let headingName = headingElement.nodeName;
// 結果をコンソールに表示
console.log("段落要素の名前: " + paragraphName);
console.log("見出し要素の名前: " + headingName);
// 段落要素の名前: P
// 見出し要素の名前: H1
ノードの値を参照
node.nodeValue
let paragraph = document.querySelector("p");
let textInsideParagraph = paragraph.firstChild.nodeValue;
console.log(textInsideParagraph);
// This is a paragraph.
let new = paragraph.firstChild.nodeValue = "新しいテキスト";
console.log(new);
// 新しいテキスト
ノードの値を設定
nodeValue
プロパティは参照するだけではなく新しい値を設定することもできる。node.nodeValue = 'value'
let paragraph = document.querySelector("p");
let textInsideParagraph = paragraph.firstChild.nodeValue;
console.log(textInsideParagraph);
// This is a paragraph.
let textInsideParagraph
ノード内のテキストを取得・設定(textContent)
textContent
プロパティを参照すると、対象のノードに含まれるテキストを取得する。node.textContent
<div>
<p>要素1</p>
<p>要素2</p>
</div>
textContent
で取得すると要素1
と要素2
が取得できる。node.textContent = 'value'
変換前: <p>サンプル</p>
変換後: <p>サンプル</p>
要素内のレンダリングされたテキストを取得・設定(innerText)
テキストを参照する
htmlElement.innerText
タグで改行が行わている場合は改行された状態でテキストを取得<div>
<p>サンプル1<br>サンプル2</p>
<p>サンプル3</p>
</div>
innerText
で取得すると以下のようになる。サンプル1
サンプル2
サンプル3
新しいテキストを設定する
htmlElement.innerText = 'value'
<body>
<div id="test">ここが変化するよ〜</div>
<button onClick="textchage();">テキストが変わる魔法ボタン</button>
<script>
function textchage() {
let element = document.getElementById("test");
element.innerText = "ほら、変わった!";
}
</script>
</body>
HTML文を参照する(innerHTML)
element.innerHTML
<div id="test">
<p>サンプル1<br>サンプル2</p>
<p>サンプル3</p>
</div>
let elements = document.getElementById("test")
console.log("element.innerHTML")
<p>サンプル1<br>サンプル2</p>
<p>サンプル3</p>
<div id="test">
の要素の中身を取得するので、外側のdiv
タグは含まれない。取得した要素の内側にdiv
タグがあればもちろん含まれる。新しいHTML文を設定する
element.innerHTML = 'value'
<body>
<div id="test">ここをh1タグの見出しに変えます</div>
<button onClick="setTitle();">ここを押したら変わるよ</button>
<script>
function setTitle() {
let element = document.getElementById("test");
element.innerHTML = "<h1>でかい見出しになった!!</h1>";
}
</script>
</body>
要素自身も含めたHTML文を参照する(outerHTML)
outerHTML
プロパティを参照すると要素と要素に含まれる HTML 文を取得できる<div>
<p>サンプル1<br>サンプル2</p>
<p>サンプル3</p>
</div>
outerHTML
で取得すると以下のようになる。そのままになる。<div>
<p>サンプル1<br>サンプル2</p>
<p>サンプル3</p>
</div>
要素の属性値を取得・設定(getAttribute,setAttribute)
属性値を取得
element.属性名
let element = document.getElementById(test');
console.log(element.children[0].href);
console.log(element.children[1].className);
console.log(element.children[1].title);
console.log(element.children[1].href);
undefined
属性値を設定
element.属性名 = 'value'
let element = document.getElementById('test');
element.class = 'test';
let element = document.getElementById('shopinfo');
console.log(element.children[0].getAttribute('href'));
console.log(element.children[1].getAttribute('class'));
属性値を取得(getAttributeメソッド)
getAttribute
メソッドがある。指定した属性名の属性値を取得する。element.getAttribute(qualifiedName)
属性値を設定(setAttributeメソッド)
書き方
element.set Attribute(qualifiedName, value)
let element = document.getElementById(' test');
element.setAttribute('class', 'test'); // 新たにclass属性をtestとして設定
要素にせってされている属性を削除(removeAttribute)
書き方
element.removeAttribute(qualifiedName)
ノードを子ノードの中の先頭または最後に追加(prepend,append,appendChild)
ノードを子ノードの先頭に追加する(prepend)
parentnode.prepend(node[,node,...])
prepend
メソッドはノードを子ノードの先頭に追加する。
let child = document.createElement('p');
child.prepend('テスト');
let parentnode = document.getElementById('test');
parentnode.prepend(child);
ノードを子ノードの最後に追加する(append)
parentnode.append(node[,node,...])
ノードを子ノードの最後に追加する(appendChild)
appendChild
メソッドはノードを子ノードの最後に追加する。parentnode.appendChild(node)
ノードを子ノードの中の指定ノードの前または後ろに追加(before,insertBefore,after)
ノードを子ノードの中の指定ノードの前に追加する(before)
before
メソッドはノードを子ノードの中の指定ノードの前に追加する。childnode.before(node[,node,...])
let childnode = document.getElementById('test');
let child = document.createElement('li');
child.append('要素を前に追加');
childnode.before(child);
ノードを子ノードの中の指定ノードの前に追加する(insertBefore)
parentnode.insertBefore(node, childnode)
<body>
<ul id="list">
<li id="test1">要素1</li>
<li id="test2">要素2</li>
</ul>
<button onClick="getElement();">ノードを追加</button>
<script>
function getElement() {
let child = document.createElement("li");
child.append("追加する要素");
let parentnode = document.getElementById("list");
let childnode = document.getElementById("test2");
parentnode.insertBefore(child, childnode);
}
</script>
</body>
ノードを子ノードの中の指定ノードの後に追加する(after)
childnode.after(node[,node,...])
after
メソッドはノードを子ノードの中の指定ノードの後に追加する <body>
<ul id="list">
<li id="test1">要素1</li>
<li id="test2">要素2</li>
</ul>
<button onClick="getElement();">ノードを追加</button>
<script>
function getElement() {
let child = document.createElement("li");
child.append("追加する要素");
let childnode = document.getElementById("test2");
childnode.after(child);
}
</script>
</body>
既存のノードを追加する
<body>
<ul id="list">
<li id="test1">要素1</li>
<li id="test2">要素2(これを要素4の後ろに追加する)</li>
</ul>
<ul id="list2">
<li id="test3">要素3</li>
<li id="test4">要素4</li>
</ul>
<button onClick="getElement();">ノードを追加</button>
<script>
function getElement() {
let child = document.getElementById("test2");
let childnode = document.getElementById("test4");
childnode.after(child);
}
</script>
</body>
ノードを別のノードに置換(replaceWith)
ノードを別のノードに置き換える(replaceWith)
childnode.replaceWith(node[,node,...])
<body>
<ul id="list">
<li id="test1">要素1</li>
<li id="test2">要素2(これを置き換える)</li>
</ul>
<button onClick="getElement();">ノードを追加</button>
<script>
function getElement() {
let child = document.createElement("li");
child.append("置き換わった!");
let childnode = document.getElementById("test2");
childnode.replaceWith(child);
}
</script>
</body>
ノードを削除(remove, removeChild)
ノードを削除する(remove)
childnode.remove()
let childnode = document.getElementById('removeId');
childnode.remove();
ノードを削除する(removeChild)
<script>
function getElement() {
let parentNode = document.getElementById("list");
parentNode.removeChild(parentNode.lastElementChild);
}
</script>
HTMLを表す文字列からノードを作成し指定の位置に追加(insertAdjacentHTML)
insertAdjacentHTML
メソッドを使用すると、引数に指定した文字列を HTML 文として解析し、 HTML 文から作成したノードを指定した位置に追加することができる。element.insertAdjacentHTML(position, text)
第一引数にしていできる値
'beforebegin'
element の前に追加
'afterbegin'
element の子要素の先頭に追加
'beforeend'
element の子要素の最後に追加
'afterend'
element の後に追加
実例
<body>
<ul id="list">
<li id="test1">要素1</li>
<li id="test2">要素2</li>
</ul>
<button onClick="getElement();">ノードを追加</button>
<script>
function getElement() {
let element = document.getElementById("list");
element.insertAdjacentHTML("beforeend", "<li>追加されました!</li>");
}
</script>
</body>
現在処理を実行しているscript要素を取得(currentScript)
<script>
要素を取得する <body>
<script>
let element = document.currentScript;
console.log(element);
</script>
</body>
<script>
let element = document.currentScript;
console.log(element);
</script>
<body>
<script>
let element = document.currentScript;
element.insertAdjacentHTML("beforebegin", "<p>取得したScriptタグに要素を追加</p>");
</script>
</body>
ドキュメントにHTMLを表す文字列を書き込む(write)
document.write(string)
<body>
<script>
document.write("<p>書き込みました</p>");
</script>
</body>
書き込みました
と表示される。
【初学者用まとめ(基礎編)】まず覚えるべきJavaScriptのコードの書き方(自習記録)
- はじめに
- コンソールに表示する(console.log())
- コメントアウト
- 変数
- 変数と定数の違い
- テンプレートリテラル
- データの入力を受け取る
- そもそもconsoleとは?
- 条件分岐
- 配列
- JavaScriptでは文字列も連続のデータ
- 変数に値を加える際に省略して記述する方法(代入演算子)
- インクリメント演算子
- デクリメント演算子
- 繰り返し
- 配列を使って繰り返しする
- 関数
- オブジェクトを使って複数のデータを扱う
- Webページに組み込む
- 例外処理
- 正規表現
- グローバル関数
- DOM操作
- イベント処理
- 日付・時刻・曜日を扱う方法【Dateオブジェクト】
- MDN web docs
はじめに
前提
まず前提として、この記事を書いているのは未経験からプログラミングに挑戦中の者になります。学習はフィヨルドブートキャンプに参加しながら取り組んでいて、現役のエンジニアの方からレビューをもらいながら進めています。
現在の学習状況は以下を随時更新しているので興味ある方はご覧ください。
筆者は執筆時点で、Ruby(Ruby on Rails)のプラクティスが終わったあとにJavaScriptに取り組んでいるので、完全に0からではありません。JavaScriptを多言語から学ぶにあたって、まず覚えるべき要点をまとめました。
詳しい解説ではなく要点をまとめた学習記録・ノートになりますのでご注意ください。
コンソールに表示する(console.log())
- 括弧 () 内に入力された文字をコンソールに出力
- シングルクォーテーション( ' )かダブルクォーテーション( " )で囲む
- 文の最後はセミコロン(;)
- 演算子も使える(クォーテーションがつくと文字列になるので注意)
// 8となる console.log(5 + 3); // 12となる console.log(20 - 8); // 文字列とみなされ、そのまま表示される console.log("4 + 5"); // 36 console.log(8 * 4); // 6 console.log(24 / 4); // 「A」と「B」を連結してコンソールに出力 console.log("A" + "B"); // 文字列の「20」と「15」を連結してコンソールに出力 console.log("20" + "15"); // 1となり、このあたりはRubyと同じ console.log(7 % 2);
コメントアウト
1行のコメントアウト
- 文頭に「//」を書くと、その行はコメントとみなされる
const price = prompt("価格を入力せよ"); // 以降はコメントとして使える
複数行のコメント
/*
から*/
の間は改行があってもコメントアウトになる
/* aaa bbb ccc */
変数
変数は数値や文字列などの値を変数(名前が書かれた箱のようなイメージ)に入れて保持できる。
何度も使ったりする場合はその都度コードを書いていたら冗長になってしまうため、変数に格納して使いまわす。
- 書き方
let 変数名 = 変数に入れる値
※letだけでなくconstやvarもある。再代入できるかどうかなどの細かい違いがあるので「変数と定数の違い」の表を見てください
- JavaScriptでは2単語以上ではキャメルケース
⚪︎
createName
※スネークケースは×create_name
// 変数nameを定義し、「けだま」を代入 let name = "けだま"; // 変数nameの値を出力してください console.log(name); //=> けだま
- 上から順に実行される。変数は上書きできる
let name = "しらたま"; console.log(name); // 変数nameの値を更新 name = "きなこ"; // 変数nameの値を出力すると「きなこ」になる console.log(name);
- 変数自体を更新する
let number = 7; // 変数numberの値に3を加えると10になる number += 3 ; // 変数numberの値を2で割ると5が出力される number /= 2;
変数と定数の違い
上記のようにletを使うと値を再代入できてしまうがconst
であれば以下のようにスコープが狭い。
- 定数を使うメリット 定数のメリットは、「後から値を更新できない」ところにある。また予期せぬ更新を防ぐ。
基本的にはconstを使う!!
// 定数languageを定義 const language = "フランス語"; // 定数languageの値を出力 console.log(language); // => フランス語 console.log(language + "を話せます"); // => フランス語を話せます
テンプレートリテラル
- 文字列の中で
${定数または変数}
とすることで、文字列の中に定数や変数を含めることができ - 文字列全体を
バッククォーテーション(
)`で囲む
const name = "きなこ"; const age = 4; // ぼくの名前はきなこです console.log(`ぼくの名前は${name}です`) ; // 今は4歳です console.log(`今は${age}歳です`);
データの入力を受け取る
prompt
メソッドはユーザーに対して入力を求め、入力された値を戻り値とするメソッド。受け取った値を変数に代入する。そしてコンソールに出力すると受け取れていることが確認できる。
変数 = input('メッセージ文字列')
const text = prompt("入力せよ"); console.log(text);
Chromeのデベロッパーツールで実行してみると「入力ダイアログボックス」が表示される。
promptメソッドの戻り値は数字であっても文字列として保持される
const price = prompt("価格を入力せよ"); // 100を入力 console.log(price + 10); // =>10010
- parseIntで整数化させると数値として扱われる
const price = prompt("価格を入力せよ"); // 100を入力 console.log(parseInt(price) + 10); //=> 110
そもそもconsoleとは?
console.log
の中でメソッドはlog
を指すconsole
はオブジェクトが入った変数オブジェクトというのはJavaScriptで操作できる「何か」を表すもの
- 他にもWebブラウザの表すWindowオブジェクトやWebページの内容を表すDocumentオブジェクトなどがある
- オブジェクトとは複数の「機能(メソッド)」と「変数(プロパティ)」の集合体だといえる
条件分岐
- 特定の条件を満たす場合のみ実行する
- 「true(真)」と「false(偽)」で判定する(合わせて真偽値または論理値)
- JavaScriptにはtrueかfalseを返す関数やメソッドや演算子があり、これを組み合わせて条件分岐を作っていく
(例)
isNaN('文字列')
:trueかfalseを返すメソッド
age < 30
::true かfalseを返す演算子
if(式や関数など)
:trueかfalseかで分岐する文
入力されたものが数値かどうかを確認する
isNaN
関数を浸かって実践。promptメソッドを浸かって入力されたものが数値になるかを判断してみます。
- isNaN関数書き方:
isNaN( 値 )
値は数値に変換は不可?- NaNは「Not a Number」の略
- 渡された値が数値に変換可能ならfalse
- 変換不可能ならtrueを返す
isNaN('1234') // => false isNaN('-10') // => false isNaN('3.14') // => false isNaN('555') // =>true 全角はNG isNaN('abc123') // => true
if文で数値でない場合に処理を実行する
- 書き方
if (式や関数など) { 実行する処理 }
(式や関数など)
がtrueの時にブロック内の処理を実行する
const text = prompt( "入力せよ" ); if ( isNaN( text ) ) { console.log( "数字ではありません" ); } // => 数字ではありません
数値のときに計算する場合
isNaN
メソッドのまま逆の判定をしたい時は ! isNaN
とすることでtrueをfalse逆にすることが出きる。Chromeのデベロッパーツールで100を入力してみると計算できていることがわかる。
const text = prompt( "入力せよ" ); if ( ! isNaN( text ) ) { console.log( parseInt( text ) + 80 ); } // => 180
条件に合致しないときの処理も記述する
const text = prompt( "入力せよ" ); if ( ! isNaN( text ) ) { console.log( parseInt( text ) + 80 ); } else { console.log( "数字ではない" ); } // => 数字ではない ※文字列を入力
比較演算子を使ってif文をつくる
JavaScriptの比較演算子は以下の一覧のようになっている。
比較演算子 | 説明 |
---|---|
== | 等しいかどうかを比較(型の変換あり) |
!= | 等しくないかどうかを比較(型の変換あり) |
=== | 厳密な等価性を比較(型の変換なし) |
!== | 厳密な不等価性を比較(型の変換なし) |
< | より小さいかどうかを比較 |
> | より大きいかどうかを比較 |
<= | 以下かどうかを比較 |
>= | 以上かどうかを比較 |
Rubyの比較演算子と比較して特に違いを感じたのは「==」「===」と「!=」「!==」の違いです。
123 == '123' // => true 型変換されるので等しいという判定になる 123 === '123' // => false 型変換されないので等しくないとなる
これは「!==」も同様です。「==」「!=」はこのルーズさがあるので問題を引き起こす可能性があり「!==」「===」が推奨と学習テキストには記述がありました。
この比較演算子とif文を使って条件分岐を作ってみまます。
const text = prompt("年齢は?"); const age = parseInt(text); if (age >= 20) { console.log("成人"); } // => 成人
3つ以上の条件分岐
これはRubyとほぼ同じ。しかしRubyだとelsif
だがJavaScriptではelse if
(半角スペースあり)となるあとはif
else if
else
それぞれで{}
を用意する必要がある。
if (式や関数) { // 実行する処理 } else if (式や関数) { // 実行する処理 } else (式や関数) { // 実行する処理 }
上から実行され該当するしたところで処理は終了。
使用例
const text = prompt("年齢を入力してください"); const age = parseInt(text); if (age < 20) { console.log("未成年"); } else if (age < 65) { console.log("成人"); } else { console.log("高齢者"); }
条件分岐の中に条件分岐を書く
const text = prompt("年齢を入力してください"); if (!isNaN(text)) { const age = parseInt(text); if (age < 20) { console.log("未成年です"); } else { console.log("成人です"); } }
switch文
switch 文は対象となる値がいずれかの値と一致するかどうかを調べて処理を分けることができる。
function evaluateNumber(number) { switch (true) { case typeof number !== 'number': console.log('無効な数値です'); break; case number > 0: console.log('入力した数値は正の数です'); break; case number < 0: console.log('入力した数値は負の数です'); break; case number === 0: console.log('入力した数値はゼロです'); break; default: console.log('該当する条件がありません'); } } // 異なる数値を試す evaluateNumber(10); // '入力した数値は正の数です' evaluateNumber(-5); // '入力した数値は負の数です' evaluateNumber(0); // '入力した数値はゼロです' evaluateNumber('abc'); // '無効な数値です' evaluateNumber(true); // '該当する条件がありません'
Switchのあとの()内に記述した式を評価して、その値がcaseの後に書かれたいずれかの値と一致するかどうか調べて一致した処理を行う。
break
を記述すると一致した場合処理を終える。break
がないと一致した式以降の処理も全て実行されてしまうため注意。
また、いずれの値とも一致しなかった場合は default 句の後に記述されている処理を実行する。 default 句は不要であれば省略も可能。
なお、条件の比較は== 演算子
ではなく === 演算子
を使って比較されることに留意する。
使いどころとしては「if 文を使って数多くの値と === 演算子を使って一致するものがないかどうか調べたい場合」にはswitch 文を使うとすっきりする。
論理演算子
論理演算子 | ||
---|---|---|
論理積 | && | かつ(左右の値がtureのときだけtureを返す) |
論理和 | || | または(左右一歩位がtrueのときにtrueを返す) |
論理否定 | ! | trueとfalseを逆転する |
$$
const text = prompt("年齢を入力してください"); const age = parseInt(text); if (age >= 6 && age <= 15) { console.log("義務教育期間中"); }
||
const text = prompt("年齢を入力してください"); const age = parseInt(text); if (age < 20 || age >= 65) { console.log("未成年または高齢者"); }
if文には真偽値を返す式や関数以外を渡すこともできる
falseとみなされる値
false 0 -0 0n "" (空文字) null undefined NaN
数値の 0 ・文字列の空文字、・null ・undefined などは falseとなり、それ以外がtrueとなる!
たとえばこんなif分はtrueとなりブロックの中のコードが実行される。
if (0) { // 実行したい処理 }
if文を1行で書く
以下のような記述を覚えておくとコードがスッキリする
処理が1行しかない場合の省略形
if (num > 80) console.log("numは80より大きいです。");
このように記述が可能
三項演算子で記述
条件式 ? trueの処理 : falseの処理
配列
配列は中に複数の値を入れられる「型」。要素の値は文字列、数値なんでもOK!
配列の書き方
変数 = [値0, 値1, 値2, …]
配列の要素の取り出し方
配列内の要素を利用する時は、変数名のあとに各カッコで囲んで数値を記述する。この数値を「インデックス(添え字)」という。
なお、このインデックスは最初の値は「0」から始まるので注意(1ではない)。
先頭を取得
変数[0]
const member = ["a", "b", "c", "d", "e"]; console.log(member[0]); //=> "a"
末尾を取得
Rubyのように変数[-1]
では取得できない
const member = ["a", "b", "c", "d", "e"]; // 方法1: 配列のlengthメソッドを使用 const lastElement = member[member.length - 1]; console.log(lastElement); // "e" // 方法2: pop() メソッドを使用して最後の要素を取り出す const lastElement2 = member.pop(); console.log(lastElement2); // "e"
配列の要素を書き換える
以下のようにすると配列の要素を書き換えることができる。
const member = ["a", "b", "c", "d", "e"]; member[0] = "x"; console.log(member[0]); // "x"
配列の要素が空のパターン
以下のように要素が空の場合、その要素を取り出すとundefined
になる。
let arr = [1, , 3]; console.log(arr); >> [1, empty, 3] console.log(arr[1]); >> undefined
要素の値として式を指定
配列を作成する時に要素の値として数値や文字列などのリテラルだけではなく式を指定することができる。
let tax = 1.1; let price = [100 * tax, 200 * tax]; console.log(price); >> [110, 220]
多次元配列
配列の中の要素に配列が入っている場合、多次元配列と呼ぶ
let familyData = [ ["Shiratama", 5], ["Kinako", 4], ];
多次元配列の値を取り出し方
以下のように「配列の要素⚪︎個の配列の⚪︎個目の要素」として取得する(配列[0][0]
など)
let familyData = [ ["Shiratama", 5], ["Kinako", 4], ]; console.log(familyData[0]); // ['Shiratama', 5] console.log(familyData[0][0]); // Shiratama
配列を操作する際に使える便利なメソッド
配列はArray型のオブジェクト。
コード | 説明 |
---|---|
array.length | 配列の要素数を返す。 |
array.push(element) | 配列の末尾に要素を追加する。 |
array.pop() | 配列の最後の要素を削除して返す。 |
array.remove(value) | 配列から値と一致する要素を取り除く。 |
array.sort() | 配列の要素を並べ替える。 |
array.shift() | 配列の先頭要素を削除して返す。 |
様々なオブジェクトとそのメソッドがあるためリファレンスを確認する!!
ブラウザのコンソールで配列の中身を表示する
以下を実行して、コンソールを確認。「▶️」を押すと、中身が確認できる。
JavaScriptでは文字列も連続のデータ
文字列ABCDE"
であってもfor文で配列のように文字列[要素数]
で値を取得できる。
const teams = "ABCDE"; console.log(teams[0]); // A for (let team of teams) { console.log(team); } // A // B // C // D // E
変数に値を加える際に省略して記述する方法(代入演算子)
代入演算子 | 例 | 説明 |
---|---|---|
+= | a += 10 | 加算代入 (a = a + 10) |
-= | a -= 5 | 減算代入 (a = a - 5) |
*= | a *= 2 | 乗算代入 (a = a * 2) |
/= | a /= 3 | 除算代入 (a = a / 3) |
%= | a %= 4 | 剰余代入 (a = a % 4) |
**= | a **= 2 | 指数代入 (a = a ** 2) |
インクリメント演算子
JavaScriptには、インクリメント演算子として ++ が用意されていて、変数の値を1つ増加させるための演算子。 インクリメント演算子は、ループ処理やカウンターの更新など、変数の値を増加させる際によく使われる。
- 前置インクリメント (++a): 値を増加させた後の値を返す
let a = 5; let b = ++a; console.log(a); // 結果は 6 console.log(b); // 結果は 6
++a が前置インクリメントで、a の値が増加した後に b に代入されている。そのため、a と b はともに 6 の値を持っていいる。
- 後置インクリメント (a++): 値を返した後に値を増加させる
let x = 5; let y = x++; console.log(x); // 結果は 6 console.log(y); // 結果は 5
後置インクリメントの場合、まず x の値が y に代入され、その後に x の値が増加する。そのため、x は 6 に増加し、y は元の値の 5 を持っている。
デクリメント演算子
インクリメント演算子の逆。インクリメントが変数の値を1つ増加させる演算子であるのに対し、デクリメントは変数の値を1つ減少させる演算子。主に数値の変数やカウンターの値を減らす際に使用する。
- 前置デクリメント (--a): 値を減少させた後の値を返す
let x = 10; let y = --x; console.log(x); // 結果は 9 console.log(y); // 結果は 9
--x が前置デクリメントで、x の値が減少した後に y に代入されている。そのため、x と y はともに 9 の値を持っている。
- 後置デクリメント (a--): 値を返した後に値を減少させる 後置デクリメントを使った場合、値を返す順序が異なる
let p = 10; let q = p--; console.log(p); // 結果は 9 console.log(q); // 結果は 10
後置デクリメントの場合、まず p の値が q に代入され、その後に p の値が減少する。そのため、p は 9 に減少し、q は元の値の 10 を持っている。
繰り返し
名の通り同じ処理を繰り返す文。指定した条件がtrueの間処理が繰り返され、指定した条件から外れると処理を終了させる。ループとも呼ばれる。
while文
while文は、条件を満たす間繰り返しをする書き方になります。(while 〜する限り)
while (継続条件) { 繰り返したい処理; }
let count = 0; while (count < 10) { console.log(`${count + 1}回目の表示です`); // 0から始まるの表示が1から始まるよう+1している count++; // 変数countに1を毎回足す } // 1回目の表示です // 2回目の表示です // 3回目の表示です // 4回目の表示です // 5回目の表示です // 6回目の表示です // 7回目の表示です // 8回目の表示です // 9回目の表示です // 10回目の表示です
毎回変数countに数字が1ずつ追加されていってcount < 10が満たす間は繰り返される。
for文
for文は回数が決まった繰り返しに向いている。
- 書き方
for (初期化; 継続条件; 最終式) { 繰り返しする処理; }
動き方
- 処理を始める前に初期化が実行される
- 継続条件がtrue(真)の間、繰り返される
- 最終式はブロック処理が終わるごとに実行される
使用例
for (let count = 0; count < 10; count++) { console.log(`${count + 1}回目の表示です`); } // 1回目の表示です // 2回目の表示です // 3回目の表示です // 4回目の表示です // 5回目の表示です // 6回目の表示です // 7回目の表示です // 8回目の表示です // 9回目の表示です // 10回目の表示です
逆順で繰り返す
1ずつ増えていくではなく、1ずつ減らしていくパターンで繰り返したいときにはデクリメント演算子を使用する。
for (let count = 10; count >= 0; count--) { console.log(`残り${count}`); } 残り10 // 残り9 // 残り8 // 残り7 // 残り6 // 残り5 // 残り4 // 残り3 // 残り2 // 残り1 // 残り0
繰り返しからの脱出(break)&スキップ(continue)
- break
中にif文を使って条件を指定して、その条件を満たす場合に繰り返しを中断する
for (let count = 0; count < 10; count++) { if (count == 5) { break; } console.log(`${count + 1}回目の表示です`); } // 1回目の表示です // 2回目の表示です // 3回目の表示です // 4回目の表示です // 5回目の表示です
- continueを使ってスキップする
continueの場合は条件に合致したときはスキップし、繰り返しは継続する。
for (let count = 0; count < 10; count++) { if (count == 5) { continue; } console.log(`${count + 1}回目の表示です`); } // 1回目の表示です // 2回目の表示です // 3回目の表示です // 4回目の表示です // 5回目の表示です 6回目の表示がなくなっている! // 7回目の表示です // 8回目の表示です // 9回目の表示です // 10回目の表示です
繰り返しの中で繰り返しを使う
繰り返し分の中で、違う繰り返し文を使うことももちろん可能。
- 掛け算1〜9の位を計算
for (let a = 1; a < 10; a++) { for (let b = 1; b < 10; b++) { console.log(`${a} × ${b} = ${a * b}`); } }
繰り返しの中の繰り返しなので変数aもbも使える。
配列を使って繰り返しする
for~of文を使う(ES2015から利用可)
for~of文は配列から要素を1つずつ順番に取り出して繰り返しできる(ES2015から利用可)。
const members = ["a", "b", "c", "d", "e"]; for (let member of members) { console.log(`${member}さん`); } // aさん // bさん // cさん // dさん // eさん
for文を使って配列のインデックスを指定する
for~of文が使えない場合での以下のように配列のインデックスを指定できる。for文で定義した変数を活用する。
const members = ["a", "b", "c", "d", "e"]; for (let count = 0; count < 5; count++) { console.log(`${members[count]}さん`); } // aさん // bさん // cさん // dさん // eさん
(練習)5チームのリーグ戦の対戦表をつくる
- A~Eまでの5チーム
- A 対 Aなどのあり得ないパターンを排除したい
チーム名を配列にセットし、for〜of文をネストさせ、if文で同じ値だった場合はスキップさせる。しかしこれだとA 対 Aと同チームは排除できるが、A 対 BとB 対 Aのように同じ組み合わせは残ってしまう。
const teams = ["A", "B", "C", "D", "E"]; for (let team1 of teams) { for (let team2 of teams) { if (team1 == team2) { continue; } else { console.log(team1 + " 対 " + team2); } } } // A 対 B // A 対 C // A 対 D // A 対 E // B 対 A // B 対 C // B 対 D // B 対 E // C 対 A // C 対 B // C 対 D // C 対 E // D 対 A // D 対 B // D 対 C // D 対 E // E 対 A // E 対 B // E 対 C // E 対 D
以下のでも同様
const teams = ["A", "B", "C", "D", "E"]; for (let team1 of teams) { for (let team2 of teams) { if (team1 != team2) { console.log(team1 + " 対 " + team2); } } }
for文を使って以下のようにやれば実現できそう
const teams = ["A", "B", "C", "D", "E"]; for (let i = 0; i < teams.length; i++) { for (let j = i + 1; j < teams.length; j++) { console.log(`${teams[i]} 対 ${teams[j]}`); } } // A 対 B // A 対 C // A 対 D // A 対 E // B 対 C // B 対 D // B 対 E // C 対 D // C 対 E // D 対 E
関数
- 関数を作ることを「関数を定義する」という
- プログラムの一部を繰り出して名前をつけるイメージ
function 関数名(引数1, 引数2, ...){ 実行される処理1; 実行される処理2; ... return 戻り値; } 関数名(引数1, 引数2, ...); // 呼び出し方
関数を自分で作るメリット
プログラムの構造がわかりやすくなる プログラムを一括りにして名前をつけることでもあるため、そのプログラムが何をしているのかを理解しやすくなる。故に、命名が重要になる!
関数は何度も呼び出せる 関数は使い回すことができる。そのため重複したコードを書く必要がなくなり管理もしやすく、コードの記述量も減る。
関数の書き方
- アロー関数式(ES2015からできるようになった記述)
=>
を「アロー」と呼び()=>{}
と組み合わせて関数を作る
const 変数名 = () => { 関数内で実行する文 }
const test = () => { console.log("テスト"); };
アロー関数式の省略形
関数ブロック内で実行する処理が return 文だけだった場合、ブロックをあらわす { と } 、そして return も省略して次のように記述することができる
let 変数名 = (引数1, 引数2, ...) => 戻り値;
引数が一つで関数ブロック内で実行する処理が return 文だけだった場合
let 変数名 = 引数 => 戻り値;
使用例
let calcScore = (x, y) => x + y; let result = calcScore(10, 20); console.log(result); // 30
関数の呼び出し
関数名();
で呼び出す。引数を受ける場合は()内に値を入力する。
const test = () => { console.log("テスト"); }; test(); // テスト
引数を使う関数
()内に引数名を設定して引数を渡して関数を呼び出す。
const createLetter = (name) => { console.log(`${name}さん、こんにちは`); }; createLetter("しらたま"); createLetter("きなこ"); // しらたまさん、こんにちは // きなこさん、こんにちは
何度も同じ処理をする場合、引数を設定することで名前が変わっても同様の処理を行うことができる。
複数の引数を渡す
const createLetter = (name, age) => { console.log(`${name}さん、こんにちは`); console.log(`${age}歳の誕生日おめでとう`); }; createLetter("しらたま", 6); createLetter("きなこ", 4); // しらたまさん、こんにちは // 6歳の誕生日おめでとう // きなこさん、こんにちは // 4歳の誕生日おめでとう
functionで関数を作る(ES2015以前の場合)
function createLetter(name) { console.log(`${name}さん、こんにちは`); } createLetter("しらたま"); createLetter("きなこ"); // しらたまさん、こんにちは // きなこさん、こんにちは
関数の中で関数を使う(テンプレート文字列 ※ES2015〜)
「テンプレート文字列」を使うと何度もconsole.log()
を書く必要がなくなる
- テンプレート文字列は``(バッククォーテーション)で囲んだ範囲に書いた文字列
- この範囲での改行やスペースはプログラムの結果に反映される
- 長い文章を書く場合に有効な記述
`文章1 文章2`
- 使用例
const createInvoice = (name, price) => { const msg = `${name}様 お買い上げありがとうございます。 ${price}円をお支払いください。`; console.log(msg); }; createInvoice("しらたま", 1000); createInvoice("きなこ", 800); // しらたま様 // お買い上げありがとうございます。 // 1000円をお支払いください。 // きなこ様 // お買い上げありがとうございます。 // 800円をお支払いください。
関数リテラル
関数リテラルという方法で関数を定義することもできる。 以下のように書く。
let 変数名 = function(引数1, 引数2, ...){ 実行される処理1; 実行される処理2; ... return 戻り値; }; 変数名(引数1, 引数2, ...); // 呼び出しは同じ
このように変数の中にfunctionを定義する。変数名は変数名となる。引数がない場合でもfunction
の後ろには必ず()
の記述が必要な点に注意。
関数リテラルの使い所 関数リテラルでは関数に名前をつける必要がない。そのため1度しか使われない関数の場合、各々を1つずつ関数宣言として定義していく場合、関数名が他の関数や変数名と被ってしないかを気にしないといけなくなる。
しかし、関数リテラルを使えば関数名を指定せずに関数を定義できるためそのような心配がいらなくなる
一度しか利用されない関数は、例としてイベント処理やコールバック関数などで使用される。
ローカル変数
先ほど使った関数内のmsg変数はこのメソッド内でのみ呼び出せる。これをローカル変数という。
const createInvoice = (name, price) => { const msg = `${name}様 お買い上げありがとうございます。 ${price}円をお支払いください。`; console.log(msg); }; console.log(msg); // 呼び出そうとしてもReferenceError: msg is not defined(定義されていないため参照できない)となる
戻り値を返す関数
自作の関数で戻り値を返したい場合はreturn
文を書く
- 書き方
const 変数 = () => { 関数で実行する処理; return; };
- 例
function add(a, b) { return a + b; } console.log(add(1, 4)); // 5
return
がなくなると期待した値が出力されずundefined
となる
function add(a, b) { a + b; } console.log(add(1, 4)); undefined
関数から複数の値を返す
return
を使うと1つの値しか戻せない。2つ以上の値を戻り値したい場合は、配列などで返すことができる。
function test(num1, num2, num3){ const maxNum = Math.max(num1, num2, num3); const minNum = Math.min(num1, num2, num3); return [maxNum, minNum]; } let numArray = test(80, 60, 70); console.log("Max = " + numArray[0]); // Max = 80 console.log("Min = " + numArray[1]); // Min = 60
可変長引数で都度変わる引数の数に対応する
JavaScript では関数で引数を記述するときに、引数の前に '...' を付けると、呼び出し側から渡されてきたすべての値を要素とした配列が要素に格納される('...'を付けた引数を Rest Parameter と呼ぶ)。
呼び出し側で2つ引数がわたされても、それ以外の数が渡されても受け取って取り扱うことができる。
function 関数名(...引数){ ... }
使用例
function calcSum(...num) { let sum = 0; for (let i = 0 ; i < num.length ; i++){ sum += num[i]; } return sum; } calcSum(1, 2, 3); >> 6 calcSum(1, 2, 3, 4, 5); >> 15
通常の引数と組み合わせて使用することできる
function 関数名(引数1, 引数2, ...引数3){ ... }
JavaScriptでは設置した引数に過不足があってもエラーにならない
- JavaScriptでは設置した引数に過不足があってもエラーにならない
- 渡されてきた値が多かった場合は単に使われず、少なかった場合に値が格納されなかった引数は
undefined
となる - ない値に対して参照が行われるとエラーとなる
再帰関数
- 再帰関数とは関数内で自分自身の関数を呼びだすこと
function test(){ test(); } test();
- 無限ループにならないようにこの条件になったら自分自身の呼び出しを終了するような記述が必要
function test(){ if (条件式) { test(); } return; } test();
コールバック関数
コールバック関数は、他の関数内で使用される関数。コールバック関数は、ある特定のイベントが発生したときに呼び出されたり、他の関数の一部として実行されたりする。
const numbers = [1, 2, 3, 4, 5]; function processNumber(number) { console.log(number * 2); } numbers.forEach(processNumber);
上記のforEach メソッドは、コールバック関数を受け取り、それぞれの要素に対してその関数を実行する使用例。このように、JavaScript では別途定義した関数を別の関数を呼びだす時に引数に指定して渡すことができる。
コールバック関数が主に使われるケース コールバック関数は主に非同期処理を行う時に使用される。呼び出した関数の処理が終わったあとなどにコールバック関数として指定した関数を実行させたい場合に使われる。
例:「一定時間経過したあとに特定の処理を実行して欲しい場合」「ボタンがクリックされたときに実行して欲しい処理を指定する」
高階関数
高階関数は、他の関数を引数として受け取るか、関数を返す関数。これにより、より柔軟なコードを書くことができる。 例えば、以下の高階関数 multiplyBy は、数値を受け取り、それを掛け算する新しい関数を返している。
function multiplyBy(factor) { return function (number) { return number * factor; }; } const double = multiplyBy(2); // double は掛け算2を行う関数 const triple = multiplyBy(3); // triple は掛け算3を行う関数 console.log(double(5)); // 10 console.log(triple(5)); // 15
オブジェクトを使って複数のデータを扱う
書き方
変数 = { data1 : 値a, data2 : 値b }
- オブジェクトではデータをプロパティ名(キー)と値のペアで管理する
- 一組のプロパティ名と値で表されるデータのことをプロパティと呼ぶ
- ブジェクトでは複数のプロパティをまとめて管理することができる
- プロパティの値には数値や文字列などのプリミティブ型の値のほかに、配列や関数といったオブジェクト型の値を指定することができる
- 値として関数を記述したプロパティのことをメソッドとも呼ぶ
値の取り出し方
「変数名.プロパティ」
const person = { firstName: "A", lastName: "B", age: 20, }; console.log(person.firstName); // A
「変数名['プロパティ']」
const person = { firstName: "A", lastName: "B", age: 20, }; console.log(person["firstName"]); // A
プロパティの追加
let mySize = { width: 400, height: 300, }; console.log(mySize); myBox.color = 'Red'; // 新しいプロパティを追加 console.log(mySize); >> {width: 400, height: 300, color: 'Red'}
存在しないプロパティ名を指定して値を代入することができる。
プロパティを削除
オブジェクトからプロパティを削除したい場合には delete 演算子を使う。
delete オブジェクト.プロパティ名 delete オブジェクト['プロパティ名']
以下のように削除されていることがわかる
let mySize = { width: 400, height: 300, }; console.log(mySize); myBox.color = 'Red'; // 新しいプロパティを追加 console.log(mySize); >> {width: 400, height: 300, color: 'Red'} delete mySize.color; // プロパティを削除 console.log(mySize); >> {width: 400, height: 300}
オブジェクトのプロパティを順番に取得する
for...in 文でオブジェクトのプロパティ名を順に取り出しながら繰り返し処理を行うことができる。
let cat = { name:'しらたま', age:5, hobby:'寝る' }; for (key in cat){ console.log('cat.'+ key + '=' + cat[key]); } // cat.name=しらたま // cat.age=5 // cat.hobby=寝る
プロパティの一覧を取得する(keys)
オブジェクトに含まれるプロパティ名の一覧を取得するには Object オブジェクトの keys メソッドを使う。引数に指定したオブジェクトのキーを配列で返してくれるメソッド。
Object.keys(オブジェクト)
let cat = { name: "しらたま", age: 5, hobby: "寝る", }; let keyArr = Object.keys(cat); console.log(keyArr); // ["name", "age", "hobby"]
forEach メソッドをあわせて使って値を順に取得する
配列の要素を順に取得する Array オブジェクトの forEach メソッドをあわせて使うことでプロパティの値を順に取得することもできる。
let cat = { name: "しらたま", age: 5, hobby: "寝る", }; let keyArr = Object.keys(cat); keyArr.forEach(function (element) { console.log(cat[element]); }); // しらたま // 5 // 寝る
複数のオブジェクトを配列にまとめる
配列の要素にオブジェクト(ハッシュ)を入れた場合の値の取り出し方。 配列の要素数を指定した後に上記の取り出し方をすればOK!
const persons = [ { firstName: "A", lastName: "B", age: 20 }, { firstName: "C", lastName: "D", age: 50 }, ]; console.log(persons[0]["firstName"]); console.log(persons[1]["firstName"]); // A // C
プロパティの値の一覧を取得する(values)
オブジェクトに含まれるプロパティの値の一覧を取得するには Object オブジェクトの values メソッドを使う 。引数で渡したオブジェクトの値を配列で返してくれる。
Object.values(オブジェクト)
let cat = { name: "しらたま", age: 5, hobby: "寝る", }; let keyArr = Object.values(cat); console.log(keyArr); // ['しらたま', 5, '寝る']
プロパティの名前と値の一覧を取得する(entries)
オブジェクトに含まれるプロパティの名前と値のペアの一覧を取得するには Object オブジェクトの entries メソッドを使う。プロパティごとに配列で返してくれる。
Object.entries(オブジェクト)
let cat = { name: "しらたま", age: 5, hobby: "寝る", }; let keyArr = Object.entries(cat); console.log(keyArr);
プロパティ名と値のデータからオブジェクトを作成する(fromEntries)
※ fromEntries メソッドは ECMAScript 2019 (ES10) から導入
プロパティと値のデータからオブジェクトを作成するには Object オブジェクトの fromEntries メソッドを使う。
Object.fromEntries(オブジェクト)
反復処理可能なオブジェクトを引数に指定する(Array や Map など)。このメソッドを実行すると、指定されたオブジェクトをもとに新しい Object オブジェクトが生成される。
指定するオブジェクトは、データのペアが2つあるものでなければならない。1つ目の値はプロパティ名として使用され、2つ目の値はそれに対応するプロパティの値として格納される
let catArray = [ ["name", "しらたま"], ["age", 5], ["hobby", "寝る"], ]; let catObj = Object.fromEntries(catArray); console.log(catObj); // {name: 'しらたま', age: 5, hobby: '寝る'}
ひとつしか値がない要素がある場合はundefined
になる
let catArray = [ ["name", "しらたま"], ["age"], ["hobby", "寝る"], ]; let catObj = Object.fromEntries(catArray); console.log(catObj); // 'しらたま', age: undefined, hobby: '寝る'}
複数のオブジェクトを一つにまとめる(assign)
複数のオブジェクトを一つにまとめたるには Object オブジェクトの assign メソッドを使う。
Object.assign(コピー先オブジェクト, コピー元オブジェクト1, コピー元オブジェクト2, ...)
let objA = { a: 800 }; let objB = { b: 350 }; let objC = { c: 600 }; let allObj = Object.assign(objA, objB, objC); console.log(allObj); // {a: 800, b: 350, c: 600} console.log(objA); // {a: 800, b: 350, c: 600}
上記のように3つのオブジェクトを1つにまとめることができる。注意すべきなのは元のオブジェクトも変更が加えられるという点です。
もし、変更を加えたくない場合は以下のようにする。
let objA = { a: 800 }; let objB = { b: 350 }; let objC = { c: 600 }; let allObj = Object.assign({}, objA, objB, objC); console.log(allObj); // {a: 800, b: 350, c: 600} console.log(objA); // {a: 8 tenn00}
- 同じプロパティ名があった場合は後のキーと値で上書きされる
配列なので繰り返し文で取得することもできる
const persons = [ { firstName: "A", lastName: "B", age: 20 }, { firstName: "C", lastName: "D", age: 50 }, ]; for (let person of persons) { console.log(person["firstName"]); }
Webページに組み込む
HTMLでJSファイルを読み込む
HTMLの中で<script src="ファイル名.js"></script>
タグを使ってファイルを読み込む。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>サンプル</title> </head> <body> <h1>サンプル</h1> <input type="test" /> <button>実行</button> <p>結果表示</p> <script src="test.js"></script> // HTMLの中にscriptタグをつけてjsファイルを読み込む </body> </html>
ブラウザにhtmlタグをドラッグ&ドロップすると表示が確認できる。もちろんまだjsファイルには何も書いていないので見たの表示はHTMLの情報のみが記述されている。
HTMLの要素を選択する
- JavaScriptでHTML操作をするには、まず対象となる要素を選択する
- そのために使うのが
querySelector(クエリセレクター)
メソッド- セレクタとは要素を選び出す時の指示になる文字列のこと
- p要素であれば「'p'」、h1要素を選びたければ「'h1'」と指定する
querySelector(クエリセレクター)
メソッド
書き方
let 変数 = document.querySelector('文字列')
- 正式にはwindow.document.querySelector()
だがwindowは省略できる
- querySelector(クエリセレクター)
メソッドは見つけた要素のElementオブジェクトを返すのでその後に使うために変数にいれておく
innerTextで要素の中のテキストにアクセスする
- 目的の要素を表すElementオブジェクトが取得できれば、あとはそれが持つメソッドやプロパティを使って操作できる
- 要素の中のテキストは
innerText
プロパティから利用できる 書き方
変数.innerText
変数にはElementオブジェクトを入れた変数。innerText
は要素内部のテキストを表す使い方
test.js
const element = document.querySelector("p"); element.innerText = "文字を変更しました。";
p要素を取得してテキストが書き換わったことがわかる。
うまくいかない場合はブラウザのデベロッパーツールのコンソールに表示されるので、そこを確認。
入力ボックスからデータを受け取る
先ほど入力していたHTMLのinput要素に入力された値を受け取って表示する。
- input要素
<input type="text">
これです
その他のinput要素抜粋
type="radio"
type="password"
type="checkbox"
入力ボックスに入力した値をJavaScriptで利用するには
value
プロパティを利用する<input type="text">
のtextボックスに入力された値は変数.value
で取得する
const input = document.querySelector("input"); const element = document.querySelector("p"); element.innerText = input.value;
- inputをセレクタとしてドキュメントに問い合わせて、見つけた要素を新規作成した変数に入れる
- pをセレクタとしてドキュメントに問い合わせて、見つけた要素を新規作成した変数に入れる
- 変数inputの値を変数elementのinnerTextプロパティに入れる
しかし、まだこの状態だと「実行」を押しても何もなく、元々あった文字列も消えている。
ユーザーのアクションに反応するイベント
- JavaScriptはWebブラウザが読み込まれた瞬間に実行される
- 実行した時点ではinput要素には何も入力されていない
- 入力されていないinput要素から値を取得して、p要素のテキストとして読み込まれたため実行結果が消えている状態
- これを解決するには「イベント」を使ってユーザーが何かをしたタイミングで実行させる必要がある
①ユーザーが実行ボタンをクリック(イベント)
② 連動してコードを実行させる
イベントと関数を結びつける
ElementオブジェクトのaddEventLisner
メソッドを使う
書き方
変数.addEventListener("イベントタイプ", 関数);
1つめの引数にイベントタイプ、2つめの引数に関数を指定する。関数を指定する第2引数の書き方は、アローとブロックを指定する。イベントタイプ 今回は
'click'
を指定する。
様々なイベントタイプがある
- 第2引数の関数の書き方
変数.addEventListener("イベントタイプ", () => { 実行したい処理; });
- 実演
const input = document.querySelector("input"); const button = document.querySelector("button"); const element = document.querySelector("p"); button.addEventListener("click", () => { element.innerText = input.value; });
例外処理
プログラムの中でエラーが発生した場合、例外に対する処理を設定していない場合にはプログラムがその時てで強制終了してしまう。そのために例外処理を使う。
try...catch文を使った例外処理
以下のように計算をする際に数値だけではなく長整数が混ざってしまった場合は以下のようにエラーになる。
function sum(num1, num2){ let sum = num1 + num2; return sum; } console.log('開始'); let result = sum(10, 8n); console.log(result); console.log('終了'); // 開始 // TypeError: Cannot mix BigInt and other types, use explicit conversions
このままだとプログラムが止まって終わってしまうので、適切に例外を捉えて処理を行う。
try...catch文の使い方
try{ 例外が発生する可能性がある文を記述 } catch(e) { 例外をキャッチしたときに実行される処理 }
catch
のあとの括弧の中に記述した変数 e (変数名は任意)には、例外がスローされたときに設定された値が格納される- 通常は Error オブジェクト(自分で例外をスローする場合には任意の値を指定できるので Error オブジェクトとは限らない)
function sum(a, b){ let sum; try{ sum = a + b; } catch(e) { console.error(e); return console.log('エラーになりました'); } return sum; } console.log('開始'); console.log(sum(10, 8)); console.log(sum(10, 8n)); console.log('終了'); // 開始 // 18 // TypeError: Cannot mix BigInt and other types, use explicit conversions // エラーになりました // 終了
このようにエラーとなりつつも終了
まで実行されていることがわかる。
try...catch...finally文を使った例外処理
try...catch...finally 文を使用すると、 try ブロックの中で例外がスローされたかどうかに関わらず、必ず最後に実行する処理を記述することができる。
try{ 例外が発生する可能性がある文を記述 } catch(e) { 例外をキャッチしたときに実行される処理 } finally { 最後に実行される処理 }
finally は、 try ブロックの処理が終わったあと、また例外がスローされた場合は catch ブロックの処理が終わったあとで必ず実行される。
function calc(a, b) { let sum; try { sum = a + b; } catch (e) { console.error(e); return console.log("エラーになりました〜"); } finally { console.log("わいは何がなんでも登場するんや"); } return sum; } console.log("開始"); console.log(calc(1, 9)); console.log(calc(1, 9n)); console.log("終了"); // 開始 // わいは何がなんでも登場するんや // 10 // TypeError: Cannot mix BigInt and other types, use explicit conversions // エラーになりました〜 // わいは何がなんでも登場するんや // 終了
catchの部分でreturn console.log("エラーになりました〜");
とreturnで返して終わってもわいは何がなんでも登場するんや
という文字が出現してきます。1回目の通常の処理にももちろん登場
try ブロックや catch ブロックの中で retnrn 文や break 文などが実行されて処理が try...catch...finally 文から別のところへ移動する場合であっても、移動が行われる前に実行
正規表現
※ここではあくまでJavaScriptの正規表現書き方を記述するので正規表現そのものはまとめていません
正規表現リテラルを使用
/パターン/ /パターン/フラグ
このようにスラッシュで囲って使う
RegExpコンストラクタを使用
RegExp オブジェクトのコンストラクタを使用して正規表現オブジェクトを作成する方法
new RegExp(パターン[, フラグ])
- 1 番目の引数には正規表現リテラルを指定orパターンを表す値を文字列として記述
- フラグを設定する場合は 2 番目の引数に一つまたは複数のフラグを文字列として記述
- 正規表現のパターンがあらかじめ決まっているのであれば基本的に正規表現リテラルを使って正規表現オブジェクトを作成するのがシンプル
JavaScriptで使えるフラグ
正規表現ではフラグ(またはオプション)を設定することで文字列がパターンにマッチする仕組みを変更することができる。
/パターン/フラグ
フラグ | フラグ名 | 説明 | ||
---|---|---|---|---|
g | Global | 通常、正規表現は最初に一致した文字列だけを探しますが、g フラグを設定するとすべての一致を探します。 | ||
i | IgnoreCase | パターンで大文字と小文字を区別しなくなります。例えば、/a/ の場合は /A/ にもマッチします。 | ||
m | Multiline | 複数行モード。メタ文字の ^ と $ が文字列の先頭と末尾だけでなく、各行の先頭と末尾にもマッチするようになります。 | ||
s | DotAll | メタ文字の . は通常、改行文字にはマッチしませんが、s フラグを設定すると改行文字にもマッチします。 | ||
u | Unicode | 通常、正規表現で \uhhhh は UTF-16 のコードユニットを表しますが、u フラグを設定すると \u{hhhh} や \u{hhhhh} を Unicode コードポイントとして使用できます。 | ||
y | Sticky | lastIndex プロパティが示す位置より後ではなく、lastIndex プロパティの位置からのみ一致するかどうかを判定します。 |
文字列が正規表現とマッチするかテスト
RegExp オブジェクトのインスタンスメソッドの test
は、対象の文字列が正規表現とマッチするかテストしtrue
または false
を返す。
正規表現オブジェクト.test(文字列)
let regexp = /cat/; let str1 = 'My cat likes to nap in the sun.'; let str2 = 'I adopted a new kitten last week.'; console.log(regexp.test(str1)); // true console.log(regexp.test(str2)); // false
正規表現にマッチした文字列を取得
execメソッド
exec メソッドは、対象となる文字列が正規表現とマッチした場合に、マッチした文字列を取得する
正規表現オブジェクト.exec(文字列)
[郵便番号を抜き出す例]
let regexp = /[0-9]{3}-[0-9]{4}/; let text = "My postal code is 123-4567."; console.log(regexp.exec(text)); // ['123-4567', index: 18, input: 'My postal code is 123-4567.', groups: undefined]
- 引数に指定した文字列が正規表現とマッチした場合は、マッチした文字列が格納された配列を返す
- マッチしなかった場合は null を返す
- 戻り値の配列にはインデックス 0 の要素にパターン全体にマッチした文字列、インデックス 1 以降の要素には設定したキャプチャグループで囲んだパターンにマッチした文字列が格納される
抜き出した値を取得するには配列なので以下のようにする
let regexp = /[0-9]{3}-[0-9]{4}/; let text = "My postal code is 123-4567."; let result = regexp.exec(text); console.log(result[0]); // 123-4567
エラーの場合には以下のようにな値が配列の要素0に格納されているのがわかる
let regexp = /[0-9]{3}-[0-9]{4}/; let text = "My postal code is 111."; let result = regexp.exec(text); console.log(result[0]); // Uncaught TypeError: Cannot read properties of null (reading '0')
エラーになってしまうので条件分岐または例外処理をすると良い
let regexp = /[0-9]{3}-[0-9]{4}/; let text = "My postal code is 111."; let result = regexp.exec(text); if (result !== null) { console.log(result[0]); } else { console.log("Not Match.") } // Not Match.
パターンにキャプチャグループが含まれる場合
キャプチャグループ(Capture Group)は、正規表現(正規表現パターン)内で一部のパターンにマッチしたテキストを個別に取り出すための仕組み。キャプチャグループは、正規表現内の括弧 () で囲まれた部分にあたる。
正規表現パターンの一部にマッチした文字列を個別に取得したり、後で参照したりする場合に使用される
const pattern = /(\d{2})-(\d{2})-(\d{4})/; const date = "12-31-2022"; const match = pattern.exec(date); if (match) { const day = match[1]; // キャプチャグループ1から日を取得 const month = match[2]; // キャプチャグループ2から月を取得 const year = match[3]; // キャプチャグループ3から年を取得 console.log(match[0]); // 12-31-2022 console.log("Year: " + year); // Year: 2022 console.log("Month: " + month); // Month: 31 console.log("Day: " + day); // Day: 12 }
マッチした文字列の位置を参照
exec メソッドを実行して文字列が正規表現にマッチした場合、マッチした文字列の先頭文字のインデックスが戻り値として取得した配列の index プロパティに設定されいる。
これです:['123-4567', index: 18, input: 'My postal code is 123-4567.', groups: undefined]
let regexp = /[0-9]{3}-[0-9]{4}/; let text = "My postal code is 123-4567."; console.log(regexp.exec(text)); // ['123-4567', index: 18, input: 'My postal code is 123-4567.', groups: undefined]
index: 18
のように文字列の何個目の要素でマッチしているかがわかる。プロパティなので.index
で取り出せる。
let regexp = /[0-9]{3}-[0-9]{4}/; let text = "My postal code is 123-4567."; let result = regexp.exec(text); // ['123-4567', index: 18, input: 'My postal code is 123-4567.', groups: undefined] console.log(result.index); // 18
正規表現にマッチした文字列のインデックスを取得
String オブジェクトのインスタンスメソッド search
は、対象の文字列が正規表現とマッチした場合、マッチした最初の文字列の先頭文字のインデックスを返す。
文字列.search(正規表現オブジェクト)
let regexp = /[0-9]{3}-[0-9]{4}/g; let text1 = "My postal code is 123-4567."; let text2 = "123-4567 is my postal code."; console.log(text1.search(regexp)); // 18 console.log(text2.search(regexp)); // 0
このように正規表現にマッチした文字列のインデックスを返してくれている。該当箇所が二箇所あっても最初にマッチしたインデックスしか返さないことに注意する!
正規表現にマッチするものがない場合
-1
という値が返ってくる。
let regexp = /[0-9]{3}-[0-9]{4}/g; let text1 = "My postal code is 123-4567."; let text2 = "My postal code is nothing."; console.log(text1.search(regexp)); // 18 console.log(text2.search(regexp)); // -1
正規表現にマッチしたすべての文字列を取得
文字列.match(正規表現オブジェクト)
- String オブジェクトの match
メソッドは、正規表現にマッチした文字列を配列で取得(正規表現にgフラグ必要)
- 正規表現のパターンにキャプチャグループが含まれていた場合は、キャプチャグループにマッチした文字列も併せて取得
let regexp = /[0-9]{3}-[0-9]{4}/g; let text1 = "My postal code is 123-4567. My postal code is 765-4321."; result = text1.match(regexp); console.log(result); // ['123-4567', '765-4321'] console.log(result[0]); // 123-4567 console.log(result[1]); // 765-4321
グローバル関数
parseInt関数(文字列を整数に変換)
parseInt(文字列 [, 基数])
- JavaScript のグローバル関数のひとつである parseInt は文字列を整数に変換した値を返す
- 対象の値が文字列でない場合は文字列に変換したあとで整数に変換される
- (Number オブジェクトにグローバル関数の parseInt 関数と同じ動作をする Number.parseInt メソッドがある)
- 第一引数に指定した文字列を整数に変換
- 第二引数(省略可能)で整数の基数を指定(10進数の場合は10)。 2 から 36 までの数値で指定可能
- 初めの文字が数値に変換できない場合は NaN
- 文字列の先頭にある空白を取り除く
- 先頭に
+
または-
がある場合は符号として扱う - 数値でない文字が現れる直前までの文字列を整数に変換
parseInt("20", 10); // 14 parseInt(" -100cm", 10); // -100 parseInt("Hi", 10); // NaN parseInt('100', 2); // 4
parseFloat関数(文字列を浮動小数点数に変換)
parseFloat(文字列)
- 引数に指定した文字列を浮動小数点数に変換
- 引数が文字列でない場合は文字列に変換したあとで浮動小数点数に変換
- parseInt 関数とは違い paseFloat 関数では0xや 0Xで始まる文字は 16 進数の文字とは認識されない
parseFloat("20"); // 20 数値はそのまま parseFloat(" 1.2345cm"); // 1.2345 parseFloat("3.2e2"); // 320.0 parseFloat("Hello2024"); // NaN
isFinite関数(値が有限か無限かを判定する
isFinite(値)
isFinite
は対象の値が有限の値か無限の値かを調べてtrue
または false
を返す
- Number オブジェクトで
isFinite
メソッドがありより厳格に有限の数値かどうかを調べられる - 引数に指定した値が有限の値かどうかを調べ、値が NaN ・正の無限大( Infinity )・負の無限大( -Infinity )であれば false 、それ以外は true を返す
- 値が数値型ではない場合はまず数値型に変換されたあとで調べる
isFinite(Infinity); // false isFinite(-Infinity); // false isFinite(NaN); // false isFinite(20); // true isFinite(0); // true isFinite(-5); // true isFinite('Hi'); // false 数値に変換されるとNaN isFinite('100'); // true 数値に変換される isFinite(''); // true 数値に変換すると 0
DOM操作
ながくなってきたので別記事にしています。
イベント処理
こちらも記事が長くなってきたので、別記事にまとめています。
日付・時刻・曜日を扱う方法【Dateオブジェクト】
こちらも記事が長くなってきたので、別記事にまとめています。
MDN web docs
MDN Web Docs(Mozilla Developer Network Web Docs)は、Mozillaが提供するウェブ開発に関する公式かつ包括的なサイトです。
MDNは、Web技術やプラットフォームに関する情報、ガイド、リファレンス、チュートリアルなどを提供してくれているのでさらにステップアップしていくために必須なサイトになるので最後にリンクを貼って終わりたいと思います。
【対策はfreezeメソッド】Rubyの定数は再代入・変更ができるので注意が必要
まず前提として、この記事を書いているのは未経験からプログラミングに挑戦中の初学者になりますのでご注意ください。 学習はフィヨルドブートキャンプに参加しながら取り組んでいて、現役のエンジニアの方からレビューをもらいながら進めています。 現在の学習状況は以下を随時更新しているので興味ある方はご覧ください。 学習を進めていく中で定数を扱うタイミングがありました。 「定数」と文字だけみると変更できない印象を持ちますが、Rubyにおける定数は警告はでるものの、変更ができてしまうため、変更されたくない場合は対策が必要になります。 特に再代入できるだけでなくミュータブルなオブジェクト(文字列・配列・ハッシュなど)であれば定数の値を変更できてしまうので注意しなければならないことを学びました。 この記事では定数が変更できてしまう点について自分でコードを打っていろいろと確認してみたので、自己学習の記録も兼ねてアウトプットしている記事になります。 結論は「freezeメソッドを使ってオブジェクトを凍結する」になります。凍結されたオブジェクトを変更しようとすると例外 FrozenError を発生させることができます。 ただし、配列やハッシュを使う場合は使い方に注意が必要だと感じました。理由はこれ以降の対策の部分で解説したいと思います。 理由を検索していたらRubyを作った松本行弘氏がQuoraの質問で以下のように回答で以下のように解説していました。 Q.なぜRubyでは定数も再代入可能で、わざわざfreezeを使わないとだめになっているのですか?ハナから再代入不可にしなかった理由は? この質問は、定数という言葉が示すと考えられるふたつの役割が混同されています。そして、Rubyではこのふたつの役割は明確に異なるものです。 第一の役割は、参照している値(オブジェクト)が変化しない、というものです。Rubyの定数はこの役割だけを持ち、大文字から始まる名前は定数として、その参照先を変えないことになっています。質問のうち、「再代入可能で、ハナから再代入不可にしなかった」というのはこの役割を示しています。 これは確かにその通りで、Rubyでは定数に再代入すると、警告こそ出力されますが、それを無視さえすれば、定数の値を書き換えることができます。 なぜそうなっているかというと、Rubyをアプリ組込みに利用して、たとえばRubyを組み込んだエディタを開発した場合、Rubyで記述した設定ファイルに書いてある定数が書換不能でエラーになってしまった場合、設定ファイルとしての使い勝手が悪くなることが想定されたからです。そこで、定数であるにも関わらず、書き換え禁止は警告のみにとどめ、やろうと思えば書き換えられるが紳士協定として、定数とするという方針にしました。 定数のもうひとつの意味は、「参照先が書き換わらない」という意味です。Cのconstはこちらの意味も持っていますが、多くのオブジェクトが書換え可能(mutable)なRubyでは、定数にはこの役割は与えられていません。定数の参照先のオブジェクトが書き換わるのを禁止したければ、「わざわざfreezeを使わないとだめ」です。これはRubyの「定数」とは無関係です 定数には 1.「参照している値(オブジェクト)が変化しない」 2.「参照先が書き換わらない」 の2つの役割があり、Rubyの定数は「参照している値(オブジェクト)が変化しない役割」だけを持っているため再代入や変更ができると説明されていました。 なるほど。難しいけど興味深い…。 実行してみると再代入後の値が返って変更ができていることが確認できます。 クラス外からも変更できました。 クラス自体をfreezeすると変更を防止できる しかし、私が学習しているフィヨルドブートキャンプでメンターもしていて、有名なチェリー本を書いている伊藤さんによれば「Rubyの場合、普通は定数を上書きする人はいないためクラスをfreezeまでする例は少ない」とのことです。 しかし、変更できるという点は知っておく必要はありそうです。 定数に配列を入れた場合で実際にコードを入力してみると以下のように変更できることが確認できました。 追加もできる 削除もできる freezeメソッドを使って凍結すると? freezeを使って凍結すると変更に対してFrozenErrorが返ってくるようになる。 しかし、この状態ではまだ完全に変更を防ぐことができているわけではないことに注意! 配列やハッシュをfreezeしても配列・ハッシュそのものは凍結できても各要素については凍結されていない。 このように変更できることがわかります。要素に対する変更も防ぎたい場合は以下のように、各要素に対してもfreezeする必要があります! 追加できる 削除もできる 変更もできる freezeすると凍結できるが要素までは防げない ハッシュ自体は凍結されている 要素に対しての変更はできてしまう ハッシュの場合はeachしても長ったらしいですし、mapは配列の戻り値になっていまいます。以下のように要素ごとにfreezeするのもなんか不恰好。 Hash#transform_values (Ruby 3.2 リファレンスマニュアル) 以上、Rubyの定数について深掘りしてみました。 今回調べてみて定数には 「参照している値(オブジェクト)が変化しない」 「参照先が書き換わらない」 という2つの役割がそもそもあり、Rubyの定数は 「参照している値(オブジェクト)が変化しない役割 だけを持っているため、再代入や変更ができる」 という理由を確認することができました。 またその対策に
はじめに
前提
Rubyの定数は書き換えができてしまうので注意が必要
結論:freezeメソッドでオブジェクトを凍結(内容の変更を禁止)する
そもそもなぜRubyの定数は変更ができてしまうのか調べてみた
定数の変更ができてしまう事例と対策
クラスをfreezeして変更を防ぐ事例
class Cafe
COFFE_PRICE = 300
COFFE_PRICE = 500
end
p Cafe::COFFE_PRICE
#=> warning: already initialized constant Cafe::COFFE_PRICE
#=> warning: previous definition of COFFE_PRICE was here
#=> 500
class Cafe
COFFE_PRICE = 300
end
Cafe::COFFE_PRICE = 500
p Cafe::COFFE_PRICE
#=> warning: already initialized constant Cafe::COFFE_PRICE
#=> warning: previous definition of COFFE_PRICE was here
#=> 500
class Cafe
COFFE_PRICE = 300
end
Cafe.freeze
Cafe::COFFE_PRICE = 500
p Cafe::COFFE_PRICE #=> can't modify frozen #<Class:Cafe>: Cafe (FrozenError)
定数(配列)が変更されてしまう例と対策
MEMBER = ['Foo', 'Bar', 'Baz']
MEMBER[0].upcase!
p MEMBER #=> ["FOO", "Bar", "Baz"]
MEMBER = ['Foo', 'Bar', 'Baz']
MEMBER.push('Piyo')
p MEMBER #=> ["Foo", "Bar", "Baz", "Piyo"]
MEMBER = ['Foo', 'Bar', 'Baz']
MEMBER.pop
p MEMBER #=> ["Foo", "Bar"]
MEMBER = ['Foo', 'Bar', 'Baz'].freeze
MEMBER.push('Piyo')
p MEMBER #=> can't modify frozen Array: ["Foo", "Bar", "Baz"] (FrozenError)
MEMBER = ['Foo', 'Bar', 'Baz'].freeze
MEMBER[0].upcase!
p MEMBER #=> ["FOO", "Bar", "Baz"]
MEMBER = ['Foo', 'Bar', 'Baz'].map(&:freeze).freeze
MEMBER[0].upcase!
p MEMBER #=> can't modify frozen String: "Foo" (FrozenError)
定数(ハッシュ)が変更されてしまう例と対策
FILE_TYPE = {
'file' => '-',
'directory' => 'd',
'link' => 'l'
}
FILE_TYPE.store('Foo', 'Bar')
p FILE_TYPE #=> {"file"=>"-", "directory"=>"d", "link"=>"l", "Foo"=>"Bar"}
FILE_TYPE = {
'file' => '-',
'directory' => 'd',
'link' => 'l'
}
FILE_TYPE.delete('link')
p FILE_TYPE #=> {"file"=>"-", "directory"=>"d"}
FILE_TYPE = {
'file' => '-',
'directory' => 'd',
'link' => 'l'
}
FILE_TYPE['link'].upcase!
p FILE_TYPE #=> {"file"=>"-", "directory"=>"d", "link"=>"L"}
FILE_TYPE = {
'file' => '-',
'directory' => 'd',
'link' => 'l'
}.freeze
FILE_TYPE.delete('file')
p FILE_TYPE #=> can't modify frozen Hash: {"file"=>"-", "directory"=>"d", "link"=>"l"} (FrozenError)
FILE_TYPE = {
'file' => '-',
'directory' => 'd',
'link' => 'l'
}.freeze
FILE_TYPE['link'].upcase!
p FILE_TYPE #=> {"file"=>"-", "directory"=>"d", "link"=>"L"}
FILE_TYPE = {
'file' => '-'.freeze,
'directory' => 'd'.freeze,
'link' => 'l'.freeze
}.freeze
FILE_TYPE['link'].upcase!
p FILE_TYPE #=> can't modify frozen String: "l" (FrozenError)
transform_values
という便利なメソッドを発見したので以下のようにもできました。transform_values
はすべての値に対してブロックを呼び出した結果で置き換えたハッシュを返します。キーは変化しません。FILE_TYPE = {
'file' => '-',
'directory' => 'd',
'link' => 'l'
}.transform_values(&:freeze).freeze
FILE_TYPE['link'].upcase!
p FILE_TYPE #=> can't modify frozen String: "l" (FrozenError)
最後に
freeze
メソッドを使う方法があるが、ハッシュや配列に対してfreezeしても各要素には凍結が適用されないことにも注意が必要だと感じました。
【merge済】プログラミング初心者がOSS活動に挑戦してIssue→PRまでやってみた
まず前提として、この記事を書いているのは未経験からプログラミングに挑戦中の者になります。学習はフィヨルドブートキャンプに参加しながら取り組んでいて、現役のエンジニアの方からレビューをもらいながら進めています。 現在の学習状況は以下を随時更新しているので興味ある方はご覧ください。 何が伝えたいかというと、まだ学習中の身である私が、OSS活動に参加し、GitHubでIssue・PRをOSSの開発者に送る経験をしたということです。 OSS活動というと「技術力と経験に長けた方々がひしめく異世界の活動」で私のような学習中のレベルの人間は参加するべきはないというイメージを持っていました。
しかし、そんなことはなく「OSS活動は誰でも参加できる活動」であることを体験を通して感じることができました。 この体験をアウトプットすることで、OSS活動に興味があっても、まだ自信のない学習中の方やビギナーの方の背中を押せる内容になれば良いなという思いを込めて発信したいと思います。 オープンソースソフトウェア(Open Source Software)は、頭文字を取ってOSSと略されます。作成者がソースコードを無償で公開していて、利用や改変、再配布が自由に許可されているソフトウェアのことを言います。 またOSS活動とは、オープンソースソフトウェアの開発と維持管理に取り組む活動です。 OSS活動には、新しいオープンソースソフトウェアの開発、既存の製品のメンテナンス、関連するドキュメンテーションの更新など、さまざまな取り組みが含まれます。 OSSは、誰でも自由に改良し、再配布できるという特徴があるため、ユーザーの中から誰かがソースコードのバグを見つけ、改善して修正するというサイクルが世界中で行われています。 この活動により、ソフトウェアの安定性が向上し、低コストで高品質な開発が実現できるというメリットがあるそうです。 私が現在学んでいるプログラミング言語の1つであるRubyも、ブログなどがつくれる有名なWordPressもOSSです。 我々が使うものは多くのOSSの上に成り立っていると言っても過言ではありません。 参加しているフィヨルドブートキャンプでオンラインプログラミングスクールのフィヨルドブートキャンプとOSS Gateのコラボレーション企画開催!!というイベントがあり、これは参加するしかない! と思い立って申込開始と同時に申し込みしました😄 OSS Gateさんは、OSS開発に参加する「入り口」を提供する取り組みをしている団体です。 OSS開発に未参加の人を参加する人へ、少し参加したことがある人を継続的に参加する人へ。そうやってOSS開発に参加する人を継続的に増やしていく。それが「OSS Gate」の目的です。 また私は元々ブロックチェーンの技術にも興味があり、学習のアウトプットも兼ねてブロックチェーンのノードを作って運用をしています。 ブロックチェーンは、運営がいなくても成り立つ非中央集権的な仕組みとなっています。なので開発などはOSSとなっていて、まさにOSS活動に支えられているのでこの活動に興味がありました。 当日は以下のようなスケジュールで行われました。
このイベントに参加してみて1番頭に残ったのは「それぞれができることを、できる範囲で貢献する」という言葉です。 冒頭でもお伝えしたように、私はOSS活動というと「技術力と経験に長けた方々がひしめく異世界の活動」で私のような学習中のレベルの人間は参加することができない、というイメージを持っていました。同じような印象を持っている方もきっと多いのではないでしょうか?
しかし、さまざまなレベルの人がいて・状況や環境(時間的制約など)もさまざまです。「各々ができる範囲で貢献する」という言葉を聞いて私の中で一気にハードルが下がりました。 またプログラミングのレベルがビギナーであれば、ビギナーにしか気付けないこともあります。 OSS Gate Workshopで用意されているWork logを作成。このワークショップでは作業の記録や気付きなどを細かく投稿しながら進め、適宜アドバイスをもらいながら進めていきます。 リンクから飛んでいただけると雰囲気を掴んでもらえると思います。 まずは今回貢献するOSSを選定します。自分の使っている身の回りのものもOSSである可能性があります。ブラウザやエディターの拡張機能や、Rubyのgemなどももちろん対象です。 私はRuby on Railsの言及機能(モデルの自己結合&多対多のアソシエーション)実装のプラクティスの歓迎要件になっていた、「文字列にURLが含まれていたら自動でハイパーリンクになる」という機能を実装するために使ったrails_autolinkを選定しました。 選定したらOSSであるかのライセンスを確認します。公式サイトやGitHubを確認します。私が選定したgemのライセンスはGitHub上で確認しました。 今回私の選んだOSSは Licenseの中にも Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the 'Software'), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:(一部抜粋) まずはユーザーとしてOSSを利用します。その中で何か改善できることはないか、問題がないかを確認していきます。 ひとまずGitHubのREADME通りに進めていきます。そこでREADMEの指示通りに動かしてもエラーになる、意図した挙動にならない等がでてくればIssueの候補になります。 貢献できそうな内容があればそれをまとめてIssueを作って投稿します。 エラーになるまでに行った手順、またエラーであればそれぞれの実行環境によって挙動が変わる可能性もあるので、エラーになった自分自身の実行環境についてもしっかり具体的に伝えます。 意外と「具体的」というのが難しく、「無意識に前提だと思い込んでいると、つい伝えない情報がでてしまうので注意しましょう」と講義の中でもアドバイスがありました。相手にエスパーさせなくてもエラーの再現ができるよう情報を過不足なく伝えることが大切とのことでした(OSのバージョンetc)。 貢献の方法は状況についての報告をするIssueでも良いですし、改善できる内容であればPRを出してみても良いとのことでした。 今回はOSS Gateということでまず日本語でIssueの内容を書いてそれをレビューしてもらい、ブラッシュアップしてから英語に翻訳してコントリビュートしました。 今回私が選んだrails_autolinkに特にエラーは見受けられなかったので、READMEの改善提案をしました。 現状のREADMEは以下のように使用例としてコードと実行結果のみが書かれています。 もちろん、「読めばいいじゃん!」と言われればその通りですが何ができるのかを理解するのに少し時間がかかります。また一つずつが別の例なのか、手順なのかパッとみて判断できない状況のように感じました。 とくに私のようなビギナーであっても、瞬時にこのgemの魅力・実現できることが理解できるREADMEだと助かるな〜と感じたのでREADMEの改善提案することにしました。 私が作った改善提案は以下のようになりました。 基本的な使い方 URLのみをリンクに変換する メールアドレスのみをリンクに変換する HTMLタグをサニタイズせずにリンクを生成する リンクのカスタマイズとテキストの短縮 このように簡単に1文を添えるだけで何ができるgem なのか瞬時に判断して必要な部分だけ確認することができます。 これもビギナーだからこそ感じる(楽したいだけ?😅)部分でもあるのかな〜なんて感じました。
これを英語に翻訳してIssueとしました。またサポーターの方から、今回はREADMEの修正で差分がでるほうが確認しやすいのでPRという形で出してみては?とアドバイスをもらいPRとしても提出してみました! mergeされるかどうか見守っていきたいと思います(ドキドキ)。 無事mergeされました!READMEが少し見やすくなりました😊貢献できたことが嬉しいですね〜!
本当に良い経験になりました! Merge pull request #86 from hirano-vm4/update_README · tenderlove/rails_autolink@2bcb324 今回、貴重な機会をいただいたフィヨルドブートキャンプとOSS Gateの皆様に感謝です!本当にありがとうございました。 そして、挑戦した自分を少しだけ褒めたいと思います。何事も一歩踏み出してやってみることが大切だと思うので、できる範囲でこれからも挑戦していきたいと思います! 今回の参加を通じて伝えたいメッセージは、「OSS活動は誰でも参加できる活動である」ということです。 OSS活動は「各自ができる範囲で貢献する活動」です。私自身、まだ学習途中で現状はビギナーレベルです。 しかし、ビギナーであるからこそ感じること・気づきを共有することで、プロフェッショナルの方々はより専門的な分野に集中できます。 このように、いろんな人が協力し合って成り立つのがOSS活動で、そしてこれが魅力なのかもしれません。 異なるバックグラウンドやスキルを持つ人々が協力し、成り立つ世界って素晴らしいと感じると同時に、恩恵を多大に享受していることに気付かされた1日でした。
はじめに
前提
OSS活動とは
イベントの背景・参加の動機
イベントの概要
ワークショップやセッションの内容
興味深いトピックと学びのポイント
OSS活動の流れ
1.まずはWorklogを作成
2.OSSの選定
3.ライセンスの確認
MIT License
ということがわかりました。MIT License
をOpen Source Initiative で検索しでてくればOSSということになります。以下の条件に従う限り、無償で提供されます。本ソフトウェアおよび関連する文書ファイル(以下、「ソフトウェア」と呼びます)のコピーを取得した任意の個人に、ソフトウェアを制限なく扱い、使用、複製、変更、統合、公開、配布、サブライセンス許諾、販売する権利を含むがこれに限定されない権利を付与します。…(省略)
という旨の表記が確認がとれました。4.ユーザーとしてOSSを利用して気付きを得る
5.気付きをIssueやPRという形で貢献する
初めてのOSSコントリビューション
Issueの報告とPR
require 'rails_autolink'
auto_link("Go to http://www.rubyonrails.org and say hello to david@loudthinking.com")
# => "Go to <a href=\"http://www.rubyonrails.org\">http://www.rubyonrails.org</a> and
# say hello to <a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>"
auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :link => :urls)
# => "Visit <a href=\"http://www.loudthinking.com/\">http://www.loudthinking.com/</a>
# or e-mail david@loudthinking.com"
auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :link => :email_addresses)
# => "Visit http://www.loudthinking.com/ or e-mail <a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>"
auto_link("Go to http://www.rubyonrails.org <script>Malicious code!</script>")
# => "Go to <a href=\"http://www.rubyonrails.org\">http://www.rubyonrails.org</a> "
auto_link("Go to http://www.rubyonrails.org <script>alert('Script!')</script>", :sanitize => false)
# => "Go to <a href=\"http://www.rubyonrails.org\">http://www.rubyonrails.org</a> <script>alert('Script!')</script>"
post_body = "Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com."
auto_link(post_body, :html => { :target => '_blank' }) do |text|
truncate(text, :length => 15)
end
# => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.m...</a>.
require 'rails_autolink'
auto_link("Go to http://www.rubyonrails.org and say hello to david@loudthinking.com")
# => "Go to <a href=\"http://www.rubyonrails.org\">http://www.rubyonrails.org</a> and
# say hello to <a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>"
auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :link => :urls)
# => "Visit <a href=\"http://www.loudthinking.com/\">http://www.loudthinking.com/</a>
# or e-mail david@loudthinking.com"
auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :link => :email_addresses)
# => "Visit http://www.loudthinking.com/ or e-mail <a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>"
## デフォルトではHTMLタグをサニタイズして、悪意のあるコードから保護
auto_link("Go to http://www.rubyonrails.org <script>Malicious code!</script>")
# => "Go to <a href=\"http://www.rubyonrails.org\">http://www.rubyonrails.org</a> "
## :sanitize => falseをオプションにつけることでサニタイズさせない
auto_link("Go to http://www.rubyonrails.org <script>alert('Script!')</script>", :sanitize => false)
# => "Go to <a href=\"http://www.rubyonrails.org\">http://www.rubyonrails.org</a> <script>alert('Script!')</script>"
post_body = "Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com."
auto_link(post_body, :html => { :target => '_blank' }) do |text|
truncate(text, :length => 15)
end
# => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.m...</a>.
今回の成果と貢献
提出したIssue
提出したPR
2023/10/19追記 無事mergeされました
感想とまとめ
参考文献とリソース
Rubyでクラスメソッドをprivateしても呼び出せるので注意が必要
- はじめに
- classでprivate以下に記述したとしてもクラスメソッドはprivateにならない
- 方法1:class << self構文を使う
- 方法2: private_class_methodを使う
- 意図しないメソッドの可視性に注意したい
はじめに
私は現在、フィヨルドブートキャンプで、未経験からプログラミング学習をしているものになります。
この記事は、自分自身が学習したことをまとめ、アウトプットすることを目的として書いていますので、間違いがある可能性がありますのでご注意ください。
classでprivate以下に記述したとしてもクラスメソッドはprivateにならない
Rubyでクラス内にprivateを記述し、その配下にクラスメソッドを定義しても外部から呼び出せてしまうので注意が必要です。
class Cat private def self.hello 'meow' end end Cat.hello #=> "meow"
このように呼び出せてしまいました。
privateメソッドが有効なのはインスタンスメソッドだけです。
クラスメソッドをprivateの範囲にいれるには2つ方法があります。
方法1:class << self構文を使う
class Cat class << self private def hello 'meow' end end end Cat.hello #=> private method `hello' called for Cat:Class (NoMethodError)
方法2: private_class_methodを使う
class Cat def self.hello 'meow' end private_class_method :hello end Cat.hello #=> private method `hello' called for Cat:Class (NoMethodError)
クラスメソッドを定義した後にpraivate_class_method
でprivateのメソッドを指定します。
意図しないメソッドの可視性に注意したい
自分で呼び出せると思っていた範囲以上にメソッドが呼び出せてしまうのは意図しないエラーやトラブルにつながる可能性もあるので基礎知識として知っておかなければならないことを勉強していて改めて感じました!
日々勉強ですね🔥
rubyで正規表現で扱う時に%rを使うと便利
はじめに
私は現在、フィヨルドブートキャンプで、未経験からプログラミング学習をしているものになります。
この記事は、自分自身が学習したことをまとめ、アウトプットすることを目的として書いていますので、間違いがある可能性がありますのでご注意ください。
rubyで正規表現を使ってURLを抜き出す際にスラッシュが多くてみにくい問題
学習しているプラクティスの中で、文字列中の特定のURL(http://localhost:3000/reports/日報のid
)を抜き出す場面がありました。
String#scan (Ruby 3.2 リファレンスマニュアル)メソッドを使ってhttp://localhost:3000/reports/日報のid
を抜き出すコードは以下のようになります。
content.scan(/http:\/\/localhost:3000\/reports\/\d+/)
これでも実現はできるのですが、どうしても/(スラッシュ)
が多くなるのでその都度、エスケープの\(バックスラッシュ)
が多くなりとても見にくいです\(^o^)/
%rを使った書き方にするととても見やすい
書式は以下の通りです。
%r{パターン}
これを先ほどの例に当てはめると以下のようになります。
content.scan(%r{http://localhost:3000/reports/\d+})
これで非常に見やすくなりました。
他の記述パターン
%r
で始まり、対になった区切り文字で文字の集合を囲うことで文字列を作成できるので以下のような記号を使うこともできます。
%r[パターン] %r(パターン) %r<パターン> %r|パターン| %r!パターン! %r*パターン*
逆に区切り文字に使用した文字がパターン内で現れる場合にはエスケープが必要な点には注意が必要です。パターン内で使用されない区切り文字を使うのが良さそうです。