質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.37%
Vue.js

Vue.jsは、Webアプリケーションのインターフェースを構築するためのオープンソースJavaScriptフレームワークです。

スコープ

スコープとは、プログラム内で変数名など、参照可能な有効範囲のことを指します。

JavaScript

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

Q&A

解決済

2回答

5552閲覧

JavaScript(Vue.js)のaddEventListenerのスコープについて

izaya

総合スコア16

Vue.js

Vue.jsは、Webアプリケーションのインターフェースを構築するためのオープンソースJavaScriptフレームワークです。

スコープ

スコープとは、プログラム内で変数名など、参照可能な有効範囲のことを指します。

JavaScript

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

0グッド

0クリップ

投稿2021/03/16 15:03

編集2021/03/16 16:00

アップロードする画像の高さと幅を取得し、その配列を作りたいと思っています。

vue

1<template> 2 <input type="file" ref="preview" @change="getWidthAndHeight" accept="image/*" multiple> 3</template> 4 5export default { 6 data() { 7 return { 8 widthAndHeightArray: []; 9 } 10 } 11 methods: { 12 getWidthAndHeight() { 13 const files = this.$refs.preview.files; 14 for (let i = 0; i < files.length; i++) { 15 let image = new Image(); 16 image.src = URL.createObjectURL(files[i]); 17 image.addEventListener('load', function() { 18 const widthAndHeight = { 19 width: image.naturalWidth, 20 height: image.naturalHeight, 21 }; 22 this.widthAndHeightArray.push(widthAndHeight); 23 }); 24 } 25 } 26 } 27}

上のようなコードを書いたのですが(重要な部分以外は省略しています)、

this.widthAndHeightArray.push(widthAndHeight);
の部分で「Cannot read property 'push' of undefined」のエラーがでます。

つまり、widthAndHeightArrayが未定義ですよということです。

widthAndHeightArrayはグローバルな変数のはずですが、addEventListenerの中では使えないという仕様なのでしょうか?

どうすればwidthAndHeightArrayにwidthAndHeightをpushすることができるのでしょうか?

試したこと

ダメもとですがaddEventListenerの代わりにonloadを使ってみましたが同じくダメでした。

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

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

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

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

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

guest

回答2

0

ベストアンサー

イベントハンドラー内でthisを使用する場合、オブジェクト(image)内を指すことになるからだと思われます。アロー関数を使用すれば回避できるのではないでしょうか。

javascript

1image.addEventListener('load', ()=> { 2 const widthAndHeight = { 3 width: image.naturalWidth, 4 height: image.naturalHeight, 5 }; 6 this.widthAndHeightArray.push(widthAndHeight); 7 }); 8

リンク内容
リンク内容

ご参考までに。

投稿2021/03/17 00:48

BlueMoon

総合スコア1339

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

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

izaya

2021/03/17 05:39

おっしゃる通りアロー関数にすることでthisがdataを参照するようになり、うまくコードが動きました! thisの参照を確かめるべきでした。。 参考記事まで教えていただいてありがとうございますm(__)m thisの理解が甘かったようですので、そのあたりもう一度勉強してみます。
BlueMoon

2021/03/17 08:03

解決できて良かったです。自分もvueとJSは勉強中です。
guest

0

間違えました。

下記の案のようにimage.addEventListener('load', this.handleOnLoad(image));としてしまうと、load時にthis.handleOnLoad(image)が呼ばれるのではなく、その場でthis.handleOnLoad(image) を実行した値が引数として渡されてしまうので誤りでした。失礼しました。

解決案

イベントハンドラ部分を別途methodsの1つとして定義することで問題を回避できそうです。
例えば、methods部分を下記のように実装します。

getWidthAndHeight() {
const files = this.$refs.preview.files;
for (let i = 0; i < files.length; i++) {
let image = new Image();
image.src = URL.createObjectURL(files[i]);
image.addEventListener('load', this.handleOnLoad(image));
}
},
handleOnLoad(image) {
const widthAndHeight = {
width: image.naturalWidth,
height: image.naturalHeight,
};
this.widthAndHeightArray.push(widthAndHeight);
}

