- 投稿日:2020-10-13T22:37:15+09:00
[JavaScript] addEventListenerのリスナ関数に引数を渡す&解除可能にする
addEventListenerのリスナ関数に引数を渡す&解除可能にする
解決したい問題
- addEventListenerのコールバック関数に引数を渡したい
- リスナは1回だけ使用してすぐにイベントリスナをremoveしたい
という問題を解決するために至った3通りの解決方法と、その実際を記事にまとめました。
その1: コールバック関数に引数を渡す方法
- Function.prototype.bindを使う
var message = 'Hola!'; var greeting = function(message, event) { console.log(message); console.log(event); }; element.addEventListener('click', greeting.bind(null, message), false);出力結果
Hola! MouseEvent {}
.bind
の第一引数で、ターゲットであるgreeting
関数のthisの値を指定する
null
でthis
はWindow
になる
Event
オブジェクトは、通常必ず引数としてコールバック関数へ渡される
なので呼び出す際はEvent
オブジェクトを明示しなくてもいいこの場合、
Event
オブジェクトは必ず引数群の「一番最後」に追加されている
handleEvent()
を含むオブジェクトを渡す
var message = 'Hola!'; var greeting = function(event) { console.log(this); console.log(this.msg); console.log(event); }; element.addEventListener('click', {handleEvent: greeting, msg: message}, false);出力結果
{msg: message, handleEvent: f} Hola! MouseEvent {}つまりコールバック関数の引数ではなく、代わりにプロパティとして渡す方法です
これが実現できる理由は2つあって
handleEvent
が含まれていること
これが含まれていることによって、addEventListener
に渡したオブジェクトがEventListener
インターフェースとして認識されてhandleEevnt
メソッドをコールバック関数として実行してくれるからコールバック関数のthis値が、
addEventListener
に渡したオブジェクトを指すから
this
がオブジェクトを指すので、オブジェクトのプロパティにコールバック関数がアクセスすることができるようになるという理屈こいつを使う際の問題は、
this
がオブジェクトに固定されてしまうことであり、
たとえばクラスのメソッドにアクセスできない。もしも
click
した要素にアクセスしたい場合、
コールバック関数内でevent.currentTarget
でアクセスすればよい// handleEventとして渡すコールバック関数 var greeting = function(event) { console.log(event.currentTarget); }出力結果
<button class="btn"></button>
- IIFE
var message = 'Hola!'; var greeting = function(message, event) { console.log(message); console.log(event); } element.addEventListener('click', (function(){ return function(e) { greeting(message, e); } })());出力結果
Hola! MouseEvent {}即時関数とクロージャを使う方法
引数は渡せるし、event
オブジェクトも取得できるざっと調べてみたところ以上の3点が確認可能な方法だった
各方法でイベントリスナをremoveしてみる
removeするための条件は、
- コールバック関数が無名関数ではないこと
- 同じ要素・同じ関数名を指定すること
基本の確認:
button.set
をクリックすると、div.box
にイベントリスナがセットされて
引数をコンソール表示するのと、イベントリスナを除去するようにしますHTML
<body> <button class="set">set event listener</button> <div class="box">click me</div> </body>script
var set = document.querySelector('.set'); var box = document.querySelector('.box'); // callback function var handler = function (event) { console.log('box has clicked'); box.removeEventListener('click', handler, false); } set.addEventListener('click', function (event) { console.log('set event listener'); box.addEventListener('click', handler, false); });一度だけコールバック関数が実行され、正常に
handler
をbox
からremove出来たことが確認できました
では引数をコールバック関数に渡しつつ、一度だけ発火するイベントリスナをremoveできるか確認します
- callback.bindの場合
var handler = function (message, event) { console.log(message); console.log(event); box.removeEventListener('click', handler, false); } set.addEventListener('click', function (event) { console.log('set event listener'); var message = 'HOLA!'; box.addEventListener('click', handler.bind(null, message), false); });結果、
message
は表示されたので、引数を渡せていますが
removeは出来ていないようです。
また、何度もset
ボタンをクリックするとbox
へイベントリスナが重複して登録されているのが確認できました。
- IIFEの場合
// callback var handler = function (message, event) { console.log('box has clicked'); console.log(message); console.log(event); box.removeEventListener('click', handler, false); } set.addEventListener('click', function () { var message = 'HOLA!'; box.addEventListener('click', (function () { // `event` should be passed here. return function callback(event) { handler(message, event); box.removeEventListener('click', callback, false); } })(), false); });今回は引数
message
をhandler
へ渡し、
なおかつcallback
をremoveできたことが確認できました
- handlerEvent()含むオブジェクトを渡す場合
// callback var handlerIncaseHandleEvent = function (event) { console.log('box has clicked'); console.log(this.msg); console.log(event); // thisを指定すること box.removeEventListener('click', this, false); } // Object case set.addEventListener('click', function () { var message = "HOLA!"; box.addEventListener('click', { handleEvent: handlerIncaseHandleEvent, msg: message, }, false); });結果、引数を渡すことは成功しており、removeの成功も確認できました。
各方法の一長一短
callback.bind, IIFE, Objectの3通りの方法について、
引数を渡す方法、引数を渡して自らremoveする方法の結果を見てきました。それぞれの方法について長所・短所を独断と偏見でまとめてみます
.bind
スコープをコントロールしやすく、OOP開発のような他のクラスのメソッドを縦断して利用するような開発において、
this値を明示的に指定できるので利用しやすそうと思いましたが
よく考えたら、callback.bind()
がremoveEventListener
では除去できないのは致命的であるように思えますIIFE
結果からみると一番制約がなく使い勝手がよさそうに見えます。
しいて言えば、クロージャのcallbackの中身が冗長になりがちというくらいでしょうか
handleEvent
を含むObject
を渡す場合最もわかりやすいので使いやすいですが、
this
が固定されてしまうので、呼出したcallback関数は自身のクラスのメソッドを使うことができないです最後に
今回の難敵
addEventListener
を手玉に取るために、
「要素の内側と外側を判別するプログラム」を作ってみました
数日中にその記事をあげるかもです。当記事はVanillaJS習作を制作中の初心者開発者による現状の学習経過のアウトプットの一つです。
先人の皆様からの間違いのご指摘や、
よりよい方法を教えてくださるとこれ幸いです。
(OOP開発においてスコープと引数と解除可能をどうやって成立させているのやら)ご覧いただきありがとうございました。
- 投稿日:2020-10-13T22:35:04+09:00
ドットインストール:JavaScript:詳解JavaScript 基礎文法編
参考
わかりやすいのでおすすめ
詳解JavaScript 基礎文法編02 ~ 03 別ファイルから実行等
コード
コード成果物
04 ~ 08 文字列/数値/定数/変数
コード
コード成果物
09 ~ 12 データ型/数字からなる文字列等
コード
コード成果物
13 ~ 15 条件分岐/条件演算子/論理演算子
コード
コード成果物
16 switchで条件分岐
コード
コード成果物
17 ~ 19 for/while/continue、break
コード
コード成果物
20 ~ 21 関数/引数
コード
コード成果物
22 ~ 23 returnで値を返す/関数式
コード
コード成果物
24 アロー関数
コード
コード成果物
25 スコープ
コード
コード成果物
26 ブロック
コード
コード成果物
- 投稿日:2020-10-13T22:25:51+09:00
JavaScript htmlのプルダウンメニューの読み取り方法
・htmlで記述した、プルダウンメニューの作り方
<form id="form> <select name="select"> <option value="index.html>日本語</option> <option value="index-en.html>英語</option> <option value="index-zh.html>中文</option> </select> </form>・フォームに必要不可欠なname属性(入力項目をサーバーで扱うときに使う名前)はselectタグに記述する。optionタグには書かない。
・各optionタグにそのデータを表すvalue属性を記述する。
・プルダウンメニュー(select要素)の場合、selectタグのname属性と選択されたoptionタグのvalue属性がセットになって、サーバーに送信される。ここから、javascriptを記述していきます。
'use strict'; document.getElementById('form').select.onchange=function(){ location.href=document.getElementById('form').select.value; }このコードは、onchangeイベントプロパティが発生したら呼び出される関数を作っている。
onchangeイベントは、「フォームに入力された内容が変わったとき」に発生する。
その関数の処理は、locationオブジェクトのhrefプロパティ(表示しているURL)に、フォームで選択されたoptionのvalue(URL)が代入される。
ここで、疑問なのが、なぜ「.select.value;」となっているのか?
言い換えると、selectタグ内にはvalue属性を書いてないで、optionタグ内にvalue属性を書いたのに、なぜselectの中でvalueプロパティを読み取っているのか?
その理由は、『プルダウンメニューの場合は選択されているoptionのvalue属性を調べるために、その親要素であるselectのvalueプロパティを読み取る。』そして、script.js全体のコードを以下で記述します。
'use strict'; const lang=document.querySelector('html').lang; if(lang==='ja'){ document.querySelector('option[value="index.html"]').selected=true; }else if(lang==='en'){ document.querySelector('option[value="index-en.html"]').selected=true; }else if(lang==='zh'){ document.querySelector('option[value="index-zh.html"]').selected=true; } document.getElementById('form').select.onchange=function(){ location.href=document.getElementById('form').select.value; }documentオブジェクトのquerySelectorメソッドは、()内に書かれた「セレクタ」にマッチする要素を取得する。セレクタとはCSSで使うやつです。例えば、
2行目のセレクタはタイプセレクタで指定している。
4行目のif文ないの処理では、('option[value="index-en.html"]');となっていて、これは属性セレクタです。
属性セレクタとは、 要素名[属性名="属性値"] で指定する方法です。
ここでまた注意点があります。
(複数の要素がマッチしたらどうなるの?という点)
この場合、htmlではマッチした全てが対象になるが、
javascriptでは『最初にマッチした要素』一つだけ対象になる。参考:javascript超入門 P198~
- 投稿日:2020-10-13T20:59:41+09:00
VueでURLからハッシュ("#")を削除し、リロードもできるようにする
概要
Vueアプリはデフォルトでrouterのパスに「ハッシュ(#)」が含まれています。これはrouterを「historyモード」に変更することで削除することができますが、historyモードを使用することでページをリロードした際に、404エラーが返却されてしまいます(historyモードでない場合にはこの問題は発生しません)。
しかしながら一点問題があります。シングルページのクライアントサイドアプリケーションなので、適切なサーバーの設定をしないと、ユーザーがブラウザで直接 http://oursite.com/user/id にアクセスした場合に 404 エラーが発生します。- HTML5 History モード
しかし、適切な設定をすれば「ハッシュを削除しつつ、リロードにも対応する」ことが可能となります。
Expressをインストールする
$ npm install express --saveconnect-history-api-fallbackをインストールする
npm install --save connect-history-api-fallback参考:connect-history-api-fallback
ルートディレクトリにserver.jsを作成する
server.jsvar express = require('express'); var path = require('path'); const history = require('connect-history-api-fallback'); var serveStatic = require('serve-static'); app = express(); app.use(history()); app.use(serveStatic(__dirname + "/dist")); var port = process.env.PORT || 5000; app.listen(port); console.log('server started '+ port);package.jsonを編集する
ルートディレクトリにあるpackage.jsonに追記します。
package.json{ "name": "<プロジェクト名>", "version": "0.1.0", "private": true, "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build", "lint": "vue-cli-service lint", + "start": "node server.js" },アプリを起動する
ルートディレクトリで次のコマンドを実行し、アプリを起動します。
$ npm run build $ node server.jshttp://localhost:5000でアプリにアクセスすると、ページをリロードしても404が返却されない、かつハッシュ記号もurlから削除されているはずです。
参考:
Vue.jsで作ったアプリをHerokuにデプロイ
vue-routerのルーティングURLからハッシュを除去しつつ、URL直接指定でも表示させる(Node, Express)
- 投稿日:2020-10-13T20:53:36+09:00
【初心者でもわかる】同じ属性値(nameやvalue)を持つチェックボックスも連動してチェックさせたり外したりする方法
どうも7noteです。jQueryで同じ属性値同士を連動させる方法について。
全く同じチェックボックスが複数個所にある場面(通常時&モーダル等)に出くわしたとき、通常画面ではチェックを入れているのにモーダルの画面にあるチェックボックスが反映されないので困ってしまいます。
今回は離れた場所でも同じ属性値を持つチェックボックス同士を連動させる方法について書いていきます。
この記事はjQueryを使用しています。(jQueryって何?という方はこちら)
ソース
index.html//通常通り見えている用 <div class="normal"> <label><input type="checkbox" value="りんご">りんご</label> <label><input type="checkbox" value="ばなな">ばなな</label> <label><input type="checkbox" value="みかん">みかん</label> </div> // モーダルで出てくる用 <div class="mordal"> <label><input type="checkbox" value="りんご">りんご</label> <label><input type="checkbox" value="ばなな">ばなな</label> <label><input type="checkbox" value="みかん">みかん</label> </div>script.js$(function(){ $("input[type='checkbox']").on('change', function(){ //チェックボックス(type='checkbox')の値が変更されたとき・・・ cbv = $(this).val(); //クリックされたチェックボックスのvalue値を変数に格納 if( $(this).prop('checked')){ //もしクリックされたチェックボックスがチェックされていたら・・・ $("input:checkbox[value='" + cbv + "']").prop('checked',true); //同じvalueを持つチェックボックスは全部チェックを入れる }else{ $("input:checkbox[value='" + cbv + "']").prop('checked',false); //逆にチェックが外れていたら全部チェックを外す。 } }); });書いてみると大したことはなく、スクリプトだけなら10行以内で書けるほど簡単に書けます。
それぞれのスクリプトが行なっている処理はコメントで書いている通りですが、「チェックボックスにチェックが入ったタイミングで起動し、チェックボックスの状態を確認。その後、それに合わせて同じ値を持つ物に対して全部チェックを入れるか、外すか」という処理をしています。まとめ
javascriptを勉強しはじめの頃は、1文字ズレたり、変数なのか文字列なのかの区別を付けさせるのに苦労しますが、1つ1つ丁寧に解読していけば分からないこともない程度なので、valueじゃなくnameでやったり、他の条件を追加するなどして使ってみてください。
おそまつ!
~ Qiitaで毎日投稿中!! ~
【初心者向け】HTML・CSSのちょいテク詰め合わせ
- 投稿日:2020-10-13T19:43:18+09:00
paralleldots APIをnode経由で使用してみた [感情分析編]
はじめに
paralleldots AI APIというテキストから感情を分析するAPIがあるので、Nodeをプロキシとして利用し、このAPIを使ってみました。
環境
・ node version : v12.18.3
・ npm version : 6.14.6URIと機能
Path HTTPメソッド 機能 /api/v1/emotion POST 入力テキストの全体的な感情と各感情ラベル(Happy、Sad、Angry、Excited、Bored、Fear)の信頼スコアを含むjson応答を返します。 使用したparalleldots AI API
paralleldots AI APIとは??
開発者向けの包括的なドキュメント分類およびAPIのセットです。10億を超えるドキュメントでトレーニングされており、感情分析や感情検出などを提供しているそう。今回は、paralleldots AI APIの[/v4/emotion]こちらを使用していきます。
設定できるパラメータ
名前 詳細 Required Type text 分析したい文章を入力します。 Yes string/array api_key Api key Yes string lang_code 言語コード Yes string ・ ただ、今回は、nodeでプロキシしているので、プロキシサーバ側で[api_key]及び[lang_code]は設定しています。
構成
package.json{ "name": "node_poc", "version": "1.0.0", "description": "paralleldots AI API", "main": "app.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "dependencies": { "axios": "^0.20.0", "express": "^4.17.1" } }app.jsconst express = require("express"); const app = express(); const axios = require('axios'); const server = app.listen(9000, function(){ console.log("Node.js is listening to PORT:" + server.address().port); }); app.post("/api/v1/emotion", function(req, res, next){ let params = new URLSearchParams(); params.append("api_key", '××××××××××××××××××××××××××××'); params.append("lang_code", 'en'); params.append("text", req.query.text); try { axios.post('https://apis.paralleldots.com/v4/emotion', params) .then((response) => { res.send(response.data) }) } catch (error) { console.error(error); } });Response
今回は、requestを日本語で行おうと思ったのですが、[lang_code]を英語以外を使用したい場合は、無料枠では使用できない為、仕方なく英語で行いました。
textには、
Be careful about reading health books. You may die of a misprint.
日本語訳にすると、[健康系の本を読むときは注意しなさい。ミスプリントのせいであなたは死ぬかもしれない。]
という意味です。笑Requestは、Postmanを使用しました。(curlより見やすい為)
・ 実際のResponse
{ "emotion": { "Happy": 0.0872024649, "Angry": 0.2344884125, "Bored": 0.0416403769, "Fear": 0.3095755387, "Sad": 0.1825278824, "Excited": 0.1445653247 } }結果
やはり、死ぬかもしれないという恐怖を入れ込んだ文章を送ったため、Fearが一番結果の数値として高いことが分かります。
精度的にどうなのかは、個人の感性に依存しそうですが、、参照
- 投稿日:2020-10-13T19:43:18+09:00
paralleldots APIをnode経由で使用してみた (感情分析編)
はじめに
paralleldots AI APIというテキストから感情を分析するAPIがあるので、Nodeをプロキシとして利用し、このAPIを使ってみました。
環境
・ node version : v12.18.3
・ npm version : 6.14.6URIと機能
Path HTTPメソッド 機能 /api/v1/emotion POST 入力テキストの全体的な感情と各感情ラベル(Happy、Sad、Angry、Excited、Bored、Fear)の信頼スコアを含むjson応答を返します。 使用したparalleldots AI API
paralleldots AI APIとは??
開発者向けの包括的なドキュメント分類およびAPIのセットです。10億を超えるドキュメントでトレーニングされており、感情分析や感情検出などを提供しているそう。今回は、paralleldots AI APIの[/v4/emotion]こちらを使用していきます。
設定できるパラメータ
名前 詳細 Required Type text 分析したい文章を入力します。 Yes string/array api_key Api key Yes string lang_code 言語コード Yes string ・ ただ、今回は、nodeでプロキシしているので、プロキシサーバ側で[api_key]及び[lang_code]は設定しています。
構成
package.json{ "name": "node_poc", "version": "1.0.0", "description": "paralleldots AI API", "main": "app.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "dependencies": { "axios": "^0.20.0", "express": "^4.17.1" } }app.jsconst express = require("express"); const app = express(); const axios = require('axios'); const server = app.listen(9000, function(){ console.log("Node.js is listening to PORT:" + server.address().port); }); app.post("/api/v1/emotion", function(req, res, next){ let params = new URLSearchParams(); params.append("api_key", '××××××××××××××××××××××××××××'); params.append("lang_code", 'en'); params.append("text", req.query.text); try { axios.post('https://apis.paralleldots.com/v4/emotion', params) .then((response) => { res.send(response.data) }) } catch (error) { console.error(error); } });Response
今回は、requestを日本語で行おうと思ったのですが、[lang_code]を英語以外を使用したい場合は、無料枠では使用できない為、仕方なく英語で行いました。
textには、
Be careful about reading health books. You may die of a misprint.
日本語訳にすると、[健康系の本を読むときは注意しなさい。ミスプリントのせいであなたは死ぬかもしれない。]
という意味です。笑Requestは、Postmanを使用しました。(curlより見やすい為)
・ 実際のResponse
{ "emotion": { "Happy": 0.0872024649, "Angry": 0.2344884125, "Bored": 0.0416403769, "Fear": 0.3095755387, "Sad": 0.1825278824, "Excited": 0.1445653247 } }結果
やはり、死ぬかもしれないという恐怖を入れ込んだ文章を送ったため、Fearが一番結果の数値として高いことが分かります。
精度的にどうなのかは、個人の感性に依存しそうですが、、参照
- 投稿日:2020-10-13T18:58:54+09:00
JavaScript 困ったところまとめ【更新版】
async/await 使い方
asyncとは、
・非同期通信を行う時に宣言する。
index.jsconst number1 = () => { setTimeout(()=>{ console.log("number1") }, 1000) } const number2 = () => { console.log("number2") } const number3 = () => { console.log("number3") } number1() number2() number3() //=>number2 //=>number3 //=>number1index.jsconst number = async() => { const number1 = () => { return new Promise((resolve, reject)=>{ setTimeout(()=>{ console.log("number1") resolve(); }, 1000) }) } const number2 = () => { console.log("number2") } const number3 = () => { console.log("number3") } await number1() number2() number3() } number() //=>number1 //=>number2 //=>number3このようにasync/awaitをしようする事により処理の時間が話からないものや、順番を指定したい時に使えます。
連想配列に要素を追加
index.js/* このようにしたい let dateDropDown = {items : [ {label: 0, value: 0}, {label: 1, value: 1}, {label: 2, value: 2}, {label: 3, value: 3}, ]} 以下のように記述 */ let dateDropDown = {items : []} for (let i = 0; i < 4; i++){ let option = {} option.label = i option.value = i dateDropDown.items.push(option) }
- 投稿日:2020-10-13T17:42:55+09:00
【JavaScript】クロージャを使ってみよう。
こんにちは、今回はクロージャについて大体理解できた!ということで、
クロージャの使い方をメモしていきたいと思います。これからクロージャを勉強する方の助けに少しでもなれば幸いです。
クロージャとは
以下のように定義されています。
クロージャは、関数とその関数が宣言されたレキシカル環境の組み合わせ
MDN | クロージャどういうこと?という感じですが、簡単に言い換えるなら
「関数とその関数から参照できるローカル変数を使用している状態」と言えると思います。言葉ではイメージしづらいので、定義については一旦置いておきまして、クロージャを使うとできることを見ていきましょう。
クロージャを使うことで以下のような機能を持った関数を作ることができます。
①プライベート変数を保持する関数
②動的な関数を生成する関数順番に見ていきましょう。
①プライベート変数を保持する関数
例としてカウンター機能を実装しながら、クロージャの使い方をみていきます。
let counter = 0; //参照できる外部変数 function countUp(){ //1加算する関数 counter++; console.log(counter); // 1 } countUp(); // 実行カウンターを実装する為に
まずcountUpという1加算する関数と、関数内で使用するcounter変数を定義します。この時点でカウンターは完成していますが、このコードには1点問題があります。
それが「counter変数がどこからでも変更できてしまう」という点です。これを解決するために、クロージャの仕組みを利用します。
先ほどのコードを、以下のように変更します。
function countFactory(){ // 追記①:関数と変数を入れ子にする let counter = 0; function countUp(){ counter++; console.log(counter); } return countUp; // 追記②:countUp関数を返す。 }突然ですが、JavaScriptでは、上記のように関数の中に関数を宣言すること、そして関数自体を返すことが可能です。
よって今回は、新しく定義したcountFactory関数に、先ほどの関数と変数を丸ごと入れます。
そして、ただ入れ子にしただけでは外部からcountUp関数を使用できなくなってしまうため、countUp関数を返してあげましょう。
これでクロージャを利用したカウンターの完成です。
counter変数は、先ほどはグローバルコンテキスト上で宣言されていた為、どこからでもアクセス可能となっていましたが、今回は関数内に宣言されている為、関数実行時にのみアクセス可能になった(プライベートな変数になった)ことが分かります。
では、続いて実行をしてみます。
function countFactory(){ let counter = 0; // プライベートなローカル変数 function countUp(){ counter++; console.log(counter); } return countUp; // 関数を返す。 } //以下、追記部分 const myCountUp = countFactory(); // まずはcountFactory関数を実行。 myCountUp(); // 1 myCountUp(); // 2 myCountUp(); // 3まず親関数であるcountFactoryを実行します。
関数が返るので、新しく変数myCountUpという変数を用意し、代入します。myCountUpは関数になりますので、()を付けることで実行できます。
結果として、実行した分だけ、1.2.3...とカウントアップされていきます。ポイントとなるのは、counterローカル変数がmyCountUp関数が実行された際にのみ加算されるようになったという点です。
そして本来、関数内のローカル変数は処理が終了すれば破棄されますが、
内側の関数でcounterローカル変数を参照している(つまりクロージャが作られている)為、内側の関数内でcounter変数の参照を維持することができています。また以下のコードを見てみましょう。
function countFactory(){ let counter = 0; function countUp(){ counter++; console.log(counter); } return countUp; } //親関数を2回実行 const myCountUpA = countFactory(); const myCountUpB = countFactory();親関数を2回実行し、AとBの2つの変数にcountUp関数を返します。
AとB、それぞれ何度か実行して結果を見てみましょう。
myCountUpA(); // 1 myCountUpA(); // 2 myCountUpB(); // 1 myCountUpA(); // 3 myCountUpB(); // 2上記のように実行すると、AとBはそれぞれ独立してカウントアップしています。
この結果から、counter変数はAとBの関数内に、それぞれ独立して保持されていることが分かります。
イメージとしては左手にAというカウンター、右手にBというカウンターをそれぞれ持っていて、必要なタイミングでカウントしている感じです。(そのままですが、、、笑)
AとBのカウンターは機能は同じですが別物なので、カウントは別々に行われます。こんなイメージです。長くなりましたが、クロージャを使うことでプライベート変数を保持した関数を作ることができました。
続いて、動的な関数を生成する関数を見ていきましょう。
②動的な関数を生成する関数
ここでは、倍数を表示してくれる関数を生成してみたいと思います。
function multipleFactory(init){ // 関数を生成する親関数 return function(val){ // returnで無名関数を返す console.log(init * val); } } const multiple3 = multipleFactory(3); // 3の倍数を表示する関数 const multiple5 = multipleFactory(5); // 5の倍数を表示する関数 const multiple10 = multipleFactory(10); // 10の倍数を表示する関数 multiple3(4); // 12 multiple5(5); // 25 multiple10(5); // 50基本的な部分は先ほどの①の例と変わりません。
違う点は親関数と子関数にそれぞれ引数を持たせたことと、親関数を複数実行している点です。
関数を生成する関数と言われると難しく感じるかもしれませんが、行っているのは親関数を複数回呼び出しているだけです。
親関数を複数実行すると、その度に関数が返ってくる為、新しく関数が作られることになります。
しかし、ただ新しい関数を作っただけでは実行結果は全て同じになります。
なので、異なる値を引数として与えることで、例えば、3の倍数を返す関数、5の倍数を返す関数、と言った具合に、動的に異なる値を返す関数を作ることができるわけです。
このように、クロージャと引数を利用して、動的な関数を生成する関数を作ることができます。
まとめ
ここまでお読みいただきありがとうございます。
クロージャに関しては色々な言葉で説明がされているので混乱しましたが、活用例と重要ポイントに関してはおおよそ理解できたと思います。
至らない点がありましたらご指摘いただけると幸いです。参考
https://developer.mozilla.org/ja/docs/Web/JavaScript/Closures
https://qiita.com/Yametaro/items/7a4521e23520947cc43e
https://qiita.com/manten120/items/bea6686e6021c6254596
https://meetup-jp.toast.com/923
- 投稿日:2020-10-13T15:21:27+09:00
ReactでAPIを呼び出したところ、CORSエラーが発生。その対処方法
ReactでpixabayのAPIを使って画像検索アプリケーションを作成した際にCORSエラーが発生
エラー内容
Access to XMLHttpRequest at 'https://pixabay.com/api/?q=animal' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.エラーの発生経緯
const App =()=>{ const [images, setImages]= useState([]); const ApiKey = process.env.REACT_APP_PIXABAY_APIKEY; const onSearchSubmit =async(term)=>{ try{ const params={ key: ApiKey, q: term, }; const response= await axios.get('https://pixabay.com/api/', { params }); setImages(response.data.hits);上記のようにAPIキーを環境変数に記述し、App.jsに引き渡す処理を実装した際に発生。
色々と検索してみた結果以下の記事を発見、参考にしてみました。なるほど。セキュリティ上の問題か〜賢い!そしておもしろい!
参考にしていじってみよう。そして10年後・・・・(冗談)
サーバーを再起動したら解決
いろいろと試行錯誤した結果、「まさか!」と思い、
control+cして、yarn startしたところ、普通に解決しました。
なぜだ・・・。一日中サーバー起動しっぱなしで作業してたからですかね?
詳しい方、教えていただけますと幸いです。。。再起動したら改善されることは、よくあることなので覚えておこう。
- 投稿日:2020-10-13T14:07:15+09:00
初投稿!!Mathオブジェクトまとめ
- 投稿日:2020-10-13T12:47:12+09:00
ネイティブjsで動的にHTML要素にonclick追加
WHAT
静的なウェブページ(HTMLページ)のliタグをぜーんぶ自動で回収してきて、そいつら全員にonclickタグをつけてあげる。
WHY
「phpとかRubyとかいちいちやってらんないヨー」
「サクッとjsで実装できねえのかな」
そんな画面でサクッと動的にHTMLタグにクリックイベントを追加できたらめっちゃ楽じゃん。
<a href="#" onclick="へのへのもへじ"></a>
こんな奴を何回もコピペするより
<a href="#"></a>
これの方が短くていいよね!しかも動的だから書き換えも楽!何が一番いいって、wordpressサイトとかのよくわからんループを読み解かなくていい。
HTMLさえ読み取れれば動的処理を追加できる。
バックエンド開発苦手な人のための「サクッとjs処理」HOW
基本的な考え方は以下の通りだよ。
1. idを指定して、その中のliタグ集合させる。
2. liタグにonclick要素をjsで動的に追加。
以上、楽勝!index.html<div class="wrapper"> <ul id="parentNode"> <li>Num-01</li> <li>Num-02</li> <li>Num-03</li> <li>Num-04</li> <li>Num-05</li> </ul> </div>main.js// 親要素をidで取得 var target01 = document.getElementById('parentNode'); // liっていうタグを全部自動で集めるよ var target02 = target01.getElementsByTagName('li'); // 集めたliタグに対して一括で処理するよ for (let i = 0; i < target02.length; i++) { var target03 = target02[i]; console.log(target03); // settAttributeでonclick要素を追加するよ、引数はhello()関数ね target03.setAttribute("onclick", "hello(this)"); } // ハロー関数を設定しておくよ function hello(item) { alert(item.textContent + "だよ"); }
- 投稿日:2020-10-13T10:48:24+09:00
配列同士の要素を比較する方法
配列同士の要素を比較するプログラムを書いてプルリクした際、レビューでもっといい方法を教えて頂いたのでメモです!
具体的には、下記のとおり、Afterの方がより目的(配列同士の要素を比較)に沿った方法になっているということです。
Before → 配列2つを文字列にして一致するかどうかチェック
After → 配列の中身をすべて比較してチェックやりたいこと
「配列A,Bの要素に差分があるかどうか」を知りたい。
配列内の数字はajaxで通信して取得していて、今の所いつでも昇順で入っている。やったこと
Before
index.vue~ 略 ~ methods: { isDiffArrays() { let arrayA = [1, 2, 3, 4]; // 仮で入れています 実際はfunctionの外からもってきました let arrayB = [2, 3, 4, 6]; // 仮で入れています 実際はfunctionの外からもってきました return (arrayA.toString() != arrayB.toString()); } } ~ 略 ~いちおう、中身の比較はしていますが、何らかの原因で昇順以外で格納されてしまうと、
例えば[1, 2, 3, 4]==[2, 3, 4, 1]でfalseが返らないという結果にもなり得ます。After
index.vuemethods: { isDiffArrays() { let arrayA = [1, 2, 3, 4]; // 仮で入れています 実際はfunctionの外からもってきました let arrayB = [2, 3, 4, 6]; // 仮で入れています 実際はfunctionの外からもってきました let numOnlyInA = arrayA.filter(i => arrayB.indexOf(i) == -1); // 上記の方法でarrayBが持たないarrayAの値(ここでは1)をnumOnlyInAに入れることができます。 // 同じ方法でarrayAが持たないarrayBの値(ここでは6)をnumOnlyInBに入れます。 let numOnlyInB = arrayB.filter(i => arrayA.indexOf(i) == -1); return ((numOnlyInA.length != 0) || (numOnlyInB.length != 0)) } }この方法なら、配列の要素ひとつひとつをfilterでチェックしているので、
[1, 2, 3, 4]と[2, 3, 4, 1]を比較したときには【中身の要素は同じ】という結果になります。
- 投稿日:2020-10-13T08:29:35+09:00
Node-REDのobnizノードがバージョンアップした!
Node-REDのobnizノードをバージョンアップしましたので、使い方概要を書いていきたいと思います。
バージョンアップ概要
- obnizとの通信用のjsonデータを書かなくて良くなった!楽!
- obnizにアウトプットしかできなかった(ディスプレイに文字出すとかLEDつけるとか)のが、インプットもできるようになった!
- JavaScriptSDKでできることが全部そのままできるようになった!
- 互換性なくなった!
インストール方法
NodeREDの右上のメニューから、パレットの管理を選びます
「ノードを追加」タブにてobnizと検索します。
node-red-contrib-obnizがv0.6.1で出てきますので、右側の「ノードを追加」ボタンを押します
左側に青いobnizノードが2つ出てくればインストール成功です
使い方
ノードの種類
obnizノードが2つあります。
obniz repeatはセンシングなど、常にデータを取り続けることがしたいとき、
obniz funcはフロー上のきっかけでなにか動作させたいときに使います。どちらのノードもプロパティを開くと、obnizの設定と、コードを書く画面が出てきます。
まずはobnizの設定をしましょう。
obnizの設定
obniz repeatノード、もしくはobniz functionノードのプロパティから、"新規にobnizを追加"を指定して右側の編集ボタンを押すとobnizの設定画面に行くことができます。
ここでobnizの設定を行います。
よく使う項目は3個です
obnizID
使用するobnizデバイスのobnizIDを入れますDevieType
使用するobnizデバイスの種類を選択します初期化処理
obniz.wiredやobniz.ble.initなど、起動時に1度だけ行いたい処理を記載します。obnizIDとDevieTypeはおそらく迷わないと思うので割愛して、初期化処理だけ詳しく説明したいと思います。
初期化処理
functionノードと同じような感覚で、初期化処理を記載します。
ここではobniz
変数とobnizParts
変数が割り当てられていて、そのまま使うことができます。
awaitにも対応しているので、obnizをJavaScriptSDKで使うときと同じように書くことができます。
変数名 説明 obniz new Obniz("OBNIZ_ID")で作られたobnizインスタンスが割り当てられます obnizParts すべてのobniz関連ノードで共通のオブジェクトが割り当てられます。
初期は空オブジェクト{}
です。
obniz.wiredで作られたパーツインスタンスを格納するのにご利用くださいたとえば、M5StickCに外付けLEDをつけた際は、こんなコードを書くことになります。
obnizParts.led = obniz.wired("LED",{anode:0});obniz repeatの設定
obniz Repeatノードの設定は、主に3つです
obniz
上記obnizの設定で作成したobnizを選択しますinterval
繰り返しの間隔時間を指定します(ミリ秒)コード
繰り返しで行う動作を設定しますコードでは、obnizの初期化処理のときと同じく下記変数が割り当てられていて、使うことができます
変数名 説明 msg 空オブジェクト {}
obniz new Obniz("OBNIZ_ID")で作られたobnizインスタンスが割り当てられます obnizParts すべてのobniz関連ノードで共通のオブジェクトが割り当てられます。
初期は空オブジェクト{}
です。
obniz.wiredで作られたパーツインスタンスを格納するのにご利用ください今回の例だと、obnizの初期化処理で
obnizParts.led
を作成していますので、ここではobnizParts.led
を使うことができますnodeのデータ出力方法は2つあり、どちらもfunctionノードと同じ仕様になっています。
return msg
で発火node.send(msg)
で発火基本は
return msg
を使ってもらえればと思いますが、非同期処理などがある場合はnode.send(msg)
でも発火できます。たとえば、m5stickCのボタンAが押されてるかどうかを1秒おきに発火させるにはこのように書きます。
msg.payload = await obniz.buttonA.isPressedWait(); return msg;obniz functionの設定
obniz functionノードの設定は、主に2つです
obniz
上記obnizの設定で作成したobnizを選択しますコード
発火時に行う動作を設定しますコードでは、他と同じく下記変数が割り当てられていて、使うことができます
変数名 説明 msg フローから受け取ったメッセージオブジェクト obniz new Obniz("OBNIZ_ID")で作られたobnizインスタンスが割り当てられます obnizParts すべてのobniz関連ノードで共通のオブジェクトが割り当てられます。
初期は空オブジェクト{}
です。
obniz.wiredで作られたパーツインスタンスを格納するのにご利用ください今回の例だと、obnizの初期化処理で
obnizParts.led
を作成していますので、ここではobnizParts.led
を使うことができますnodeのデータ出力方法は2つあり、どちらもfunctionノードと同じ仕様になっています。
ここらへんはobniz repeatと一緒ですね。
return msg
で発火node.send(msg)
で発火基本は
return msg
を使ってもらえればと思いますが、非同期処理などがある場合はnode.send(msg)
でも発火できます。たとえば、msg.payloadの値に応じてLEDを光らせたり、ディスプレイに文字を表示する場合はこのように書きます。
if(msg.payload > 100){ obnizParts.led.on(); }else{ obnizParts.led.off(); } obniz.display.clear(); obniz.display.print(msg.payload);また、obniz functionノードでデータのセンシングを行えば、発火されたときだけデータを取得しに行くということができるようになります
msg.payload = await obniz.buttonA.isPressedWait(); return msg;まとめ
obnizノードが進化したので、だいぶ使いやすくなったと思います。
バグ連絡は twitter:@9wick まで!pull-reqもお待ちしてます!
https://github.com/obniz/obniz-nodered-sdk
- 投稿日:2020-10-13T07:46:41+09:00
javascriptで複利計算プログラム
<script type="text/javascript"> function Calculator() { var rate, amount, period; rate = parseInt(document.myform.rate.value, 10); amount = parseInt(document.myform.amount.value, 10); period = parseInt(document.myform.period.value, 10); document.myform.receipt.value = fukuri(rate, amount, period); } function fukuri(rate, amount, period) { var receipt; receipt = amount; var str = ""; str += "<table class=’test’>"; str += "<tr><th>預入期間</th><th>受取金額</th></tr>"; //預入金額=元金×(1+金利)/100 //Math.round:小数点以下を四捨五入 for (var i = 1; i <= period; i++) { receipt = Math.round(receipt * (1 + rate / 100)); str += "<tr><td>" + i + "年</td><td>" + receipt + "円</td></tr>"; } str += "</table>"; document.getElementById("result").innerHTML = str; return receipt; } function TableClear() { document.getElementById("result").innerHTML = ""; } </script> <body> <h1>複利計算</h1> <p>金利・金額・預入期間(年)を入力してください。</p> <form name="myform"> <table> <tr> <th>金利</th> <td><input type="text" name="rate" size="22" value="4" />%</td> </tr> <tr> <th>金額</th> <td><input type="text" name="amount" size="22" value="10000" />円</td> </tr> <tr> <th>預入期間</th> <td><input type="text" name="period" size="22" value="10" />年</td> </tr> <tr> <td colspan="2" class="btn"> <input type="button" onclick="Calculator()" value="計算" /> <input type="reset" onclick="TableClear()" value="クリア" /> </td> </tr> <tr> <th>受取金額</th> <td><input type="text" name="receipt" size="22" />円</td> </tr> </table> </form> <hr /> <div id="result"></div> </body>
- 投稿日:2020-10-13T02:37:27+09:00
CSS BUTTONS
Creative Button Hover
See the Pen Creative Button Hover by G Rohit (@grohit) on CodePen.
Button hover effect
See the Pen Button hover effect by Comehope (@comehope) on CodePen.
Creative Button Animation Effects | Only Using HTML & CSS
See the Pen Creative Button Animation Effects | Only Using HTML & CSS by Ahmad Emran (@ahmadbassamemran) on CodePen.
Add button hover animation
See the Pen Add button hover animation by Aaron Iker (@aaroniker) on CodePen.
Button hover effect
See the Pen Button hover effect by Comehope (@comehope) on CodePen.
More Related Article
50+ CSS BUTTONS
30+ Radio Button CSS style
9 Bootstrap Date picker
- 投稿日:2020-10-13T02:14:47+09:00
初心者のプログラミング
勉強の記録
勉強した内容
- letの使い方。
- ツイッターなどでよくある診断ツールの作成。
何をベースに勉強してるか
内容の詳細
わかったことについて
- 影響される範囲を絞れるので、別の場所で同じ引数を使ってエラーが出にくくなるってスゲーって思いました。 ただ、まだどういう状況でそれが役立つのかよくわかりません。
- 文字にはcharaCodeがあり、それによって同名なら同じ結果がでるようにできるって知りました。
- for(let i = 0; i < userName.length; i++){ のようにプロパティ全体を式に組み込めるのが面白い!
むずかしかったよ
- 正直全体的に難しい…。 なるほどねって思える部分があるだけマシなのかなって思えるけど。 もう一度同じ内容をやらないと同じことを出来る気がしない。
次回やる予定のこと
- というわけもう一度同じところはやるつもりです。 次回からはテキストを先に読み、予習してから取り掛かろうと思います。
今回書いたコード
'use strict'; const answers = [ '{userName}のいいところは声です。{userName}の特徴的な声は皆を惹きつけ、心に残ります。', '{userName}のいいところはまなざしです。{userName}に見つめられた人は、気になって仕方がないでしょう。', '{userName}のいいところは情熱です。{userName}の情熱に周りの人は感化されます。', '{userName}のいいところは厳しさです。{userName}の厳しさがものごとをいつも成功に導きます。', '{userName}のいいところは知識です。博識な{userName}を多くの人が頼りにしています。', '{userName}のいいところはユニークさです。{userName}だけのその特徴が皆を楽しくさせます。', '{userName}のいいところは用心深さです。{userName}の洞察に、多くの人が助けられます。', '{userName}のいいところは見た目です。内側から溢れ出る{userName}の良さに皆が気を惹かれます。', '{userName}のいいところは決断力です。{userName}がする決断にいつも助けられる人がいます。', '{userName}のいいところは思いやりです。{userName}に気をかけてもらった多くの人が感謝しています。', '{userName}のいいところは感受性です。{userName}が感じたことに皆が共感し、わかりあうことができます。', '{userName}のいいところは節度です。強引すぎない{userName}の考えに皆が感謝しています。', '{userName}のいいところは好奇心です。新しいことに向かっていく{userName}の心構えが多くの人に魅力的に映ります。', '{userName}のいいところは気配りです。{userName}の配慮が多くの人を救っています。', '{userName}のいいところはその全てです。ありのままの{userName}自身がいいところなのです。', '{userName}のいいところは自制心です。やばいと思ったときにしっかりと衝動を抑えられる{userName}が皆から評価されています。', ]; /** * 名前の文字列を渡すと診断結果を返す関数 * @param {string} userName ユーザーの名前 * @return {string} 診断結果 */ function assessment(userName){ //userName(文字列) を数値に変換 //全ての文字を足し算する var userNameNumber = 0; for(let i = 0; i < userName.length; i++){ userNameNumber += userName.charCodeAt(i); } //5桁の数値を回答結果の範囲(0~15)に返還 var answerNumber = userNameNumber % answers.length; //診断結果 var result = answers[answerNumber]; return result.replace(/\{userName\}/g, userName); } console.log(assessment('太郎')); console.log(assessment('次郎')); console.log(assessment('太郎'));
- 投稿日:2020-10-13T00:26:27+09:00
LINEボットでゲームブック、回想シーンを追加
前回の投稿( LINEボットでゲームブックを作った、ついでにシナリオエディタ作ったので完成 )の続きです。
LINEボットでゲームブックを作りましたが、かのTyranoScriptを参考に、回想シーンを付けてみました。
気に入った画像があるページで、「記憶」 と言うと、その時の画像や音声を覚えておいてくれるので、いつでも見返せるようになります。
今回も、GitHubに上げています。poruruba/LinebotGamebook
https://github.com/poruruba/LinebotGamebook回想シーンを思い出すために、LIFFアプリを追加します。
こんな感じの画面が、LINEアプリ内に表示されます。LIFFアプリの登録
LIFFアプリは、スマホのLINEアプリの中で起動できるWebページです。
ユーザにログインを気にさせずにWebページを表示させられるのがよいです。LINE Developersより、LINEログインチャネルを作成し、LIFFタブを選択して、LIFFアプリの「追加」ボタンを押下すれば登録できます。
LIFFアプリ名には適当な名前を入力し、エンドポイントURLには、立ち上げたサーバのURLを以下のようにして入力します。
https://【サーバのURL】/gamebook/liff/index.html
そうすると、LIFF IDが払い出されます。これを覚えておきます。ついでに、このチャネルのチャネルIDも覚えておきます。
※ちなみに、以前はMessaging APIでLIFFが登録できていたのですが、最近はだめになったようです。(その影響で、liff.sendMessages()が呼び出せなくなっているような。。。)
あとは、上記のURLに表示させたいWebページを作ればよいです。
このページにユーザに飛んでもらうためには、チャットで「"https://liff.line.me/" + LIFF_ID」という感じのURLをクリックしてもらえばよいです。LIFFアプリとサーバの連携
LIFFアプリは通常のSPAのWebページです。
以下のjavascriptライブラリを取り込みます。public/gamebook/liff/index.html<script charset="utf-8" src="https://static.line-scdn.net/liff/edge/2/sdk.js"></script>LIFFアプリのJavascriptでは、Webページが起動した直後に、以下を呼び出します。
public/gamebook/liff/js/start.jsawait liff.init({ liffId: LIFF_ID }); this.id_token = liff.getIDToken();LIFF_IDは先ほど取得したものです。
そして、このid_tokenを立ち上げたサーバに渡します。public/gamebook/liff/js/start.jsvar param = { id_token: this.id_token, cmd: 'get' }; var json = await do_post(status_url, param );(参考) liff.init()、liff.getIDToken()
https://developers.line.biz/ja/reference/liff/#initialize-liff-app
https://developers.line.biz/ja/reference/liff/#get-id-tokenサーバ側では、IDトークンを検証してLINEユーザIDを判別して、シナリオの状態を取り出し、jsonとして戻してくれるようにサーバ側を実装しました。
サーバ側の処理
サーバ側ではIDトークンをLINEサーバに渡して正しさを確認すると、ユーザの情報が取得できます。ブラウザから取得したIDトークンと、先ほどメモっておいたチャネルIDを使います。
api/controllers/linebot/index.jsvar json = await do_post_urlencoded('https://api.line.me/oauth2/v2.1/verify', { id_token: body.id_token, client_id: LINE_CHANNEL_ID } ); var userId = json.sub;(参考) LIFFアプリおよびサーバーでユーザー情報を使用する
https://developers.line.biz/ja/docs/liff/using-user-profile/userIdがわかったので、DBまたはファイルから状態を取得し、以降の処理でクライアントに返してあげています。
あとは、ソースコードを見ていただければ!!
補足
LIFFアプリは、ChromeではなくLINEアプリの中で起動するのでJavascriptのデバッグがつらいです。
その場合には、以下の部分のコメントアウトを解除してください。consoleが見れます。public/gamebook/liff/js/start.js//var vConsole = new VConsole();
Tencent/vConsole
https://github.com/Tencent/vConsole以上