20201013のJavaScriptに関する記事は18件です。

[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の値を指定する

nullthisWindowになる

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);
    });

一度だけコールバック関数が実行され、正常にhandlerboxから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);
    });

今回は引数messagehandlerへ渡し、
なおかつ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開発においてスコープと引数と解除可能をどうやって成立させているのやら)

ご覧いただきありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ドットインストール:JavaScript:詳解JavaScript 基礎文法編

参考

わかりやすいのでおすすめ
詳解JavaScript 基礎文法編

02 ~ 03 別ファイルから実行等

image.png

04 ~ 08 文字列/数値/定数/変数

image

09 ~ 12 データ型/数字からなる文字列等

image

13 ~ 15 条件分岐/条件演算子/論理演算子

image

16 switchで条件分岐

image

17 ~ 19 for/while/continue、break

image

20 ~ 21 関数/引数

image

22 ~ 23 returnで値を返す/関数式

image

24 アロー関数

image

25 スコープ

image

26 ブロック

image
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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~

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VueでURLからハッシュ("#")を削除し、リロードもできるようにする

概要

Vueアプリはデフォルトでrouterのパスに「ハッシュ(#)」が含まれています。これはrouterを「historyモード」に変更することで削除することができますが、historyモードを使用することでページをリロードした際に、404エラーが返却されてしまいます(historyモードでない場合にはこの問題は発生しません)。

しかしながら一点問題があります。シングルページのクライアントサイドアプリケーションなので、適切なサーバーの設定をしないと、ユーザーがブラウザで直接 http://oursite.com/user/id にアクセスした場合に 404 エラーが発生します。- HTML5 History モード

しかし、適切な設定をすれば「ハッシュを削除しつつ、リロードにも対応する」ことが可能となります。

Expressをインストールする

$ npm install express --save

connect-history-api-fallbackをインストールする

npm install --save connect-history-api-fallback

参考:connect-history-api-fallback

ルートディレクトリにserver.jsを作成する

server.js
var 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.js

http://localhost:5000でアプリにアクセスすると、ページをリロードしても404が返却されない、かつハッシュ記号もurlから削除されているはずです。

参考:
Vue.jsで作ったアプリをHerokuにデプロイ
vue-routerのルーティングURLからハッシュを除去しつつ、URL直接指定でも表示させる(Node, Express)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初心者でもわかる】同じ属性値(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のちょいテク詰め合わせ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

paralleldots APIをnode経由で使用してみた [感情分析編]

はじめに

paralleldots AI APIというテキストから感情を分析するAPIがあるので、Nodeをプロキシとして利用し、このAPIを使ってみました。

paralleldots

環境

・ node version : v12.18.3
・ npm version : 6.14.6

URIと機能

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.js
const 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より見やすい為)

スクリーンショット 2020-10-13 19.30.55.png

・ 実際のResponse

{
    "emotion": {
        "Happy": 0.0872024649,
        "Angry": 0.2344884125,
        "Bored": 0.0416403769,
        "Fear": 0.3095755387,
        "Sad": 0.1825278824,
        "Excited": 0.1445653247
    }
}

結果

chart.png

やはり、死ぬかもしれないという恐怖を入れ込んだ文章を送ったため、Fearが一番結果の数値として高いことが分かります。
精度的にどうなのかは、個人の感性に依存しそうですが、、

参照

paralleldotsのドキュメント

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

paralleldots APIをnode経由で使用してみた (感情分析編)

はじめに

paralleldots AI APIというテキストから感情を分析するAPIがあるので、Nodeをプロキシとして利用し、このAPIを使ってみました。

paralleldots

環境

・ node version : v12.18.3
・ npm version : 6.14.6

URIと機能

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.js
const 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より見やすい為)

スクリーンショット 2020-10-13 19.30.55.png

・ 実際のResponse

{
    "emotion": {
        "Happy": 0.0872024649,
        "Angry": 0.2344884125,
        "Bored": 0.0416403769,
        "Fear": 0.3095755387,
        "Sad": 0.1825278824,
        "Excited": 0.1445653247
    }
}

結果

chart.png

やはり、死ぬかもしれないという恐怖を入れ込んだ文章を送ったため、Fearが一番結果の数値として高いことが分かります。
精度的にどうなのかは、個人の感性に依存しそうですが、、

参照

paralleldotsのドキュメント

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript 困ったところまとめ【更新版】

async/await 使い方

asyncとは、

・非同期通信を行う時に宣言する。

index.js
const number1 = () => {
  setTimeout(()=>{
    console.log("number1")
  }, 1000)
 }

 const number2 = () => {
  console.log("number2")
 }

 const number3 = () => {
  console.log("number3")
 }
 number1()
 number2()
 number3()

//=>number2
//=>number3
//=>number1
index.js
const 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)
}

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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に引き渡す処理を実装した際に発生。
色々と検索してみた結果以下の記事を発見、参考にしてみました。

なんとなく CORS がわかる...はもう終わりにする。

なるほど。セキュリティ上の問題か〜賢い!そしておもしろい!
参考にしていじってみよう。

