🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

2回答

1034閲覧

持っているはずなのに cannot read propertyと言うわれる

REIA

総合スコア27

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2019/12/01 10:16

授業でjavascriptをさらっと触る機会があったので、javascriptだけでwebサービスを作ろうとしています。
当然javascriptだけなので運用ではなく何ができるのかという感じで触っています。
今回の作成ではタスク管理サイトを作っていて流れとしては以下になります。
0. プラスボタンをクリック
0. タスクグループのdivとタスクの一個目とタスク追加ボタンが出る
0. タスク追加ボタンをクリックするとタスクが追加される

というような簡単な流れになっています。
いかにコードとエラーを記載します。

HTML

1<!DOCTYPE html> 2<html lang="en"> 3<head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 7 <link rel="stylesheet" href="../css/style.css"> 8 <title>Document</title> 9 <link rel="stylesheet" href="../css/style.css"> 10 <script src="../javascript/taskbox.js"></script> 11 <script src="../javascript/main.js"></script> 12</head> 13<body> 14 <p>+ボタンを押すことタスクグループを追加できます。</p> 15 <svg id="add_button" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" preserveAspectRatio="xMidYMid meet" viewBox="0 0 640 640"><defs><path d="M250 0L380 0L380 640L250 640L250 0Z" id="ai3n3xJWd"/><path d="M0 250L640 250L640 350L0 350L0 250Z" id="ceUN5fTae"/></defs><g><g><use xlink:href="#ai3n3xJWd" opacity="1" fill="#000000" fill-opacity="1"/> 16 <g><use xlink:href="#ai3n3xJWd" opacity="1" fill-opacity="0" stroke="#ef1581" stroke-width="0" stroke-opacity="1"/></g></g><g><use xlink:href="#ceUN5fTae" opacity="1" fill="#000000" fill-opacity="1"/></g></g></svg> 17 <div id="task_box_group"></div> 18</body> 19</html>

javascript

1//main.js 2var task_box_group; 3var task_boxCount=0; 4var taskGroup=[]; 5window.onload=function(){ 6 task_box_group=document.getElementById("task_box_group"); 7 document.getElementById("add_button").addEventListener("click",function(){ 8 taskGroup.push(new taskbox(task_box_group,String(task_boxCount))); 9 task_boxCount++; 10 11 }); 12 13} 14

javascript

1//taskbox.js 2class taskbox{ 3 task_box=this.createTag("div",{},{'margin':'10px','float':'left','width':'200px','height':'300px','background-color':'rgba(255, 255, 255, 0.589)'}); 4 task=this.createTag("input",{'type':'text','placeholder':'タスクを入力してください'},{'border-style':'none','margin':'3px','border-radius':'3px','width':'180px'}); 5 button_task_add=this.createTag("button",{},{}); 6 taskCount=0; 7 parent; 8 add_task_id; 9 /** 10 * 11 * @param {HTMLElement} parent this contained html parent 12 * @param {string} id this id 13 */ 14 constructor(parent,id){ 15 this.task_box.id=id; 16 this.parent=parent; 17 this.task.id=this.task_box.id+this.taskCount; 18 this.taskCount++; 19 this.button_task_add.innerText="追加"; 20 this.button_task_add.id=this.task_box.id+"add"; 21 this.add_task_id=this.button_task_add.id; 22 this.parent.appendChild(this.task_box); 23 this.task_box.appendChild(this.task); 24 this.task_box.appendChild(this.button_task_add); 25 this.button_task_add.addEventListener("click",this.task_add); 26 } 27 /** 28 * @param element html tag name : string 29 * @param attrs atatch atribute : Dictionary<string,string> {attrName: attrValue} 30 * @param styles atatch style : Dictionary<string,string> {property:value} 31 * @return HTMLElement 32 */ 33 createTag(element="a",attrs,styles){ 34 var tag=document.createElement(element); 35 for(var attr in attrs){ 36 tag.setAttribute(attr,attrs[attr]); 37 } 38 for(var style in styles){ 39 tag.style.setProperty(style,styles[style]); 40 } 41 return tag; 42 } 43 task_add(){ 44 this.button_task_add.remove(); 45 this.task.id=task_box.id+this.taskCount; 46 this.task_box.appendChild(this.task) 47 this.task_box.appendChild(this.button_task_add); 48 this.taskCount++; 49 } 50}