解説

widthAndHeightArrayはグローバルな変数のはずですが、addEventListenerの中では使えないという仕様なのでしょうか?

上記に少し認識間違いがあるかもしれません。
あくまでwidthAndHeightArrayはVueコンポーネントに紐づく変数であり、グローバルと呼ぶには語弊があります。
またthisはVueコンポーネントの中であまりにも多用するため勘違いしそうになりますが、必ずしもVueコンポーネントで定義したdatamethodがある場所を参照するものではありません。thisが何を指すかについて解説すると大変パターンが多くなるので気になれば調べてみてください。
izayaさんが実装しているaddEventlistenerの第2引数に設定しているfunction内にあるthisも期待した所を参照できていません。ここでのthisはおそらくimageを指しています。function内でconsole.log(this)することでそれを確かめることができます。

以上でご説明したとおり、コードが上手く行かない原因はthis.widthAndHeightArraydata内のwidthAndHeightArrayを参照できていないことです。
こちらの解決策は単純に、確実にwidthAndHeightArrayを参照できるmethodとして別でロジックを定義しておいて、解決案でお伝えしたようにimage.addEventListener('load', this.handleOnLoad(image));のように呼び出してあげることです。

何か不明な点があればお伝え下さい。
ご確認をよろしくおねがいします!

投稿2021/03/16 16:05

編集2021/03/17 16:42
marasonPD

総合スコア168

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

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

izaya

2021/03/17 05:34

とても丁寧でわかりやすく説明していただいてありがとうございます! thisが何を参照するのかをきちんと把握すべきでしたね。。ずっと感覚で使ってしまっていました。。 確かにイベントハンドラを別メソッドとして定義することで動くようになりました。 ただ、 handleOnLoad(image) { const widthAndHeight = { width: image.naturalWidth, height: image.naturalHeight, }; this.widthAndHeightArray.push(widthAndHeight); } のところで、console.log(widthAndHeight)を調べてみると、なぜだか{width: 0, height: 0}になっていました。。画像の読み込みが終わっていないのになぜだか処理が実行されているようです。 そこで、 getWidthAndHeight() { const files = this.$refs.preview.files; for (let i = 0; i < files.length; i++) { let image = new Image(); image.src = URL.createObjectURL(files[i]); image.addEventListener('load', () => { const widthAndHeight = { width: image.naturalWidth, height: image.naturalHeight, }; this.widthAndHeightArray.push(widthAndHeight); }); } }, 上記のようにアロー関数にしてあげると、ちゃんとthis.widthAndHeightArrayがdata内のwidthAndHeightArrayを参照するようになりました! これからはmarasonPDさんのおっしゃるよう、うまくいかない場合はthisの参照を確かめてみるようにします!大変勉強になりましたm(__)m
marasonPD

2021/03/17 16:38 編集

あー、、すみません。{width: 0, height: 0}になってしまう件ですが確かに仰るとおりです。 addEventListenerに渡している時点でhandleOnLoad(image)が実行されてしまう(実行して返ってくる値が引数として渡される)ので、良くないコードでした。もしこの形で書くなら結局handleOnLoadでその関数自体をreturnするようにしないといけなそうですね。 それを考えるとイケていないので、もう一人の方が仰る通りその場でアロー関数を用いるのが良さそうです。すみません、こちらも勉強になりました。
izaya

2021/03/18 05:47

あ、なるほどです、引数になってしまうのですね! でもなんか、marasonPDさんのおかげで結果的に+αの学びを得られたのでよかったです^^ 問題解決に必要な前提知識であるthisの参照についてもとても丁寧に解説していただいたので、今後も同じところでつまづくことはなさそうです。ありがとうございましたm(__)m
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.37%

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

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

質問する

関連した質問