そして10年後・・・・(冗談)

サーバーを再起動したら解決

いろいろと試行錯誤した結果、「まさか!」と思い、
control+cして、yarn startしたところ、普通に解決しました。
なぜだ・・・。

一日中サーバー起動しっぱなしで作業してたからですかね?
詳しい方、教えていただけますと幸いです。。。

再起動したら改善されることは、よくあることなので覚えておこう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初投稿!!Mathオブジェクトまとめ

JavaScriptで数当てゲームを作る中で使用したMathオブジェクトについてまとめてみました。
(*あくまでも使用したメソッドのみです。その他のMathメソッドについてはまた学習次第追記します。)
スクリーンショット 2020-10-13 14.04.37.png

スクリーンショット 2020-10-13 14.05.33.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ネイティブ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 + "だよ");
}

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

配列同士の要素を比較する方法

配列同士の要素を比較するプログラムを書いてプルリクした際、レビューでもっといい方法を教えて頂いたのでメモです!

具体的には、下記のとおり、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.vue
methods: {
    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]を比較したときには【中身の要素は同じ】という結果になります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node-REDのobnizノードがバージョンアップした!

Node-REDのobnizノードをバージョンアップしましたので、使い方概要を書いていきたいと思います。

バージョンアップ概要

  • obnizとの通信用のjsonデータを書かなくて良くなった!楽!
  • obnizにアウトプットしかできなかった(ディスプレイに文字出すとかLEDつけるとか)のが、インプットもできるようになった!
  • JavaScriptSDKでできることが全部そのままできるようになった!
  • 互換性なくなった!

インストール方法

NodeREDの右上のメニューから、パレットの管理を選びます
スクリーンショット 2020-10-13 11.25.22.png

「ノードを追加」タブにてobnizと検索します。

node-red-contrib-obnizがv0.6.1で出てきますので、右側の「ノードを追加」ボタンを押します

スクリーンショット 2020-10-13 11.25.31.png

左側に青いobnizノードが2つ出てくればインストール成功です
スクリーンショット 2020-10-13 11.26.47.png

使い方

ノードの種類

obnizノードが2つあります。

obniz-repeat.png

obniz-function.png

obniz repeatはセンシングなど、常にデータを取り続けることがしたいとき、
obniz funcはフロー上のきっかけでなにか動作させたいときに使います。

どちらのノードもプロパティを開くと、obnizの設定と、コードを書く画面が出てきます。
obniz-func-property.png

まずはobnizの設定をしましょう。

obnizの設定

obniz repeatノード、もしくはobniz functionノードのプロパティから、"新規にobnizを追加"を指定して右側の編集ボタンを押すとobnizの設定画面に行くことができます。

obniz-config.png

ここで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});

sample-config.png

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;

スクリーンショット 2020-10-12 12.58.37.png

スクリーンショット 2020-10-12 12.57.52.png

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);

スクリーンショット 2020-10-12 13.03.06.png

スクリーンショット 2020-10-12 13.05.51.png

また、obniz functionノードでデータのセンシングを行えば、発火されたときだけデータを取得しに行くということができるようになります

msg.payload = await obniz.buttonA.isPressedWait();
return msg;

スクリーンショット 2020-10-12 13.05.15.png

スクリーンショット 2020-10-12 12.57.49.png

まとめ

obnizノードが進化したので、だいぶ使いやすくなったと思います。
バグ連絡は twitter:@9wick まで!

pull-reqもお待ちしてます!
https://github.com/obniz/obniz-nodered-sdk

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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>

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CSS BUTTONS

Untitled.png

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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初心者のプログラミング

勉強の記録

勉強した内容

  • 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('太郎'));
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LINEボットでゲームブック、回想シーンを追加

前回の投稿( LINEボットでゲームブックを作った、ついでにシナリオエディタ作ったので完成 )の続きです。

LINEボットでゲームブックを作りましたが、かのTyranoScriptを参考に、回想シーンを付けてみました。
気に入った画像があるページで、「記憶」 と言うと、その時の画像や音声を覚えておいてくれるので、いつでも見返せるようになります。
今回も、GitHubに上げています。

poruruba/LinebotGamebook
 https://github.com/poruruba/LinebotGamebook

回想シーンを思い出すために、LIFFアプリを追加します。
こんな感じの画面が、LINEアプリ内に表示されます。

image.png

image.png

LIFFアプリの登録

LIFFアプリは、スマホのLINEアプリの中で起動できるWebページです。
ユーザにログインを気にさせずにWebページを表示させられるのがよいです。

LINE Developersより、LINEログインチャネルを作成し、LIFFタブを選択して、LIFFアプリの「追加」ボタンを押下すれば登録できます。

image.png

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.js
                await liff.init({
                    liffId: LIFF_ID
                });
                this.id_token = liff.getIDToken();

LIFF_IDは先ほど取得したものです。
そして、このid_tokenを立ち上げたサーバに渡します。

public/gamebook/liff/js/start.js
                var 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.js
      var 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

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む