error

1taskbox.js:43 Uncaught TypeError: Cannot read property 'remove' of undefined 2 at HTMLButtonElement.task_add (taskbox.js:43) 3task_add @ taskbox.js:43

タスク追加ボタンをクリックするとtask_addが実行されて 
JavaScriptでクラスを使おうとするといつもこのようなエラーが起きます。
持っているはずなのに読めなかったりconsole.logでthis.task_boxを表示してもundefinedになったりよくわかりません。

ご回答の程よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

constructor内の、

JavaScript

1this.button_task_add.addEventListener("click",this.task_add);

について、task_addは

JavaScript

1task_add() { 23}

の形式で書かれていますので、thisはアロー関数のようにレキシカルではなく、ダイナミックにバインドされます。
そして、addEventListenerはコールバックとして受け取った関数のthisにイベントが発生したDOM要素をバインドします。
参考:The value of this within the handler
以下のようにlogしてみれば…

JavaScript

1task_add() { 2 console.log(this); 34}

chromeコンソールなら<button id="0add">追加</button>などと表示されるのが確認できると思います。
thisがtaskboxのインスタンスではなくDOM要素<button id="0add">追加</button>になっているわけですから、this.button_task_add.remove();とやろうとしても、DOM要素<button id="0add">追加</button>にbutton_task_addプロパティがなくundefinedが返り、続く.remove()でご提示のエラーCannot read property 'remove' of undefined at HTMLButtonElement.task_addとなります。

解決策としては、
addEventListenerの際にbind()を使って現在のコンテキストのthisに固定してから渡す

JavaScript

1this.button_task_add.addEventListener("click",this.task_add.bind(this));

または、
task_addの定義に(クラスフィールドと)アロー関数を使う

JavaScript

1// task_add() { 2// … 3// } 4task_add = () => { 56};

などがあると思います

投稿2019/12/01 14:27

shinji709

総合スコア805

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

REIA

2019/12/02 11:20

コンストラクタ内でアロー関数で動くことも理解しました。 でも、私個人的にはコンストラクタ内がゴチャゴチャするので、関数をbindする形のほうがきれいな気がします。 回答ありがとうございました。
guest

0

ベストアンサー

フィールド宣言の部分でthis.を使って呼び出すのではなく、constructorで初期化すべき物なのでは?

【クラス#フィールド宣言 - JavaScript | MDN】
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Classes#Field_declarations

投稿2019/12/01 13:02

kei344

総合スコア69596

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

REIA

2019/12/01 13:24 編集

言われてみればそうですね。 createTagメソッドを使った代入をすべてコンストラクタの中に移しまして、フィールドの部分は 変数名; だけにしました。 それでもconsole.log(this.task_box)をやったところundefinedでした。 先ほど思いついたmain.js上でaddEventListenerをやったら出たは出ましたがどれがクリックされたかの判定ができないためこの方法ではたぶんだめです。 どれがクリックされたかというのはタスクグループは複数存在できるので、それぞれにaddボタンがあります
kei344

2019/12/01 13:59

this.button_task_add.addEventListener("click",this.task_add);で関数task_addを登録されていますが、その実行時のthisはイベントが起きた要素になります。 this.button_task_add.addEventListener("click",this.task_add.bind(this));とかでいけるかな。 動くサンプル: https://jsfiddle.net/8drw927u/ 後はデバッグしてください。
kei344

2019/12/01 14:07

もう一つ補足。appendChild を複数回実行しても、別の要素が追加されるのではなく最初に追加した物が置き換えられるだけになります。 【Node.appendChild - Web API | MDN】 https://developer.mozilla.org/ja/docs/Web/API/Node/appendChild > 追加しようとしたノードが既に存在していたら、それは現在の親ノードから除かれ、新しい親ノードに追加されます 【Node.cloneNode - Web API | MDN】 https://developer.mozilla.org/ja/docs/Web/API/Node/cloneNode
REIA

2019/12/02 11:10

bindに関しては初めて知りました。 これで今までのコードのエラーも全部とれそうです。 同じ変数のappendChildで置き換えられるのは驚きましたが、ローカル変数で作ってreturnで何とかなりました。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問