20200316のJavaScriptに関する記事は30件です。

関数と引数と戻り値

関係性について

翻訳機、消費税に例えると下記の写真の通りになります↓↓

スクリーンショット 2020-03-16 23.05.51.png

翻訳してください。

と言って「翻訳」と定義した箱に野球を入れて渡すと、baseballとなります。

消費税かけて下さい。

と言って「消費税」と定義した箱に 1万円を入れて渡すと
1万1000円となります。

一度関数名を定義すれば、
箱に何かを入れて「翻訳!」とか「消費税!」と言って呼び出すだけで、
それぞれの処理を実行してくれます。

記述方法

function 関数名(引数) { 

処理内容 
return 
 } 

関数を使う 

上記のようになり、
return文を使用すると、
処理を終了し、処理結果を返すことが出来ます。
(戻り値のことです。)

消費税を例として、見ていきましょう↓↓

function tax(price) { 

var tax = price * 1.10; 
return tax; 
 } 

 console.log(tax(1000)); 

上記の記述をすると下記にようになります↓↓
スクリーンショット 2020-03-16 23.41.53.png

消費税など、
これからも変更されるであろうものは、
関数として、定義しましょう。

本日は以上になります。

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

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

Javascript命名規則

write_index → スネークケース(DBのカラム名)
getCookie → キャメルケース

JavaScriptは基本的に
関数、メソッド、変数命名→Lower Camel(小文字で初めて途中単語の頭文字を大文字に)
クラス名→Upper Camel(大文字で始めて途中単語の頭を大文字に)

キャメルケースとは

キャメルケースは「単語の先頭を大文字にしてつなげる表記方法」です。
変数名の付け方の話で、よく出てきます。

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

Glide.jsでスライドショーを作る

Glide.jsとは

https://glidejs.com/

無料で使用できるオープンソースプロジェクトです。
他のライブラリに依存することなく、軽量なことが特徴です。

インストール

npm

npm install @glidejs/glide

GitHub

https://github.com/glidejs/glide/releases/tag/v3.4.1

読み込み

スクリーンショット (23).png
以下の二つをHTMLで読み込みます。

<link rel="stylesheet" href="./@glidejs/glide/dist/css/glide.core.min.css">
<script src="./@glidejs/glide/dist/glide.min.js"></script>

スライドショーを配置する位置へ以下を記入します。

<div class="glide">
  <div class="glide__track" data-glide-el="track">
    <ul class="glide__slides">
      <li class="glide__slide">画像1</li>
      <li class="glide__slide">画像2</li>
      <li class="glide__slide">画像3</li>
    </ul>
  </div>
</div>

以下を記入し、初期化します。

<script>
    new Glide('.glide', {
      autoplay: 2000
    }).mount()
</script>

autoplayは指定の時間で自動変更してくれます。上の例では2秒で移ります。

サンプル

ezgif.com-video-to-gif.gif


Glide.jsのサイトのDOCSからその他様々な仕様を見ることができます:slight_smile:

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

【MDN】非同期JavaScriptの紹介

MDNに非同期 JavaScriptという学習項目があるのですが、目次以外は日本語がありません。
ということで以下はIntroducing asynchronous JavaScript項目の日本語訳です。

Introducing asynchronous JavaScript

この記事では、同期JavaScriptにまつわる問題点を簡潔に要約します。
そして非同期JavaScriptのテクニックの幾つかを紹介し、それぞれが問題点の解決にどのように役立つかを示します。

前提条件:基礎的なコンピュータリテラシー、JavaScriptの基礎をある程度理解していること。
目的:非同期JavaScriptとは何か、同期JavaScriptとは何が違うのか、どのようなユースケースが存在するか、ということを理解する。

同期JavaScript

非同期JavaScriptが何であるのかを理解するためには、まず同期JavaScriptが何であるかを理解することから始めなければなりません。
このセクションでは、前回の記事で現れた情報の一部を要約します。

これまでに学習してきた多くの機能は、そのほとんどが同期です。
幾つかのコードを実行してみると、ブラウザはそれを実行すると同時に結果を返します。
簡単な例を見てみましょう。
実際の動作はこちらで、ソースはこちらで見ることができます。

const btn = document.querySelector('button');
btn.addEventListener('click', () => {
  alert('You clicked me!');

  let pElem = document.createElement('p');
  pElem.textContent = 'This is a newly-added paragraph.';
  document.body.appendChild(pElem);
});

このブロックは、処理が上から順番に実行されます。

  1. 利用可能な要素をDOMから探し出す。
  2. クリックしたときに発火するclickイベントリスナーを設定する。
    1. alert()でメッセージボックスを出す。
    2. アラートが消えたら<p>要素を生成する。
    3. そこにテキストを登録する。
    4. 最後にドキュメントボディに要素を追加する。

それぞれの処理が行われている間、他には何も起こりません。
レンダリングは一時停止されます。
これは、前回の記事で述べたように、JavaScriptがシングルスレッドであるためです。
ひとつのスレッドで一度に発生させることができるイベントはひとつだけであり、他のすべてはイベントが終了するまでブロックされます。

従って上記の例では、ボタンをクリックすると、その後アラートでOKボタンを押すまで、次の段落は表示されません。
実際に以下で試してみることができます。
https://codepen.io/pen/?&editable=true
https://jsfiddle.net/api/mdn/

注意:alert()は同期によるブロッキングを示すためのデモンストレーションとして使うのには最適ですが、実アプリで使用するととても残念なことになるので注意が必要です。

非同期JavaScript

前述のブロッキングといった問題を回避するため、多くのWeb APIは非同期コードを使って実行されるようになりました。
特に何らかの外部デバイスにアクセスしたりフェッチしたりするもの、たとえばネットワーク経由でファイルを取得する、データベースにアクセスしてデータを返す、Webカメラからビデオストリームにアクセスする、VRヘッドセットに出力をブロードキャストする、といったものです。

同期JavaScriptでこれらを実現するのが難しいのは何故でしょうか。
以下に簡単な例を見てみましょう。
サーバから画像を取得する場合、即座に結果を得ることはできません。
すなわち、以下の擬似コードはおそらく動作しないだろう、ということです。

var response = fetch('myImage.png');
var blob = response.blob();
// 画像を何処かのUIに表示する

理由は、画像のダウンロードにかかる時間がわからないためです。
時間がかかった場合、2行目に辿り着いた時点でresponseはまだ使用できません。
従って、時々あるいは毎回、2行目でエラーが発生することでしょう。
これを回避するため、responseを使用するときはresponseが使用可能になるまで待機しておく必要があります。

JavaScriptで扱える非同期コードのスタイルは主に2種類が存在します。
古いスタイルのコールバック形式と、Promiseを使った新しい形式のコードです。
以下のセクションでは、それぞれを順に解説します。

非同期コールバック

非同期コールバックは、バックグラウンドで動作するコードを呼び出すときにパラメータとして指定する関数です。
バックグラウンドコードは、実行が終了するとコールバック関数を呼び出して、自分が終了したことを知らせます。
あるいは特に注意すべきことが発生した際に通知します。

非同期コールバックは少々時代遅れになりつつありますが、いま一般に使われている歴史の長いAPIではまだまだ使用されています。
以下は非同期コールバック関数をaddEventListener()の第二引数に与える例です。

btn.addEventListener('click', () => {
  alert('You clicked me!');

  let pElem = document.createElement('p');
  pElem.textContent = 'This is a newly-added paragraph.';
  document.body.appendChild(pElem);
});

第一引数には監視するイベントのタイプを指定し、第二引数がイベントが発生したときに呼び出されるコールバック関数です。
コールバック関数をパラメータとして別の関数に渡す際、別の関数を実行した時点でコールバック関数まで実行されることはありません。
別の関数内部の何処かで非同期的にコールバックされます(これが名前の由来です)。
別の関数は、対象のイベントが発生したときにコールバック関数を実行します。

コールバック関数を含む関数は独自に実装することが簡単にできます。
ここではXMLHttpRequest APIを使った例を見てみましょう。
実際の動作はこちらで、ソースはこちらで見ることができます。

function loadAsset(url, type, callback) {
  let xhr = new XMLHttpRequest();
  xhr.open('GET', url);
  xhr.responseType = type;

  xhr.onload = function() {
    callback(xhr.response);
  };

  xhr.send();
}

function displayImage(blob) {
  let objectURL = URL.createObjectURL(blob);

  let image = document.createElement('img');
  image.src = objectURL;
  document.body.appendChild(image);
}

loadAsset('coffee.jpg', 'blob', displayImage);

引数のobject URLを表示するimgタグを作ってドキュメントのbodyに追加する関数displayImage()を作りました。
次いで、URLとコンテンツタイプ、そしてコールバックを引数として受け取るloadAsset()関数を作ります。
この関数は、XMLHttpRequest(よくXHRと略される)を使って指定されたURLのリソースをフェッチし、返ってきたレスポンスをコールバック関数に渡します。
loadAsset()関数にコールバックとして渡されたdisplayImage()関数はすぐに動作するのではなく、XHRによるリソースのダウンロードが完了するまで待機します。
待機はonloadイベントハンドラで実現されています。

コールバックは汎用性があります。
関数の実行順序や関数間で渡すデータを制御できるだけではなく、状況に応じて異なる関数を呼び出すこともできます。
レスポンスの内容によってprocessJSON()関数を実行したり、displayText()関数を実行したりといった様々なアクションを持たせることができます。

全てのコールバックが非同期であるわけではないことに注意してください。
以下はArray.prototype.forEach()を用いて配列項目をループする例です。
実際の動作はこちらで、ソースはこちらで見ることができます。

const gods = ['Apollo', 'Artemis', 'Ares', 'Zeus'];

gods.forEach(function (eachName, index){
  console.log(index + '. ' + eachName);
});

この例は、ギリシャの神々の配列をループし、インデックスと値をコンソールに出力するものです。
foreach()が期待するパラメータは配列インデックスと値の2値を引数として持つコールバック関数です。
ただし、このコールバック関数は一切待機せずに即座に実行されます。

Promises

Promiseは、モダンなWebAPIで使用される、新しいスタイルの非同期コードです。
よくある例はfetch() APIで、これは要するにXMLHttpRequestの現代版スタイルのようなものです。
サーバからのデータ取得から簡単な例を見てみましょう。

fetch('products.json').then(function(response) {
  return response.json();
}).then(function(json) {
  products = json;
  initialize();
}).catch(function(err) {
  console.log('Fetch problem: ' + err.message);
});

実際の動作はこちらで、ソースはこちらで見ることができます。

fetch()は取得したいリソースのURLひとつだけを引数に取り、そしてPromiseオブジェクトを返します。
Promiseは最終的に非同期操作が完了したかもしくは失敗したかのステータスを持つオブジェクトで、それまではどちらでもない中間状態になっています。
『わかり次第すぐに結果を返すことを約束する』という概念であるため、"promise"という名前が付いています。

この概念に慣れるのは少し練習が必要です。
これはシュレディンガーの猫に似ていると言えるかもしれません。
成功失敗どちらの結果もまだ発生していない状態では、fetch()はまだ実行を完了せず、次の操作を保留します。
結果がわかったときに、fetch()の次にある3つのコードブロックが呼び出されます。

・ふたつあるthen()ブロックは、いずれもその前の操作が成功した場合に呼び出されるコールバック関数です。
各コールバックは、その前の操作が成功した場合に返した結果を入力として受け取ります。
then()ブロックはそれぞれ別のpromiseを返すため、then()ブロックをいくつも連ねて、複数の非同期操作を順番に実行することができます。

catch()ブロックは、どこかのthen()ブロックが失敗したときに実行されます。
同期のtry...catchブロックと似たようなもので、catch()ブロックはエラーオブジェクトを受け取ります。
これは発生したエラーの種類を判断するために使用できます。
後ほど解説しますが、同期try...catchpromise内で使用することはできません。
async/awaitを使っている場合は使用可能です。

注:promiseについては後から詳しく解説するので、まだ完全に理解できてなくても大丈夫です。

The event queue

Promiseなどの非同期操作はイベントキューに入れられ、メインスレッドの処理が完了した後で実行されるため、後続のJavaScriptコードの実行をブロックしません。
キューに入れられた処理は、なるべく早めに処理が行われ、結果がJavaScriptに返されます。

Promises versus callbacks

Pormiseは、古い形式のコールバックと多少の類似点があります。
これらはコールバック関数を呼び出すオブジェクトであって、コールバックそのものを関数に渡す必要はありません。

しかしながら、Promiseは非同期処理を容易にするために設計されており、古い形式のコールバックよりも多くの利点が存在します。

・複数の非同期操作を.then()を使って連結し、最初の操作の返り値を次の操作の入力に渡すことができます。
これをコールバックで行うことは困難で、しばしばコールバック地獄と呼ばれる厄介なピラミッドになります。
・Promiseのコールバックは、必ずイベントキューに積まれた順番に処理されます。
・エラー処理は旧形式コールバックより遙かに優れています。ピラミッドの各レベルに個別のエラー処理を書く必要がなく、どのレベルでエラーが発生しても最後にあるひとつの.catch()で処理を受け取ることができます。
・コールバックを渡した時点で制御する権利を失う旧形式コールバックと異なり、Promiseは制御の反転を避けることができます。

The nature of asynchronous code

非同期コードの性質をさらに詳しく見てみましょう。
非同期コードの挙動を理解しないまま、非同期コードを同期コードと同じように記述した場合にどのような問題が起こるかを確認します。

以下のコードは、上で出てきたものと似ています。
異なるところは、コードの実行される順番を調べるためにいくつかのconsole.log()が埋め込まれていることです。
実際の動作はこちらで、ソースはこちらで見ることができます。

console.log ('Starting');
let image;

fetch('coffee.jpg').then((response) => {
  console.log('It worked :)')
  return response.blob();
}).then((myBlob) => {
  let objectURL = URL.createObjectURL(myBlob);
  image = document.createElement('img');
  image.src = objectURL;
  document.body.appendChild(image);
}).catch((error) => {
  console.log('There has been a problem with your fetch operation: ' + error.message);
});

console.log ('All done!');

ブラウザがコードを実行すると、まず最初のconsole.log()Startingを表示し、次にimage変数をセットします。

その後は次の行に進んでfetch()ブロックの実行をはじめますが、fetch()は非同期実行されるため、メイン処理はPromise関連のコードより先に一番最後のconsole.log()まで辿り着き、All done!を出力します。

fetch()ブロックはファイルのフェッチ処理が終了すると、次の.then()ブロックに進み、It worked :)console.log()に出力します。
そのため、このメッセージはあなたが思っていたのと異なる順番で表示されることでしょう。

・Starting
・All done!
・It worked :)

よくわからない場合は、次の小さな例を考えてみてください。

console.log("registering click handler");

button.addEventListener('click', () => {
  console.log("get click");
});

console.log("all done");

これは、先ほどの例とよく似た動作をします。
最初と最後のconsole.log()メッセージは即座に表示されますが、2番目のget clickメッセージは、マウスのボタンをクリックするまで表示されません。
Promiseの例では、クリックされるのを待つかわりにリソースの取得が終わるまで待つということになります。

これらのトリビアルな例が示すように、非同期コードは処理順を認識しておかなければ問題を引き起こす可能性があります。
非同期コードブロックは、後で同期コードで使用するために結果を返すようなことはできません。
ブラウザが同期ブロックを処理するより前に、非同期コードが値を返すことを保証しません。

この動作を確認するために、最初の例の3番目のconsole.log()返り値を入れてみましょう

console.log ('All done! ' + image.src + 'displayed.');

コンソールには、メッセージではなくエラーが表示されるはずです。

TypeError: image is undefined; can't access its "src" property

ブラウザが3個目のconsole.log()を実行しようとした時点では、まだfetch()ブロックの動作が完了していないため、imageがまだ定義されていないからです。

注:セキュリティ上の理由から、ローカルのファイルに対してfetch()、もしくは類似の操作を行うことはできません。
上記の例をローカルで再現するためには、ローカルWebサーバを介して実行する必要があります。

Active learning: make it all async!

問題のあるfetch()の例を修正し、3個のconsole.log()が想定したとおりに実行されるためには、3番目のconsole.log()も非同期に実行する必要があります。
これは、2番目のブロックが終わった後に実行されるもうひとつの.then()ブロックを追加するか、あるいは単純に2つめの.then()ブロックの末尾に移動すれば修正できます。
今すぐ試してみてください。

注:行き詰まった場合は、こちらで答えを確認することができます。
あるいはライブに確認できます。
また、後のほうで出てくるGraceful asynchronous programming with Promisesにおいて、Promiseに関するより詳しい情報を知ることができます。

Conclusion

最も基礎的な部分では、JavaScriptは同期・ブロッキング・シングルスレッド言語であり、一度にひとつの動作しか実行することができません。
しかし、Webブラウザは同期実行されない関数を登録する関数およびAPIを提供しています。
そのかわり、これらは何らかのイベントが発生したとき(一定時間経過、ユーザ操作やマウス入力、ネットワーク経由のデータ到着など)に、非同期で呼び出す必要があります。
これらを使うことで、メインスレッドを停止したりブロッキングしたりすることなく、複数の処理を同時に実行させることができるようになります。

コードを同期的に実行するか非同期的に実行するかは、その目的によって決定すべきものです。

対象をすぐにロードして実行したい場合があります。
たとえばユーザ定義スタイルをWebページに適用したい場合、できるかぎり早くスタイルを取り込む必要があるでしょう。

しかし、データベースへのクエリやその結果を使ったテンプレートの作成など、時間のかかる操作を実行する場合は、これをメインスレッドから分離して、非同期にタスクを実行した方がよいでしょう。
時が経つにつれ、この場合は同期ではなく非同期を選択した方が理に叶っていたということがわかります。

In this module

一般的な非同期プログラミングの概念
非同期JavaScriptの紹介
協調型非同期JavaScript:タイムアウトとインターバル
Promiseによる洗練された非同期プログラミング
async/awaitによる簡単な非同期プログラミング
適切な技術を選択する

感想

MDNは、有象無象の氾濫するフロントエンド界隈において、最も信頼できる情報源のひとつです。
わけのわからない変なブログを見るくらいなら、MDNを見た方がよっぽど役に立つことが多いです。
まあ『最も』とか言っちゃうとソースやらW3Cやら見るのが最も適切だって話になるのですが、あんなもの一般人は読んでられませんしね。
あと、たまにMSDN(自動翻訳の混沌)と間違える。

本当はこの記事もMDNにコミットするつもりだったんだけど、構文の変換が想像以上に面倒だったので諦めました。
誰かかわりに送ってやって。

それにしても、ほぼ公式みたいなMDNが未だに訳されてないというのはびっくりですね。

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

Browser/Node.js両対応、シンプルなHTTPクライアント"bent"

Node.jsでHTTPリクエストしたいなーと思って一番メジャーそうな request - npm を覗いたら deprecated になってるじゃないですか!

代わりに、シンプルなHTTPクライアントで良いの無いかなーと調べてたら、requestのissuesコメントにあったbentというクライアントに辿り着きました。

bent

使い方はとても簡単。

localhost:3000GETしてレスポンスボディを文字列で受けたい場合...

const bent = require("bent");

const httpGet = bent("http://localhost:3000", "GET", "string");

const responseBody = httpGet("/");

これだけ。

Node.jsではhttpを、Browserではfetchというように内部で使い分けてるので、コードを統一しつつNode.jsで実行時にはCORS問題が発生しません。

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

僕のJavascript

自分用の備忘録

Javascriptどこに書く?

htmlに直で書く場合

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <title>タイトル</title>
    </head>
    <body>
        <script>
            ここ!
        </script>
    </body>
</html>

基礎的な宣言やら条件分岐やら反復やら

変数と定数
///////変数///////
var hen = 1;
///////定数///////
const hen = 1;
//////テンプレート文字列///////
let msg = `こんにちは${name}さん。`  //${変数}で埋め込み
配列
//普通の配列
var array =['佐藤','鈴木','田中']

//オブジェクトリテラル
var array = [key1:value1,key2:value2,key3:value3]
条件分岐
if(条件式){
    処理;
}
else if(条件式){
    処理;
}
else{
    処理;
}

//超シンプルな条件分岐はexcelの関数みたいに書ける
var x = if(y>=70) ? 'ok':'not ok'
繰り返し
for(var i=0;i<10;i++){
    処理;
}
//中断するときは
if(条件) break;
//途中抜けするときは
if(条件) continue;

関数

function method(引数){処理}

//返り値ある時は return 

htmlの要素の取得の仕方

var element = document.getElementById("element_name"); //idで取得

要素の操作色々

var element = document.getElementById("id")

//valueを操作
element.value = "value"

//タグの中のHTMLを変更
element.innerHTML = "<input type = 'text'>"

//スタイルを変更
element.style.プロパティ=;
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue CLIで作成したindex.htmlからオブジェクトを取得する

タイトルの内容をもうちょっと詳しく

  • Vue CLIで作ったプロジェクト
    • ディレクトリ構成はデフォルトのまま
  • 画面表示用にオブジェクトを渡したいが、諸事情によりindex.htmlに書くことしかできない
    • index.htmlにscriptタグを書いて埋め込む
  • なのでVue側からindex.htmlに記載された値を取得する

サンプルコード

index.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <title>args sample</title>
    </head>
    <body>
        <div id="app"></div>
        <script>
        var htmlArgs = [
        {
            "key1":"hoge",
            "key2":"fuga"
        },
        {
            "key1":"foo",
            "key2":"bar"
        }
        ];
        </script>
    </body>
</html>
App.vue
<template>
  <div id="app">
    <ul>
      <li v-for="arg in args" v-bind:key="arg.key1">
        key1={{arg.key1}} / key2={{arg.key2}}
      </li>
    </ul>
  </div>
</template>
<script>
/* global htmlArgs */
export default {
  name: 'App',
  data: function(){
    return {
      args: htmlArgs
    }
  },
}
</script>
<style>
</style>

解説

今回のキモになるのは、App.vueの/* global htmlArgs */です。
これが無い状態で実行すると、以下のようなエラーとなります。

cmd
ERROR  Failed to compile with 1 errors
error  in ./src/App.vue

Module Error (from ./node_modules/eslint-loader/index.js):

C:\Users\anonymous\sample\src\App.vue
  12:34  error  'htmlArgs' is not defined no-undef

✖ 1 problem (1 error, 0 warnings)

App.vueの中にhtmlArgsがいないのでエラーになります。
無いものを使おうとしているわけですから、当然ですね。

先ほどの/* global htmlArgs */htmlArgsはglobalにいる(からファイル内に無くてもスルーしてくれ)と宣言して、エラーを防ぐという効果があります。

これでindex.htmlにあるオブジェクトを取得できるようになりました。
めでたしめでたし。

参考

アンビエント宣言が’no-undef’ルールにひっかかる件
https://teratail.com/questions/226017

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

JavaScriptについて(素人が浅い知識で書かせていただきました)

JavaScriptで出来ること

ページの表見を変えれる(ooされたら要素をxxのように変更する)。しかしデータとしては保存されないので更新すると、始めのページ状態に戻る。

データのやり取りを行いたい場合はサーバーサイドでの処理が必要(JavaScriptでサーバーサイドで動くのはNode.js)。今回はそこには触れません。

JavaScriptの基本的な概念(DOM)

HTMLの内容を要素の親子関係として捉える。
この要素の親が誰かなどを理解しなければいけない。
DOMはHTMLの要素と似ているが実は少し違う。
HTMLのbodyの中身が読み込まれた後にDOMが作られる。JSではDOMを指定して命令を出すため、HTMLのbodyの中身が読み込まれる前にJSの処理をしてしまうと、DOMが完成していないため取り出す事ができない

HTMLのheadでJSを読み込むと、bodyが読み込まれる前にJS処理が行われてしまう。その時はJSの処理にwindow.onloadとしてHTMLの処理の後に処理が発動するようにすると良い。

応用

全ての子要素に同様にイベント(ooされたらxxする)を設定したい場合、その子達の親要素にイベントを設定してやれば良い。

JavaScriptで主な変更

ooされたら要素をxxのように変更するはどうやってやるのか

基本的にHTMLのidやclassを指定して、関数で命令をする。その時に補足的にif、for文が使われる。

要素を指定するときの例
document.getElementById(要素)
document.querySelector(要素)
document.querySelectorAll(複数の要素)

この要素指定の後に関数を使って
①要素をどう変更させるか
②要素がooされたときどうするか?
などを指定できる

現在ページに存在していないHTML要素を追加したい時

JSで追加したい要素を定義して、親要素の直下に子要素として定数を追加する。

aaa=document.createElement(作成するHTMLタグ名)
document.親要素.appendChild(aaa)

その他にも既にある要素を複製して挿入したり、要素削除も可能

ooされた時ページのデザイン(CSS)を変えたい時

現在ページで使われていないHTMLの要素(idやclass)をCSSで定義する

JSでooされたら、現在HTMLに存在する要素(idやclass)指定して、その値を上のCSSで定義した値に変える

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

SpecTest - ビヘイビア駆動開発(BDD)用テスティング・フレームワーク

はじめに

この記事 の続き。SpecTest の内容を少し紹介。まだすぐに使える形ではないですが、要望があれば何とかしたい。...無いか?

気にせずに進みます。こういうのは勢いと思い一気に書いてみました。

ビヘイビア駆動開発では、振る舞いに対するテストを書くことによって動作仕様を明確にしていく。いわゆる Tests as DocumentationSpecification by Example というもの。

しかし既存の BDD フレームワークは「テストを書く=それがドキュメント」という図式は成り立つものの、ユーザー向けの説明文書になるかというと、決してそうはなっていない、という問題がある、と私は思う、ような気がする。TDD から派生して、あくまで仕様を理解できる、という点にフォーカスしたモノ。

そこで SpecTest だ。

  • SpecTest
    • Writing a specification means writing a test, and examples are becoming test codes as is.
    • 訳「仕様を書くことはテストを書くこと、で、例はそのままテストになってるんだよ」

Specification by Example はまさにそんな感じ、Tests as Documentation はなんか逆転して Documentation as Tests みたいな感じだが、BDD というくくりの本質である「振る舞い駆動」という意味では間違ってないはず。一応、「今までの BDD テスティング・フレームワークが〇〇な形だから、これは違うよ!」という意見は聞かないことにします...。いや、間違ってないはず。たぶん間違っていないと思う。間違ってないんじゃないかな。ちょっとだけ覚悟しておきます。イメージ的には doctest 系に近いと思うが、よりプログラミングの世界から距離を取っています。書くのはユーザー向けのドキュメント。

恐らく厳密な意味で BDD のこれまでの経緯や定義と照らし合わせると違うとは思うがしかし、理想と現実の間の中で私が欲しいと思うスタイルはこうだった、それは概念として BDD と言って差し支えないだろう、ということは表明しても良いかなー、と思ってます。

まーあまり深く考えないでくださいませませ。

どんな感じ?

  • プログラミング言語は問わない(なんでも行ける)
  • Markdown で記述
  • .spectest に解釈ルールを記載(だが沢山書く必要はない)
  • (Markdown を解釈して自動的にコードを抽出して)テストしてレポート

そう、書くのは仕様。

プログラミング言語は問わない

作るのはユーザー説明用の Markdown ドキュメントなので、特にプログラミング言語は問わない。あなたの素敵な Ruby で書かれたプロダクトに対しても機能するようにデザインしましたよ。

というか何だったらプログラムじゃなくても Okay だ。curl コマンド使って REST API で取ってきた JSON の内容が妥当か、みたいな。

Markdown で記述

以下がテンプレート。

# TestSuite Name

最初の `#` で示される表題は自動的にテストスイート名として認識される。

## なんでも

なんでも書ける。

## なんでも

なんでも書ける。

## Examples

`## Examples` はテストコード記述エリア開始のサイン。
テキスト部分はなんでも書ける。

### Example 1. TestCase Name

`### Example [0-9]+\\.` はテストケース開始のサイン。
上記に続く名前がテストケース名になって、1つのテストケースが作られる。

#### Code

`#### Code` はテストコード開始のサイン。
でも以下のコードブロックがテスト本体。それ以外は何でも書ける。

```language
Test Code
```

テストコードの結果は標準出力に出すようにすること。

#### Result

`#### Result` はテスト期待値開始のサイン。
でも以下のコードブロックが期待値本体。それ以外は何でも書ける。

```
Expected Result
```

### Example 2. TestCase Name 2

2 つ目のテストケース。以下略。

予め決まったキーワードにさえ気を付けて書けば、テストという意識を持つことなく文書を書ける。キーワードは .spectest ファイルで指定も可能。

.spectest に解釈ルールを記載

.spectest ファイルのサンプルは以下。詳細は長くなるのでひとまずリンクで... SpecTest の表あたりを参照。

{
    "root": "doc/spec",

    "testfile": "test.kx",
    "resultfile": "result.txt",
    "interpreter": "kinx",

    "ignoreFiles": [
        "doc/spec/../benchmark/README.md",
        "doc/spec/spectest/README.md"
    ]
}

root はドキュメントのあるフォルダ(ディレクトリ)。ここにある README.md または CONTENTS.md をスタート地点として、そこからリンクされている .md ファイルを全てリストアップして実行する。

testfileresultfile はテストするときに使用する一時ファイル。毎回上書きして最後に消すので既にあるファイルを指定しないこと(要注意)。interpreter はテストコードを実行するインタプリタ名。コンパイル言語にはまだ対応していない(そんなに難しくないのでできそうだが)。ignoreFiles はリストアップされてしまうファイルのうち、テスト実行対象外にするファイル名を配列で指定しておく。

テストしてレポート

現在(2020/3/16)の Kinx のテスト状況をサンプルにすると、こんな感じになっている。

Test Cout = 69
[<>[<*********>][<****>][<***>][<*>][<*>][<***>][<****>][<******>][<**>][<**>][<***>]
[<****>][<*****>[W]][<*>][<*>][<*>][<*>][W][W][W][W][W][W][W][W][W][W][W][W][W][W][W]
[W][W]]

<Test Result Detail>

Entry: doc/spec/README.md
    Kinx Specification with SpecTest (0.00s)
    Entry: doc/spec/statement/declaration.md
        Declaration statement (0.55s)
            Case[0] (Normal case) ........................... successful (  0.07s)
            Case[1] (With initializer) ...................... successful (  0.06s)
            Case[2] (With initializer of expression) ........ successful (  0.06s)
            Case[3] (Multiple variable declaration) ......... successful (  0.07s)
            Case[4] (Constant value (1)) .................... successful (  0.04s)
            Case[5] (Constant value (2)) .................... successful (  0.06s)
            Case[6] (Constant value (3)) .................... successful (  0.04s)
            Case[7] (Constant value (4)) .................... successful (  0.04s)
            Case[8] (Constant value (5)) .................... successful (  0.08s)
    Entry: doc/spec/statement/enum.md
        Enum statement (0.29s)
            Case[0] (Normal case) ........................... successful (  0.07s)
            Case[1] (With initializer (1)) .................. successful (  0.07s)
            Case[2] (With initializer (2)) .................. successful (  0.07s)
            Case[3] (The scope) ............................. successful (  0.07s)
    Entry: doc/spec/statement/expression.md
        Expression statement (0.23s)
            Case[0] (Assignment) ............................ successful (  0.08s)
            Case[1] (Exponent Evaluation) ................... successful (  0.07s)
            Case[2] (Logical Undefined Operator) ............ successful (  0.06s)
    Entry: doc/spec/statement/mixin.md
        Mixin statement (0.07s)
            Case[0] (Normal case) ........................... successful (  0.07s)
    Entry: doc/spec/statement/block.md
        Block statement (0.08s)
            Case[0] (Scope) ................................. successful (  0.08s)
    Entry: doc/spec/statement/if_else.md
        If-Else statement (0.21s)
            Case[0] (Normal case) ........................... successful (  0.06s)
            Case[1] (No else clause) ........................ successful (  0.06s)
            Case[2] (If-else combination) ................... successful (  0.07s)
    Entry: doc/spec/statement/switch_case.md
        Switch-Case statement (0.31s)
            Case[0] (Normal case) ........................... successful (  0.08s)
            Case[1] (With do-while) ......................... successful (  0.07s)
            Case[2] (Non-integer value) ..................... successful (  0.08s)
            Case[3] (Complex switch-case pattern) ........... successful (  0.07s)
    Entry: doc/spec/statement/try_catch_finally.md
        Try-Catch-Finally statement (0.43s)
            Case[0] (Normal catch) .......................... successful (  0.07s)
            Case[1] (Finally (1)) ........................... successful (  0.07s)
            Case[2] (Finally (2)) ........................... successful (  0.06s)
            Case[3] (Finally (3)) ........................... successful (  0.07s)
            Case[4] (Define own exception) .................. successful (  0.06s)
            Case[5] (Complex example) ....................... successful (  0.07s)
    Entry: doc/spec/statement/while.md
        While statement (0.14s)
            Case[0] (Normal case) ........................... successful (  0.06s)
            Case[1] (Infinaite loop) ........................ successful (  0.07s)
    Entry: doc/spec/statement/do_while.md
        Do-While statement (0.15s)
            Case[0] (Normal case) ........................... successful (  0.07s)
            Case[1] (Infinaite loop) ........................ successful (  0.07s)
    Entry: doc/spec/statement/for.md
        For statement (0.21s)
            Case[0] (Normal case) ........................... successful (  0.07s)
            Case[1] (Infinaite loop) ........................ successful (  0.07s)
            Case[2] (Declation variable in scope) ........... successful (  0.06s)
    Entry: doc/spec/statement/return.md
        Return statement (0.32s)
            Case[0] (Normal case) ........................... successful (  0.07s)
            Case[1] (Without expression) .................... successful (  0.07s)
            Case[2] (if-modifier (1)) ....................... successful (  0.09s)
            Case[3] (if-modifier (2)) ....................... successful (  0.08s)
    Entry: doc/spec/statement/yield.md
        Return statement (0.36s)
            Case[0] (Normal case) ........................... successful (  0.07s)
            Case[1] (Without expression) .................... successful (  0.06s)
            Case[2] (if-modifier (1)) ....................... successful (  0.08s)
            Case[3] (if-modifier (2)) ....................... successful (  0.07s)
            Case[4] (`yield` returns array.) ................ successful (  0.07s)
        Entry(nolink): doc/spec/statement/statement/fiber.md
    Entry: doc/spec/algorithm/qsort.md
        Quicksort (0.08s)
            Case[0] (Quicksort Algorithm) ................... successful (  0.08s)
    Entry: doc/spec/algorithm/heapsort.md
        Heapsort (0.08s)
            Case[0] (Heapsort Algorithm) .................... successful (  0.08s)
    Entry: doc/spec/algorithm/mergesort.md
        Merge Sort (0.08s)
            Case[0] (Merge Sort Algorithm) .................. successful (  0.08s)
    Entry: doc/spec/algorithm/crc32.md
        CRC32 (0.08s)
            Case[0] (CRC32 Algorithm) ....................... successful (  0.08s)
    Entry(nolink): doc/spec/statement/throw.md
    Entry(nolink): doc/spec/statement/function.md
    Entry(nolink): doc/spec/statement/class.md
    Entry(nolink): doc/spec/statement/module.md
    Entry(nolink): doc/spec/statement/lambda.md
    Entry(nolink): doc/spec/statement/closure.md
    Entry(nolink): doc/spec/statement/fiber.md
    Entry(nolink): doc/spec/lib/primitive/integer.md
    Entry(nolink): doc/spec/lib/primitive/double.md
    Entry(nolink): doc/spec/lib/primitive/string.md
    Entry(nolink): doc/spec/lib/primitive/array.md
    Entry(nolink): doc/spec/lib/basic/file.md
    Entry(nolink): doc/spec/lib/basic/directory.md
    Entry(nolink): doc/spec/lib/basic/regex.md
    Entry(nolink): doc/spec/lib/basic/xml.md
    Entry(nolink): doc/spec/lib/basic/zip.md
    Entry(nolink): doc/spec/lib/net/http.md

<Test Result>
    Total Test Cases:       69
        Successful  :       51
        Failed      :        0
        Warning     :       18

Entry(nolink) はまだドキュメントが書けていないものです。書きます。もちろんテストではなく、仕様(=例)を。

おわりに

まだ以下に対応していないので、以下に対応することでもうちょっと実用になると思う。

  • Todo
    • JUnit 形式の XML で出力する。多分簡単。
    • Kinx をまだ簡単にインストールできないので、簡単にインストールできるようにする。それができないと実質使えない。もしくは完全に Kinx から独立させてしまう。
    • 履歴を保存しておき、テスト結果の推移を可視化できるようにする。
    • CircleCI とかで結果に対するバッチを作れるようにする。
    • コンパイル型のプログラミング言語に対応する。

コンセプトに応援してくださる方は(いつもの通り)★をお願いします。やる気出ると思うので。Kinx のほうに。どうしても分離して独立して使えたほうが良いよ、という方(誰に言ってるんだろう...? まぁいいか)は SpecTest のほうに★してくれればそういう意思表示と認識しましょう。

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

WebカメラとJavaScriptだけで高精度なまばたき検知を実現できた話

概要

ドライバーモニタリングシステムの展示などで睡眠検知のシステムをよく見かけます。

興味があったので自前で実装してみました。

以下のデモサイトでは睡眠検知と書きましたが、やっているのは「まばたき検知」です。

目を瞑っているか、開けているかをWebカメラの映像からリアルタイムに判定します。

これを睡眠検知とするためには、追加で「1〜2秒ほど検知結果を蓄積し、目を瞑っていた割合を見る」というような実装を行う必要がありますが、まばたきさえ正確に取れていれば瑣末な問題です。

デモサイトはPCでご覧ください。

デモはこちら -> https://blink.koatech.info/

試行錯誤

EAR(Eye Aspect Ratio)

初めは以下の記事で紹介されているEAR(Eye Aspect Ratio)という指標が使えそうだったので、それで実装していました。EARは、目尻と目頭の高さの平均と目の横幅の比率です。

https://qiita.com/mogamin/items/a65e2eaa4b27aa0a1c23

しかし、①画面に映っている顔の大きさ、②個人の目の大きさで大きく閾値がブレてしまい、うまくいきませんでした。もっと正確なLandmarksが取得できるようなモデル/顔認識エンジンがあればうまくいくのかもしれませんが、断念しました。

一応実装したものはこちら -> https://blink.koatech.info/ear

瞳の検出

我々は目の大きさ、細さが人によって全く違うにも関わらず、目の前の人が目を瞑っているか閉じているかを瞬時に判断することができます。

なんでだろうとしばらく考えて、「瞳が見えるかどうか」ではないかと仮説を立てました。

そこで思いついたのが、以下の実装です。

  • 目の画像を二値化し、瞳だけが黒くなる状態を作る
  • 目の画像のうち、黒くなっている部分(瞳)の比率で目を瞑っているかどうか判定する

処理の流れ

まず画面左のcanvasにwebカメラの映像を流します。

ユーザーの顔が写っているはずなので、ここで顔認識を走らせます。

顔認識エンジンはface-api.jsを使いました。

https://github.com/justadudewhohacks/face-api.js/

face-api.jsで顔のLandmarks(パーツの位置)が取れますので、左目と右目をそれぞれ画面右のcanvasに写します。

カラーのままでは処理が面倒なので、目を写しているcanvasを二値化します。

すると、目を開けている時は瞳にあたる部分が黒く表示されますが、目を閉じている時は何も表示されません。真っ白な画面になります。二値化の閾値によっては、たまにまつ毛が映る程度です。

画面右には、隠していますが私の顔が写っています。

  • 目を開けている時
    awake.png

  • 目を閉じている時
    sleep.png

これで、私の顔ではほぼ完璧にまばたきを検知することができました。

他の人の顔ではまだ試していないのでわかりませんが、多少閾値をいじる程度で対応可能だと思われます。

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

Ruby on RailsとAlexaで、めちゃんこ操作しにくいゲーム作ってみた。

hero_img_Ruby_Ruby-on-Rails-1.jpg

初めに

スクリーンショット 2020-03-16 17.04.11.png

 この記事では、Ruby on Railsを使用して、Alexaによる音声認識で操作することができる3×3のスライドパズル作成についてかきました。
 Ruby on Railsや、今回サーバーの実装として利用したngrokの導入方法についての説明は省いておりますのでご了承ください。
 今回、技術的説明は極力省き、実装面のみの記事としているため、主な技術要件の参考記事は以下を参照ください。

  1. Alexaコンソールについて
     Alexaコンソール(Alexaスキル)では、どのような情報をサーバー(Ruby on Rails側)にPOSTするのか書き込みます。
    クリスマスプレゼントに、Amazon Echoはいかが?〜Alexaスキルを自作してみよう〜 - Qiita

  2. ActionCableについて
     ActionCableは、Railsに比較的新しく備え付けられた非同期処理を実装するための方法です。WebSocketとの連携を補助するツールと筆者は捉えています。
    最短で作るActionCableチャットアプリ - Qiita

  3. ngrokについて
     こんなの説明いらんやろ!と言われそうですが一応。。
    【3分で出来る】ngrokでデプロイをしてみよう! - Qiita

ngrokを利用するための下準備

#config/environments/development.rb
config.hosts << '.ngrok.io'

上記を追加し、ngrokによるホスティングを許可してあげればOK

ActionCableを利用するための下準備

これは簡単です。

bash
$ rails g channel talks

このコマンドで、WebSocketに関するファイルが生成されます。
今回はコントローラー名も一括してtalksにしています。

#app/channels/talks_channel.rb
class TalksChannel < ApplicationCable::Channel
  def subscribed
    stream_from "some_channel"
  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end

  def speak
  end
end

some_channelというケーブル名にしましょう。

Railsのルーティング設定

以下を記載します。

#config/routes.rb
Rails.application.routes.draw do
  root 'talks#index'
  resources :talks, only: [:create, :index]
end

 カレントディレクトリをプロジェクト内にし、以下のコマンドでルーティングを確認できます。

bash
$ rails routes
POST   /talks(.:format)

 Alexaに話しかけた内容は、エンドポイントで指定されたURLにPOSTで送られます。/talksをAlexaコンソール上でエンドポイントに設定します。

 続いて、コントローラーです。

#app/controllers/tasks_controller.rb
class TalksController < ApplicationController
  skip_before_action :verify_authenticity_token

  # POST /talks
  def create
    request = AlexaRubykit::build_request(params)
    #paramsはAlexaから送られるJSONデータ
    request_word = request.slots[:message][:value]

    response = AlexaRubykit::Response.new
    response.add_speech("#{request.slots[:message][:value]}に移動します.")

    message = @@board.slide(request_word)
    ActionCable.server.broadcast("some_channel",message)

    render json: response.build_response
  end

  def index
    @@board = Board.new
  end

end

#0は空白
class Board
  def initialize
    @@panels = [
      [1,3,6],
      [0,7,2],
      [5,8,4]
    ]
  end
  #入れ替える要素の番号を引数として配列で受け取る index → [j,i]
  def swap(index1,index2)
    tmp = @@panels[index1[0]][index1[1]]
    @@panels[index1[0]][index1[1]] = @@panels[index2[0]][index2[1]]
    @@panels[index2[0]][index2[1]] = tmp
  end
  def slide(dir)
    brank_index = Array.new(2)
    @@panels.each_with_index do |panel,j|
      i = panel.find_index { |value| value == 0}
      if i != nil
        brank_index = [j,i]
        break
      end
    end
    case dir
    when '上', 'ウェイ'
      if brank_index[0] != 2
        self.swap(brank_index,[brank_index[0]+1,brank_index[1]])
      end
    when '下'
      if brank_index[0] != 0
        self.swap(brank_index,[brank_index[0]-1,brank_index[1]])
      end
    when '左'
      if brank_index[1] != 2
        self.swap(brank_index,[brank_index[0],brank_index[1]+1])
      end
    when '右'
      if brank_index[1] != 0
        self.swap(brank_index,[brank_index[0],brank_index[1]-1])
      end
    end
    @@panels
  end
end
#Gemfileに以下を追加
gem 'alexa_rubykit', '1.3.1'

 一気に長いコードが出てきたので少し説明します。
 まず、Boardクラスは、初期化やAlexaからのメッセージによって盤面を移動させたりする関数(slide())から構成されています。

request = AlexaRubykit::build_request(params)
request_word = request.slots[:message][:value]

 この部分では、Alexaから送られたJSONがparamsに入っており、AlexaRubyKitで受け取り、目的とする単語(今回の場合はスライドパズルなので「上」や「下」)をrequest_wordに渡します。

response = AlexaRubykit::Response.new
    response = AlexaRubykit::Response.new
    response.add_speech("#{request.slots[:message][:value]}に移動します.")
~~
    render json: response.build_response

 この部分では、Alexaに応答させる言葉をadd_speechに持たせて、renderでAlexaに送っています。

message = @@board.slide(request_word)
ActionCable.server.broadcast("some_channel",message)

 Alexaから受け取った言葉をslide関数に渡して、盤面を更新していきます。そして更新された盤面をsome_channelというActionCableを使ってクライアント側にブチ飛ばしていますね。

クライアント側での処理

 先ほどブチ飛ばした内容は、talks_channel.jsで受け取ります。

#javascript/channels/talks_channel.js
import consumer from "./consumer"

consumer.subscriptions.create("TalksChannel", {
  connected() {
    // Called when the subscription is ready for use on the server
  },

  disconnected() {
    // Called when the subscription has been terminated by the server
  },

  received(data) {
    // Called when there's incoming data on the websocket for this channel
    let txt = '';
    data.forEach((value) => {
      txt += '<div class="line">';
      value.forEach((e) => {
        if (e == 0){
          txt += '<div class="empty-box" ></div>';
        }else{
          txt += '<div class="box" ><p>' + e + '</p></div>';
        };
      });
      txt += '</div>'
    });
    document.querySelector("#display").innerHTML = txt;
    console.log(txt);
  },

  speak: function() {
    return this.perform('speak');
  }
});

 こんな感じで、クライアント側のjsをいじると、画面を更新することなくスライドパズルが動くというわけです。
 あとはHTMLとCSSを適当にいじって完成です。
スクリーンショット 2020-03-06 16.13.48.png
 なんとも春らしい見た目。

最後に

 AlexaRubyKitってなかなか日本語のドキュメントもないしかなり苦戦したので、これからAlexaを使ってアプリケーションを作ろうとする方の手助けになればいいなと思います。
 ActionCableは筆者的に今熱いのでどんどんプロダクトに使って行きたいですね。
 これからも励んでいきますので、よろしくお願いします。:pig_nose:

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

GoogleAppsScript CacheService PropertiesService

GASでデータの保存方法の2つのやり方を備忘
どちらも文字列での保存になるけど、Javascriptなら文字列化とその復号は簡単なので問題ないかと。

1.PropertiesService

リファレンス
DocumentとScriptとUserとあるけど、普段はScriptを使っている。

  let scriptProperties = PropertiesService.getScriptProperties();
  let props = scriptProperties. getProperty("property-key");
  ...
  ...

  scriptProperties. setProperty("property-key",props);

あまり特別なことはない。
スクリプトプロパティにセットした値は、スクリプトエディターで確認できる。
永続的に使えるので、ソースコードやスプシに書かない設定等でつかっている。

2.CacheService

リファレンス
こちらも、DocumentとScriptとUserとある。
ユーザー共通のキャッシュデータはScriptキャッシュ、ユーザー固有のキャッシュはUserキャッシュ、となるだろうか。(ScriptキャッシュでもUserごとにキーに付け加えればできちゃうけど)

  let cache = CacheService.getScriptCache();
  let cacheData = cache.get("cache-key");
  ...
  ...

  cache.put("cache-key",cacheData,3600);

propertiesがgetProperty、setPropertyなのにcacheはget、put。
put時には、expirationInSecondsのオプションがあるので、何秒キャッシュするか設定する。

こちらは、かなり短期、少量のデータキャッシュとなるけど、他のシートのデータを一時的に保存したり、シートを扱ってるユーザーのデータなどをキャッシュするのにはよいのでは。
キーの長さ:250文字
データ:100kb
最大保存期間:21600秒(ただしキャッシュ溢れで削除の可能性あり)

リファレンスのソースにあるように、キャッシュにあったらキャッシュをかえし、なかったらデータを取りに行くようなコードがメインになると思います。

キャッシュデータを見る方法は自分でコードから取る以外なさそう。

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

Mix usage of Promise async-await and then-catch

function myPromise(val) {
  return new Promise((resolve, reject) => {
    if (val > 5) {
      resolve("resolved"); // ok
    }
    else {
      reject("rejected"); // rejected
    }
  });
}

// just use async-await
async function apicall() {
  try {
    const result = await myPromise(3)
    console.log(result)
  }
  catch (error) { // undefined & exception
    console.log(error)
  }
}

// use async-await and then-catch
async function apicall2() {
  const result = await myPromise(9)
  .then((re) => { return re; })
  .catch((error) => {
    console.log(error)
  })

  console.log(`result2: ${result}`)
}

async function apicall3() {
  const result = await myPromise(8).catch((error) => {
    console.log(error)
  })

  console.log(`result3: ${result}`)
}

apicall3();
apicall2();

apicall();

Those ways are avaliable to perform, but I personally prefer apicall3() because async-await is a wrapper to return resovled data back.

It make more sences and be clearer than apicall2() when you want to use both await and Promise.then() methods.

async function apicall0() {
    const result = await myPromise(3)
    if(result===undefined){
        console.log('error')
    }
    console.log(result)
// DeprecationWarning: Unhandled promise rejections are deprecated. 
//In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
}

apicall0(); // terminated in error
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

スクロールフェードアウトをjQuery無しで

はじめに

js勉強中です。復習がてら投稿しました。

main.js
window.onscroll = function(){
  function fadeOut(el, duration) {
    var node = document.getElementById(el);
    var start = performance.now();
    requestAnimationFrame(function tick(timestamp) {
      // イージング計算式(linear)
      var easing = (timestamp - start) / duration;
      // opacityが0か1から変数easingを引いた値をopacityに渡す
      node.style.opacity = Math.max(1 - easing, 0);
      // イージング計算式の値が1より小さいとき
      if (easing < 1) {
        requestAnimationFrame(tick);
      }
      else {
        node.style.display = 'none';
      }
    });
  }
  var scrollTop = window.pageYOffset ;
  if(scrollTop = 0) fadeOut('fadeout', 100);
}
index.html
 <div id="fadeout">フェードアウトさせる要素</div>

解説

ページトップのときの要素を非表示にさせる処理。
requestAnimationFrameの中の処理はeasingが1より小さい値のときに、取得したDOM要素が

  • opacity:0;
  • display:none;

になる処理をする。

timestamp

perfomance.now()よりは取得する速度は遅い。でもどうやって取得してるかがわからない。

おわりに

逆に解説していただけたらありがたいです。一応コピペで使えます。

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

@kintone/rest-api-client をGitHubからインストールする

はじめに

@kintone/rest-api-client の開発中の機能を、GitHubからインストールして早めに使ってみました。
一般的にNPMパッケージをGitHubからインストールするときに比べて、特殊な方法が必要だったのでメモ。

注意

  • Webpackビルド環境がある前提です
  • 開発中のを勝手に使う場合、深刻なバグがある可能性もあるので自己責任で!

ディレクトリ構造

rest-api-clientは、Gitリポジトリとしては特殊な形をしています。

https://github.com/kintone/js-sdk
この@kintone/js-sdkというリポジトリ内に、サブディレクトリとしてrest-api-clientが存在します。
https://github.com/kintone/js-sdk/tree/master/packages/rest-api-client

NPMの世界では @kintone/rest-api-client という単独パッケージ扱いですが、Gitの世界では単なるサブディレクトリ。
(なんでこんな変な構成なのかは謎。こういうNPMデザインパターンもあるのか?誰か知ってたら教えてください)

なので、GitHubからこんな風にインストールしようとしてもエラーになります。

yarn add https://github.com/kintone/js-sdk/tree/master/packages/rest-api-client

方法その1(Gitのsubmoduleとして使用)

NPMではなく、Gitのsubmoduleとしてインストールします。

mkdir vendor
cd vendor
git submodule add https://github.com/kintone/js-sdk

自分でビルド

cd js-sdk/packages/rest-api-client/
yarn install
yarn build

こんな風にlibフォルダが出来ていたらビルド成功。

$ ls lib
KintoneAllRecordsError.d.ts       KintoneRestAPIClient.js   __tests__/  url.d.ts
KintoneAllRecordsError.js         KintoneRestAPIError.d.ts  client/     url.js
KintoneRequestConfigBuilder.d.ts  KintoneRestAPIError.js    http/
KintoneRequestConfigBuilder.js    KintoneTypes.d.ts         index.d.ts
KintoneRestAPIClient.d.ts         KintoneTypes.js           index.js

使うときは、こんな風に相対パスでjs-sdk/packages/rest-api-clientimportします。

import { KintoneRestAPIClient } from '../vendor/js-sdk/packages/rest-api-client'

これで、2020/3/16時点で未リリースのaddAllRecords関数だって使えちゃいます :smile:
image.png

方法その2(NPMモジュールとしてGitHubからインストール)

最初こっちのやり方考えてたんですが、方法1の方がいいと思ってやめました。

まず、Gitリポジトリ単位でjs-sdkごとインストールしてしまう。

yarn add https://github.com/kintone/js-sdk

そのままでは使えないので、該当ディレクトリに移動して自分でビルド

cd node_modules/@kintone/js-sdk/packages/rest-api-client/
yarn install
yarn build

使うときは、こんな風に@kintone/js-sdk配下のディレクトリをたどってimportします。

import { KintoneRestAPIClient } from '@kintone/js-sdk/packages/rest-api-client/lib'

rest-api-clientまで指定でOKかと思ったら、
rest-api-client/libまで指定しないとうまく動いてくれませんでした。

問題点

ビルド直後はうまく動くんですが、そのあと他のNPMモジュールを追加インストールしたら、せっかくビルドしたrest-api-client/libがきれいさっぱり無くなっちゃいました。インストール毎にクリーンにしてくれるんですね。。

たとえば.gitignoreで該当フォルダを除外しないようにして、libをコミットしたりすればいけますが、node_modulesの中の一部を除外するのがかなり大変だったりするので、やめといた方がよさそう・・・

一応やり方書いておきますが、こんなギャグみたいな.gitignore書いて、

.gitignore
node_modules/*
!/node_modules/@kintone
/node_modules/@kintone/*
!/node_modules/@kintone/js-sdk/
/node_modules/@kintone/js-sdk/*
!/node_modules/@kintone/js-sdk/packages/
/node_modules/@kintone/js-sdk/packages/*
!/node_modules/@kintone/js-sdk/packages/rest-api-client/
/node_modules/@kintone/js-sdk/packages/rest-api-client/*
!/node_modules/@kintone/js-sdk/packages/rest-api-client/.gitignore
!/node_modules/@kintone/js-sdk/packages/rest-api-client/lib/

さらに、こっちの.gitignoreからlibを消しておく。

node_modules/@kintone/js-sdk/packages/rest-api-client/.gitignore
 node_modules/
-lib/
 esm/
 umd/

まぁ、方法1の方が無難ですなw

おわりに

くれぐれも自己責任でね!
僕も正式リリースまでは、プロダクトコードには使いませんから!

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

Vue.js ネコ本チュートリアルのToDoリストを少しアレンジしてみた

はじめに

最近Vue.jsの学習を始めたので、アウトプットがてらToDoリストを作成してみました。
※プログラミングの勉強を始めて3か月ほどなので突っ込みどころありましたらご教授いただけますと幸いです。

自作したToDoリスト

以下が、自作したToDoリストです。
コメント 2020-02-06 211639.jpg
自作ToDoリスト
勉強がてらデザインにはBootstrapを使用してみました。

参考

参考にさせていただいたのは以下のチュートリアルです。
ToDoリストを作りながら学習しよう!

個人的にはVue.jsを学ぶ上で大変お世話になっている本(通称ネコ本)です。

Let's アレンジ

アレンジがカタカナになってるのは見栄えです。(センス・・・)

本チュートリアルでは、状態が完了と着手中の2パターンの切り替えになってるところを、
アレンジ精神で3パターンの切り替えにしたいなと思い保留を追加してみました。

具体的に変更したところは2点。

①使用するデータを追記

変更前

main.js
  data: {
    todos: [],
    current: -1,
    options: [
      { value: -1, label: 'すべて' },
      { value: 0, label: '作業中' },
      { value: 1, label: '完了' }
    ]
  },

⇓⇓⇓

変更後

main.js
  data: {
    todos: [],
    current: -1,
    options: [
      { value: -1, label: 'すべて'},
      { value: 0, label: '着手'},
      { value: 1, label: '完了'},
      { value: 2, label: '保留'}
    ]
  },

※個人的に扱いやすいように数字を変更してます。

②メソッドを変更する

main.js
    doChangeState: function (item) {
      item.state = !item.state ? 1 : 0
    },

⇓⇓⇓

変更後

main.js
    doChangeState: function(item){
      if(2 > item.state){
        item.state = item.state + 1
      } else {
        item.state = 0
      }
    },

三項演算子から条件分岐に変更しました。

JavaScript初心者なので、三項演算子だとわかるまでめちゃくちゃ理解に苦しみました。
(もう少し基礎を学んだほうがいいのは間違いない・・・)

補足

三項演算子は以下の形で条件式がtrueなら式1をfalseなら式2を返します。

条件式 ? 式1 : 式2 

まとめ

Laravelを学んでいく中でフロントサイドでVue.jsを使ってみたくなり学び始めました。

両FWともに様々なチュートリアルがありますが、アレンジ~自作ポートフォリオ完成までには、
しっかり1つ1つのスクリプトの意味を理解していかなければいけないと痛感しました。

引き続きアウトプットしながら学習をしていきたいと思います。
拙いアウトプットですがどなたかの参考になりましたら幸いです。

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

rails とjQueryで星を作る

はじめに

以前の記事で、Rubyを利用して星を散りばめる方法について書かせて頂きました。
今回は、Ruby on railsとjQueryを使って、ボタンを押すと星が作られるようなアプリを作ってみます。

実行

今回はindexアクションのみを使います。

def index

end

jQueryを使えるようにしておきます。

application.js
(前略)
//= require jquery
(攻略)

簡単なビューを用意します。

index.html.haml
.sky 
.ground 
   .ground__btn //星を作るためのボタン
     星を作る 
   .ground__delete //星を削除するためのボタン
     星を消す 
 .stars //この部分に星を作る

star.scss

.sky{ 
   background-color: #013; //夜空っぽい色
   height: 80vh; 
 } 

 .ground{ 
   background-color: #393; 
   height: 20vh; //全体から.skyの高さを引いた分
   padding: 7vh; 
   &__btn{ 
     background-color: #FF0; 
     border: solid 3px #F00; 
     height: 5vh;//スマホでも見れるように、高さはpxで指定しない
     width: 30%; 
     padding: 10px; 
     margin: auto; 
     font-weight: bold; 
     text-align: center; 
     cursor: pointer; //カーソル合わせたときに、アイコンが変わるようにする
   } 
   &__delete{ 
     background-color: #9F0; 
     border: solid 3px #00F; 
     height: 5vh; 
     width: 30%; 
     padding: 10px; 
     margin: auto; 
     font-weight: bold; 
     text-align: center; 
     cursor: pointer; 
   } 
 } 


 .star{ //星のcss 仮で入力する
   height: 50px; 
   width: 50px; 
   border-radius: 50%; 
   background-color: #FF0; 
   position: fixed; 
   -ms-filter: blur(6px); 
   filter: blur(6px); 
   top: 100px; 
   left: 100px; 
 } 

次にJavaScriptの部分です。
まずは。ボタンを押したら発火するようにします。

$(function(){
  $(document).on("click",".ground__btn",function(){
    //この部分に処理を書いていく
  });
});

ボタンを押したら、.starsの下に.stars__starというクラスを持つオブジェクトを生成するようにします。

$(function(){
  $(document).on("click",".ground__btn",function(){
    let star = `
    <div class = ".stars__star"></div>
    `;//星のhtmlデータを作ります
    $(".stars").append(star);//上記のhtmlデータを、.starsの下に追加します
  });
});

これで.btnを押し続ければ星が生成されますが、一か所に同じタイプの星が作られるだけです。
そのため、次にボタンが押される毎に、星の色や形、場所が変わった状態で生成されるようにします。

$(function(){
  $(document).on("click",".ground__btn",function(){
    var color_list = ["red","blue","green","yellow","purple","white","pink","orange"];//星の色を準備します
    var color_number = Math.floor(Math.random() * Math.floor(7));//選ぶ星の番号をランダムで得ます
    var color = color_list[color_number];//上記の配列と変数を使って、星の色を決めます
    var top   = Math.ceil( Math.random()*70 );//星の高さを決めます。.skyのheightが80vhなので、それ以上にならないようにします(.groundに作られないように)
    var left  = Math.ceil( Math.random()*100 );//星の横方向の位置を決めます
    var size  = Math.ceil( Math.random()*150 );//星の大きさを決めます
    let star = `
    <div class = "star" style = "background-color: ${color}; top: ${top}%; left: ${left}%; height: ${size}px; width: ${size}px;"></div>
    `;//ここまでで求められた値を実際のCSSに追加していきます
   $(".stars").append(star);//
  });
});

最後に、.deleteを押したら、生成した星を削除するような処理を書きます。

stars.js
$(function(){
  $(document).on("click",".ground__btn",function(){

var color_list = ["red","blue","green","yellow","purple","white","pink","orange"];
    var color_number = Math.floor(Math.random() * Math.floor(7));
    var color = color_list[color_number];
    var top   = Math.ceil( Math.random()*70 );
    var left  = Math.ceil( Math.random()*100 );
    var size  = Math.ceil( Math.random()*150 );
    let star = `
    <div class = ".stars__star" style = "background-color: ${color}; top: ${top}%; left: ${left}%; height: ${size}px; width: ${size}px;"></div>
    `;
    $(".stars").append(star);
  });
  $(document).on("click",".ground__delete",function(){
    $(".stars").children().remove().fadeOut(700);//.childrenをつけることで、.stars下のオブジェクトをすべて削除します。つけなければ、.starsが消えてしまって、再度.btnを押しても何も起きません。
  });
});

今回はこれだけで終わりですが、音を出す機能をつけても面白いかもしれません。

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

【個人開発でやっておきたい】CircleCIで環境ごとに異なるfirebaseに接続する設定

初めに

背景

  • Vue.jsを用いたWebアプリ(Vue.jsに限った話では無いと思うが念の為)
  • FirebaseのFirestoreをDBとして用い、Firebase上にHostingがしたい.
  • CI/CDにはCircleCIを用いる.

やりたいこと

プレゼンテーション1.png
  • Production環境とStaging環境で接続するDBやHosting先をいい感じに分けてほしい.

ざっくりとした方針

  • Production環境とStaging環境でそもそもFirebase上のプロジェクトを分離し、prodcutionブランチにpushされたときはProduction環境に、 StagingブランチにpushされたときはStaging環境に接続するように設定する.
  • Production環境とStaging環境の両方のAPIキーなどをCircleCI上の環境変数として定義& .envファイルに書き出すように設定
  • build時はNODE_ENVの値により.envから読み出す値を分ける.
  • deploy時はfirebase use {project}でプロジェクトを指定した上でデプロイする.

なお、hostingのみの場合はわざわざプロジェクトを分けたりする必要はありません.
しかし、firestoreはプロジェクトごとに1個しか使えないので、prodcutionとstaging等で分けたい場合はそもそものプロジェクトから分ける必要があります.

前提

  • GithubにPushしたらCircleCIでjobが走るところまでは設定できているとします.

手順

1. Firebase上にプロジェクトを2つ作成する.

production用のプロジェクトと、とStaging用のプロジェクトを2つ作成します.
便宜上以下のふたつを作成したとします.
- MyProjectProd
- MyProjectStag

2. それぞれのAPIキー等を.envに登録する.

本記事ではdotenvを用いて環境変数を読み出します.
最終的には.envはCircleCI上で自動的に作られるようにするので、この手順は飛ばしても構いません.
念の為ローカルで動作確認用に作成しているだけです.
まず、dotenvをインストールしてなければインストールしてください.

yarn add dotenv

project直下に.envを作成してください.

FIREBASE_API_KEY_PROD=****************
FIREBASE_AUTH_DOMAIN_PROD=****************
FIREBASE_DATABASE_URL_PROD=****************
FIREBASE_PROJECT_ID_PROD=****************
FIREBASE_STORAGE_BUCKET_PROD=****************
FIREBASE_MESSAGING_SENDER_ID_PROD=****************

FIREBASE_API_KEY_STAG=****************
FIREBASE_AUTH_DOMAIN_STAG=****************
FIREBASE_DATABASE_URL_STAG=****************
FIREBASE_PROJECT_ID_STAG=****************
FIREBASE_STORAGE_BUCKET_STAG=****************
FIREBASE_MESSAGING_SENDER_ID_STAG=****************

_PRODがついている方にはMyProjectProdの値を、
_STAGがついている方にはMyProjectStagの値を、入力してください.
なお、これらの値はFirebase Console上から確認することが出来ます.

3. firebaseの初期化処理を記述する.

2で登録した値を用いてfirebaseの初期化処理を書いていきます.
使用しているフレームワークや構成によって記述する場所は違うと思いますが、筆者の環境(Vue.js+Nuxt.js)ではplugins/firebase.jsに記述しています.

if (!firebase.apps.length) {
  if (process.env.NODE_ENV === "production") {
    //Prodcution環境
    firebase.initializeApp({
      apiKey: process.env.FIREBASE_API_KEY_PROD,
      authDomain: process.env.FIREBASE_AUTH_DOMAIN_PROD,
      databaseURL: process.env.FIREBASE_DATABASE_URL_PROD,
      projectId: process.env.FIREBASE_PROJECT_ID_PROD,
      storageBucket: process.env.FIREBASE_STORAGE_BUCKET_PROD,
      messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID_PROD
    });
  } else {
    //それ以外(Staging環境,develop環境)
    firebase.initializeApp({
      apiKey: process.env.FIREBASE_API_KEY_STAG,
      authDomain: process.env.FIREBASE_AUTH_DOMAIN_STAG,
      databaseURL: process.env.FIREBASE_DATABASE_URL_STAG,
      projectId: process.env.FIREBASE_PROJECT_ID_STAG,
      storageBucket: process.env.FIREBASE_STORAGE_BUCKET_STAG,
      messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID_STAG
    });
  }
}

このようにprocess.env.NODE_ENVの値によって.envから読み取る値を変えています.

4. NODE_ENVの値を設定できるようにする.

NODE_ENVの値によって処理を変える記述が出来たので、次はNODE_ENVを設定できるようにします.
本記事では、cross-envを使用します.

yarn add cross-env

cross-env自体の使い方は
環境変数設定は基本cross-envだけどたまにenv-cmdも使う
等に書いてあるとおりです.
package.jsonに以下のような設定をします.(nuxt.js仕様)


{ ...
 "scripts": {
    ...
    "build:stag": "cross-env NODE_ENV=\"staging\" nuxt build",
    "build:prod": "cross-env NODE_ENV=\"prod\" nuxt build",
    ...
  },
  ...
}

これで、

yarn build:stag

を実行すればstaging用の変数を用い、

yarn build:prod

を実行すればproduction用の変数を用いてbuildが走ります.

5. firebaseへのhostingの設定

buildしたものをfirebaseにhosting出来る設定をしていきます.
この手順は
Firebase Hosting でWebサイトを公開する方法
こちらの記事などで詳しく解説されていますので、参考にしながら進めると良いと思います.
本記事では簡易的に記述します.

staging用のプロジェクトと、prodcution用のプロジェクトがあるので、両方にエイリアスをつけて設定していきます.

firebase use --add 

で、staging用のプロジェクトをstag,
prodcution用のプロジェクトをprodとして登録します.
これで、例えばstaging用にdeployしたい場合は、

firebase use stag
firebase deploy

で実行できます.

ここまでの動作確認

これで、手動であればprodcutionとstagingを分ける事ができます.
手順としては、
1. 任意の環境用にbuildする.
2. firebaseのプロジェクトを切り替える
3. deployする

となります.
例えば、staging環境であれば

yarn build:stag
firebase use stag
firebase deploy

を実行すればOKです.
ここまで手動で想定通りの動きになるか試してみましょう.
うまく行けば、これらをCircleCI上で実現できるようにします.

6. .envに記述した値の移植

2.で作成した.envファイルは、gitの管理化には入れないため、CircleCI上の環境変数に必要な値を登録し、build時に.envファイルを作成できるようにします.
具体的には、2.で記述したキーと値のセットをそのままCircleCI上のプロジェクトの環境変数に登録します.

7. deployのためのキーの設定

CircleCI上からfirebaseのhostingにdeployするためには、キー等を設定する必要があります.
この手順にも、参考になる記事があるのでそちらを参考に進めてください.
Circle CIからFirebase Hostingに自動ビルド&デプロイする
上記記事に従って、

firebase login:ci

により得られたキーをCircleIC上の環境変数に設定します.
但し、今回はprodcution用とstaging用の2つがあるので

firebase use prod
firebase login:ci

で得られたキーをFIREBASE_TOKEN_PRODとし、

firebase use stag
firebase login:ci

で得られたキーをFIREBASE_TOKEN_STAGとして登録してください.

8. .circleci/config.ymlの記述

最後にcircleCIの設定ファイルを記述していきます.
基本的には、先程手動で行った、

yarn build:stag
firebase use stag
firebase deploy

を実行するような設定をすればOKです.
変更点は、
- 最初にcircleCIの環境変数から.envを書き出す必要があること
- firebaseコマンドのパスを相対パスで指定すること
- deploy時にキー等を指定すること
の3点です.

必要な部分だけを記述したものが以下になります.

version: 2.1
orbs: #CircleCIが準備したnode用の設定を元として使う
  node: circleci/node@1.1.6

commands:
  make_env: #.envを書き出すコマンドをまとめておく
    steps:
      - run:
          name: make .env
          command: |
            echo "FIREBASE_API_KEY_PROD=$FIREBASE_API_KEY_PROD" > .env
            echo "FIREBASE_AUTH_DOMAIN_PROD=$FIREBASE_AUTH_DOMAIN_PROD" >> .env
            echo "FIREBASE_DATABASE_URL_PROD=$FIREBASE_DATABASE_URL_PROD" >> .env
            echo "FIREBASE_MESSAGING_SENDER_ID_PROD=$FIREBASE_MESSAGING_SENDER_ID_PROD" >> .env
            echo "FIREBASE_PROJECT_ID_PROD=$FIREBASE_PROJECT_ID_PROD" >> .env
            echo "FIREBASE_STORAGE_BUCKET_PROD=$FIREBASE_STORAGE_BUCKET_PROD" >> .env
            echo "FIREBASE_API_KEY_STAG=$FIREBASE_API_KEY_STAG" >> .env
            echo "FIREBASE_AUTH_DOMAIN_STAG=$FIREBASE_AUTH_DOMAIN_STAG" >> .env
            echo "FIREBASE_DATABASE_URL_STAG=$FIREBASE_DATABASE_URL_STAG" >> .env
            echo "FIREBASE_MESSAGING_SENDER_ID_STAG=$FIREBASE_MESSAGING_SENDER_ID_STAG" >> .env
            echo "FIREBASE_PROJECT_ID_STAG=$FIREBASE_PROJECT_ID_STAG" >> .env
            echo "FIREBASE_STORAGE_BUCKET_STAG=$FIREBASE_STORAGE_BUCKET_STAG" >> .env
            cat .env

jobs:
  deploy_stag: # staging環境にデプロイする用
    executor:
      name: node/default
      tag: "12.13" # 念の為バージョン(正確にはdockerのtag)を指定しておく
    steps:
      - checkout
      - node/with-cache:
          steps:
            - run: yarn install
      - make_env
      - run:
          name: switch firebase project to production
          command: ./node_modules/.bin/firebase use stag
      - run:
          name: build
          command: yarn build:stag
      - run:
          name: deploy
          command: ./node_modules/.bin/firebase deploy --project=$FIREBASE_PROJECT_ID_STAG  --token=$FIREBASE_TOKEN_STAG

  deploy_prod: # prodction環境にデプロイする用
    executor:
      name: node/default
      tag: "12.13"
    steps:
      - checkout
      - node/with-cache:
          steps:
            - run: yarn install
      - make_env
      - run:
          name: switch firebase project to production
          command: ./node_modules/.bin/firebase use prod
      - run:
          name: build
          command: yarn build:prod
      - run:
          name: deploy
          command: ./node_modules/.bin/firebase deploy --project=$FIREBASE_PROJECT_ID_PROD --token=$FIREBASE_TOKEN_PROD

workflows:
  deploy:
    jobs:
      - deploy_stag:
          filters: # stagingブランチの場合はstaging環境へのデプロイ
            branches:
              only: staging

      - deploy_prod:
          filters: # releaseブランチの場合はrelease環境へのデプロイ
            branches:
              only: release

これで、
stagingブランチにpushされた場合はdeploy_stagが、
releaseブランチにpushされた場合はdeploy_prodが実行されるはずです.
意図したとおりに挙動していれば無事終了です.

最後に

これがベストなやり方かどうかは分かりませんが、一応このやり方で特に問題は無いと思います.
方針さえ立てばそれぞれの手順自体は難しくないはずです.
やってみれば大したことないですが、自分はどういう方針で行くか結構手探りで進めたので、この記事が他の誰かのお役に立てれば幸いです.

また、firestoreのルールに関してCircleCI上で走らせるのも少しだけ手間取ったりしたので自分の備忘録のためにもそのうち書きたいと思ってます.

参考

環境変数設定は基本cross-envだけどたまにenv-cmdも使う
Firebase Hosting でWebサイトを公開する方法
Circle CIからFirebase Hostingに自動ビルド&デプロイする
FirebaseをStaging環境とかDebug環境とかRelease環境で切り替えをする(Webアプリ編)

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

実証済!シャックリをとめるプログラム

はじめに

先日、しゃっくりが止まらなくて困っている同僚に祖母直伝の方法を試したら
ピタリと治ったので、それをフローとプログラムに落とし込んでみます。
決してネタに困って出オチ記事に走っているわけではないです。。

フローチャート

フローチャート.jpg

プログラム

StopHiccup.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <title>シャックリ停止めます?</title>
  </head>
  <body>
    <h1>シャックリ停止めます?</h1>
    <button id="btn">開始</button>
    <div id="area1">カウントダウン</div>
    <script>
        document.getElementById("btn").addEventListener('click', function() {
        var r1 = window.confirm('シャックリが止まりませんか?\nはい=OK、いいえ=キャンセル');
        if( r1 ) {
          //「true」の処理

          window.alert('シャックリ停止シーケンスを開始します。\nまず初めに、めちゃくちゃ息を吸ってみよう!');

          var r2 = true;
          do{
            r2 = window.confirm('限界まで息を吸えてる?(ここが最重要ポイント)\nはい=OK、いいえ=キャンセル');
            if( !r2 ) {
              window.alert('まだいける!\nこれ以上吸えないってとこまで頑張ってみよう!');
            }
          }while (!r2);

          window.alert('はい!息を止めて!\nあと少しだよ!がんばってー\nカウントダウン開始');

          document.getElementById("area1").innerText = 10;

          sleep(10, function() {
            document.getElementById("area1").innerText = "お疲れ様でした"
            window.alert('はい!終わりましたー。息はいて大丈夫です。\nお疲れ様でした。これでもうシャックリは出ないはずです。');
          });
        }
      })

function sleep(waitSec, callbackFunc) {
  var spanedSec = 0;
  var waitFunc = function () {
      spanedSec++;
      document.getElementById("area1").innerText = 10 - spanedSec;
      if (spanedSec >= waitSec) {
          if (callbackFunc) callbackFunc();
          return;
      }
      clearTimeout(id);
      id = setTimeout(waitFunc, 1000);
  };
  var id = setTimeout(waitFunc, 1000);
}

</script>
  </body>
</html>

最後に

しゃっくりは横隔膜の痙攣が原因だそうですが、止めるには横隔膜を刺激すれば止まるそうです。
なので、めちゃくちゃ息を吸って大角膜を広げてあげることでシャックリを止めている。
ということでしょうか。

以上、お付き合いいただきありがとうございましたm(__)m

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

【Javascript・GAS】文字列から記号を取り除きたい時

コピペで使えるようにしました.

function remove() {
  const str = "ABC,.~!@#$%^&*()_+{}[]:;\"'<>?\\/|DE";
  const replaced = str.replace(/[,\.~!@#\$%\^&\*\(\)_\+\-=\{\}\[\]:;"'<>?\\\/\|]/g, '');
  console.log(replaced); // "ABCDE"
}

全角も含むならこんな感じです.

function remove() {
  const str = "【】『』A,B.・;’「」`\C,.~!@#$%^&*()_+{}[]:;\"'<>?\\/|DE";
  const replaced = str.replace(/[【】『』,.・;’「」`\,\.~!@#\$%\^&\*\(\)_\+\-=\{\}\[\]:;"'<>?\\\/\|]/g, '');
  console.log(replaced); // "ABCDE"
}

記号は無限にあるので必要があれば随時追加してください.

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

Nuxt.jsのpagesでコンポーネントを定義する方法(2パターン)

環境

  • Nuxt.js
  • Vue.jp

ディレクトリ構成

my-project/
├ components
│ └ OriginalDialog.vue
├ pages
│ └ index.vue
└ 以下省略

コンポーネント定義(パターン1)

よくみるコーディング

index.vue
<original-dialog ref="originalDialog" />

<script>
import OriginalDialog from '@/components/OriginalDialog'

export default {
  components: {
    OriginalDialog
  },
</script>

コンポーネント定義(パターン2)

すっきりしたコーディング

index.vue
<original-dialog ref="originalDialog" />

<script>
export default {
  components:{
    originalDialog: () => import('@/components/OriginalDialog'),
  }
</script>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptでオブジェクト(≒連想配列)をフィルタリングしたい

オブジェクト(≒連想配列)をフィルタリングしたい

たとえば、以下のようなオブジェクトがあるとします。

const objInput = {
  hoge: 'hogehoge',
  fuga: 'fugafuga',
  piyo: 'piyopiyo'
};

hogefuga のみを残した以下のような新しいオブジェクトを作るのが今回の目的です。

{
  hoge: 'hogehoge',
  fuga: 'fugafuga'
}

ようするに hogefuga の要素だけを抽出したいんです。

手法A:べた書きする

hogefuga をべた書きする方法です。

const objOutput = {
    hoge: objInput['hoge'],
    fuga: objInput['fuga']
};

うん、とてもシンプル!

hogefuga というキー名が固定なのであれば、この書き方がわかりやすいと思います。

ただ、フィルタリングしたいキー名が状況に応じて変わる場合、この書き方は使えません。。。

手法B:forEachを使う

以下のようにフィルタリングしたいキー名のリストがある場合を考えます。

const keyList = ['hoge', 'fuga'];

この keyList の要素を forEach で巡ってみます。

// 出力オブジェクトを空オブジェクトで初期化します。
const objOutput = {};

// 抽出する要素の一覧で繰り返し
keyList.forEach((key) => {
    // 出力オブジェクトにキーを追加して、値を代入
    objOutput[key] = objInput[key];
});

この書き方もそこそこシンプルだと思います。

forEach さえ知っていれば、読めるので、他の人にコードを引き継ぐときの混乱も少なそうです。

ただ、最初に const で宣言している objOutput に後から要素を追加していくのは、少しリスクを感じなくもないです。

手法C:reduceを使う

今度は keyList の要素を reduce で巡ってみます。

// 出力オブジェクトをreduceの結果で初期化します。
const objOutput  = keyList.reduce((objAcc, item) => {
    // 累積オブジェクトにキーを追加して、値を代入
    objAcc[key] = objInput[key];
    // 累積オブジェクトを更新
    return objAcc;
}, {});

この書き方は万人受けしないと思いますが、個人的には好きです。

objOutput を、宣言と同時に、 reduce の結果で初期化しているので、良い感じです。

ただ、 reduce を知らないと読めないので、コードレビューとかの際に、揉めるかもしれません。

最後に

「他にもこんな書き方あるよ!」というのがあれば、是非コメントとかで教えてください!

本記事作成にあたり、以下を参考にしました。ありがとうございました。

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

jsで戻るボタンを作成した

最初はphpでパラメーターに引数を渡して戻るボタンを作成したようと思ったが、jsの方が簡単だった。
バックエンドがphpでjsで戻るボタンを作成したときに、参考にした記事
JavaScriptやPHPで1つ前のページに戻るボタンを設置する

//onclick処理
<input value="前に戻る" onclick="history.back();" type="button">

//href属性に設定
<a href="javascript:history.back()">前に戻る</a>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vuetifyで既存ページのデザインを改善してみた

はじめに

最近 Vue.jsとVuetifyに出会って
「こんな簡単に綺麗なサイト作れるんかよ!」
って衝撃を受けました。

せっかくなら前に作ったツールのデザインもVuetifyで改善してみたいと思ってやってみることに。

ビフォー

改善前のページがこちら

before

htmlとcssだけで短時間で作ろうとするとこんな出来栄えになりました・・・;

良い点を挙げるならシンプルなとこかな(?)
なのでシンプルを維持したままデザインを改善してみました。

アフター

Vuetifyでデザインを改善したものがこちら

after

なかなかいい感じ!

全部 <v-card> の中に納めました。
リンク生成ボタンを押した後に表示したい要素は<v-expand-transition>に入れることでスッキリしました。

さいごに

Vuetifyで書くとめちゃくちゃ簡単にUI改良できるので楽しすぎる。

実はフレームワーク使うのって今回が初めて。
もうフレームワークなしで何かを作るのは無理かもしれない笑

おわりです

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

Basic認証の領域へプリフライトでリクエストしてハマった

概要

CORS(クロス オリジン リソース シェアリング)となるリクエストを出したのにうまくレスポンスが戻らない。こんな時、プリフライトという仕組みに悩まされたエンジニアも多いでしょう。私もそんなエンジニアの1人でしたが、もう克服したものだと思っていました。
先日、Basic認証下の領域にあるJSONファイルについて、CORSで取得する必要がありました。まぁいつものプリフライトかと思い、過去の経験から適切な設定をしたリクエストを投げたのですが、401エラーのみが戻りうまくいきません。
手元でサーバーサイドも再現させたところ、Basic認証とプリフライトの相性の悪さを知ることができたので、共有したいと思います。

前提

少々前置きが長くなりますが、今回の問題を理解するためには前提知識が必要となります。わかっている方は飛ばしてください。

Chromeでプリフライト リクエストの観察

標準では、Inspectを使ってもプリフライト リクエストは隠されていて見ることはできません。以下にアクセスして、Out of blink CORS を Disabled に設定する必要があります。
chrome://flags/#out-of-blink-cors

Basic認証

Basic認証は2通りの方法があります(ありました)。

  • Authorizationヘッダー
    HTTPリクエストの際にAuthorizationヘッダーに情報を付与することで、認証に成功する。
  • URLエンコーディング
    RFC3986 3.2.1 ユーザ情報 に定義されている方法で、URLの中に認証情報を埋め込むことができる。
    が、Chrome等の最近のブラウザでは、セキュリティ上の理由からURLにこれらの認証情報がURLに含まれている場合、リクエスト前に削除するようになった。

つまり、Basic認証にはAuthorizationヘッダーを付与しなければならない、という状況です。

CORS

こちらについては多くのサイトでも説明されているので大幅に省略します。以下、今回の問題に関連する重要な部分のみを説明。

  • CORSとなるリソースを取得する場合、クロスオリジン リクエストを行う必要がある。クロスオリジン リクエストは、シンプル リクエストとプリフライト リクエストに分かれる。
  • 今回は、Basic認証のために Authorizationヘッダー が付与されるため、プリフライト リクエストとなる。

プリフライトリクエスト

仕様は W3C のこのあたりですが、英語でも日本語でも(私には)わかりにくいです(汗
https://www.w3.org/TR/cors/#cross-origin-request-with-preflight-0
以下、今回の問題に関係するポイント

  • CORSに該当するリクエストで、単純なリクエストではない場合、ブラウザが 勝手に プリフライトリクエストを投げる
    ※ "勝手に" というか仕様です
  • OPTIONS メソッドとなる(GETやPOSTではない)
    OPTIONSメソッドは安全(変更や削除ではなくサーバーから付加的情報を得るためのメソッドのため)
  • カスタムヘッダーはリストとして、Access-Control-Request-Headersに追加される
  • カスタムヘッダーは削除される
    ※ Basic認証のAuthorizationヘッダーはカスタムヘッダーなので削除される
  • リクエストボディは空となる

試行錯誤

  • サーバー側
    • CentOS7
    • Apache 2.4.6
  • クライアント側
    • Chrome 80.0

同一オリジン Basic認証あり でのリクエストの流れ

同一オリジンのためCORSとならないが、Basic認証が必要なリクエストとなります。
※ ちなみに図はブラウザでアクセスした場合の流れで、②と③の間でIDとPasswordを入力するダイアログが出る感じですね。
Screen Shot 2020-03-16 at 14.30.36.png

  1. クライアントからJSONをもらうためのリクエストを投げる
  2. サーバーからのレスポンス
    Basic認証領域のため、認証情報がないとダメだよと401が戻る
  3. クライアントからJSONをもらうためのリクエストを認証情報付きで投げる
  4. 200 OK でJSONデータが戻る

別オリジン Basic認証なし でのリクエストの流れ

次に別のオリジンのためCORSではあるのですが、Basic認証のない流れを見てみましょう。
Screen Shot 2020-03-16 at 13.57.27.png

  1. クライアントからJSONをもらうためのリクエストを投げようとするが、CORSのためプリフライトリクエストとなる
    OPTIONSメソッドで、これから何のメソッドやヘッダーを使いたいのかリクエストする
  2. サーバーからのプリフライトリクエストに対して200 OKが戻る
  3. JSONを取得するGETリクエストを投げる
  4. 200 OK でJSONデータが戻る

別オリジン Basic認証あり でのリクエストの流れ(NGパターン)

では別オリジンでCORSとなり、Basic認証がある場合の流れを見てみましょう。
Screen Shot 2020-03-16 at 14.00.08.png

  1. クライアントからJSONをもらうためのリクエストを投げようとするが、CORSのためプリフライトリクエストとなる
    OPTIONSメソッドで、これから何のメソッドやヘッダーをを使いたいのかリクエストする
    このとき、Basic認証のAuthorizationヘッダーも投げているのだが、プリフライトリクエストのためブラウザが勝手にヘッダーを削除してしまう
  2. サーバーからのレスポンス
    Basic認証領域のため、認証情報がないとダメだよと401が戻る

ここが今回ハマったポイントでした。Basic認証はヘッダーに認証情報を入れないと渡せない。しかし、プリフライトでは特定のヘッダーしか送れない。つまり、このままではBasic認証を通れないということになります。

対応方法

2パターンの対応を思いつきました。

  • Basic認証ではなく、サーバーの下のアプリケーションレベルでの認証を行う
  • プリフライトリクエストはBasic認証を行わない

今回、後者の方法で解決してみました。具体的には、Webサーバーの設定でOPTIONSメソッドの場合はBasic認証を行わない、という設定を行います。例えば .htaccess であれば、下記のLimitExcept を使うことで「OPTIONSを除いてBasic認証する」と設定することができます。

.htaccess
# アクセスを許可するURLを指定
Header set Access-Control-Allow-Origin "https://example.hoge"

# 許可するリクエストヘッダのメソッドを指定
Header set Access-Control-Allow-Methods "POST, GET, OPTIONS"

# 許可するリクエストヘッダの種類を指定
Header set Access-Control-Allow-Headers "Content-Type, origin, authorization"

# プリフライトレスポンスをキャッシュする時間を指定
Header set Access-Control-Max-Age "600"

# OPTIONSに対する反応
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]

<LimitExcept OPTIONS>
  AuthType Basic
  AuthUserFile /hoge/.htpasswd
  AuthName "ID and password"
  Require valid-user
</LimitExcept>

こうすることで、以下のような流れになります。

別オリジン Basic認証あり でのリクエストの流れ(OKパターン)

別オリジンでCORSとなり、Basic認証がある場合です。NGパターンと違い、OPTIONSの場合、Basic認証が除外されます。
Screen Shot 2020-03-16 at 13.57.42.png

  1. クライアントからJSONをもらうためのリクエストを投げようとするが、CORSのためプリフライトリクエストとなる
  2. サーバーからのレスポンス
    Basic認証領域だがOPTIONSのため認証は除外される
  3. クライアントからJSONをもらうためのリクエストを認証情報付きで投げる
    ※ この例では手動ではなくJavaScriptで送信しているためAuthorizationが最初から付いている
  4. 200 OK でJSONデータが戻る

まとめ

結局の所コンテンツを管理しているサーバー側にて対応してもらう、ということになります。まぁコンテンツを管理するのはサーバー側ですからね。
基本的に公開されているデータなので、Basic認証付きのCORSって少ないのかなぁ、とは思うのですが、いろいろな大人の事情によりこういうケースもあるかと思います。

ここまでくれば、今後はプリフライトで悩むこともないだろー。とフラグを立てて締めさせていただきます。

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

入力された値を大文字で表示したい

JavascriptのtoUpperCase();

$('#abc').keyup(function(){
this.value = this.value.toUpperCase()
});

『テスト』と打とうとすると。
『Tテスト』になる。

text-transform: uppercase;

解決

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

簡素なtodoリストをjavascriptで作る

はじめに

こんにちは。javascript初学者のたけしです。
javascriptで自分なりにtodoリストを作成したので、備忘録とアウトプットを兼ねて投稿します。
全てのコードを自分でググりながら書いたので、わかりにくい箇所やもっと良いやり方があるかもしれませんが私と同じような初学者の方の参考になれば幸いです。

作成の流れ

まずはtodoリストに追加したい機能を洗い出していきました。
機能の洗い出しの方法は前回私が投稿した記事を参考にして頂ければと思います。
JavaScript初学者の私がFizzBuzzアプリを自作して学んだこと

今回私が洗い出した機能
・入力された値の読み取り
・追加ボタンを押すことで入力された内容を追加する
・追加した内容にボタンを追加する(作業中・完了ボタンのことです)
・作業中・完了をボタンを押すことで切り替える
・ラジオボタンを押すことでリストの表示を切り替える
・削除ボタンを押すことでリストから削除する
・入力したら中身のテキストがクリアされる
・何も入力していない状態では機能が作動しない

以上となります。

最初から全てを洗い出していた訳ではありませんが、作っている最中にこれもつけよみたいな感じで継ぎ足していくのもいいと思います。
イメージがあるとわかりやすいと思うので、完成品のイメージを載せておきます。
スクリーンショット 2020-03-15 20.36.11.png

htmlをまずは書いていく

まずはざっとhtmlで必要なものだけ書いていきました。
参考程度に載せておきます。

html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="css/styles.css">
</head>
<body>
  <h2>新規タスクの追加</h2>
  <input type="text" id="newtasc">
  <button id="add">追加</button>

  <ul class="radio">
    <li>
      <input type="radio" id="r1" name="list" value="1" checked><label for="r1">全て</label>
    </li>
    <li>
      <input type="radio" id="r2" name="list" value="2"><label for="r2">作業中</label>
    </li>
    <li>
      <input type="radio" id="r3" name="list" value="3"><label for="r3">完了</label>
    </li>
  </ul>

  <h1>TODOリスト</h1>
  <section>
    <ul id="list">
    </ul>
  </section>

  <script src="js/main.js"></script>
</body>
</html>

cssも表示の切替などに必要なので載せておきます。

css
.radio {
  display: flex;
}

.radio li {
  list-style: none;
  margin-right: 15px;
}

button {
  background-color: whitesmoke;
  padding: 5px 15px;
}

.wrap {
  display: flex;
  flex-flow: row;
}

.divoneli {
  margin-right: 50px;
}

.btntwo {
  margin-right: 20px;
}

.none {
  display: none;
}

JavaScriptを書いていく

htmlで骨組みを作り終わったら早速javascriptを書いていきました。

コードを読みやすくするために最初に全てのコードを載せますが、後から一つずつどういう理由で書いたのか説明していきます。

JavaScript
'use strict';
{
  // 値を読み取る
  let newtasc = document.getElementById('newtasc');

  // 入力された値をリストに追加する
  let add = document.getElementById('add');
  add.addEventListener('click', () => {

    if (newtasc.value !== '') {
      // TODOリストに表示する
      let list = document.getElementById('list');

      // div要素を作成
      let wrapper = document.createElement('div');
      wrapper.className = 'wrap';
      let divone = document.createElement('div');
      let divtwo = document.createElement('div');

      // 作成した要素に追加 
      list.appendChild(wrapper);
      wrapper.appendChild(divone);
      wrapper.appendChild(divtwo);

      // li.button要素の作成 
      let li = document.createElement('li');
      li.className = 'divoneli';

      let btnone = document.createElement('button');
      btnone.innerHTML = '削除';
      btnone.className = 'btnone';
      let btntwo = document.createElement('button');
      btntwo.innerHTML = '作業中';
      btntwo.className = 'btntwo';

      // ②を①に追加
      wrapper.insertBefore(li, divone);
      wrapper.insertBefore(btnone, divtwo);
      wrapper.insertBefore(btntwo, btnone);

      // inputで読み取った値を表示
      li.innerHTML = newtasc.value;


      // 作業中・完了ボタンの切替
      btntwo.addEventListener('click', () => {
        if (btntwo.innerHTML === '作業中') {
          btntwo.innerHTML = '完了';
        } else {
          btntwo.innerHTML ='作業中';
        }
      });

      // 削除ボタンを押すと削除される
      btnone.addEventListener('click', () => {
        list.removeChild(wrapper);
      });

      // チェックボックスの表示の切替
      let all = document.getElementById('r1');
      let working = document.getElementById('r2');
      let done = document.getElementById('r3');

      // 作業中
      working.addEventListener('click', () => {
        if (btntwo.innerHTML !== '作業中') {
          wrapper.classList.add('none')
        } else {
          wrapper.classList.remove('none')
        }
      });

      // 完了
      done.addEventListener('click', () => {
        if(btntwo.innerHTML !== '完了') {
          wrapper.classList.add('none');
        } else {
          wrapper.classList.remove('none')
        }
      });

      // 全て
      all.addEventListener('click', () => {
        wrapper.classList.remove('none');
      });

      document.getElementById('newtasc').value = '';

    }

  });

}

入力された値を読み取る

まずは入力された値を読み取るための変数を作ります。

JavaScript
let newtasc = document.getElementById('newtasc');

inputタグからid属性を取得しています。
変数newtasc.valueで入力された値を読み取ることができます。

追加ボタンを押すことで入力された値を追加する

ボタンを押した時に関数を実行して欲しいので、タグのid属性を取得し、addEventListnerを使います。

JavaScript
  let add = document.getElementById('add');
  add.addEventListener('click', () => {

入力した値の表示

ボタンを押すことで値を追加していけるようにしたいのですが、その追加した値の横に作業中・完了のボタンを追加したいのでDOMを扱っていきます。
DOMとは簡単に言うと、HTMLをJavaScriptから操作するものです。
DOMの使い方などに関する説明についてはここでは割愛し、私がどのような意図で要素を作っていったのかだけ説明していきます。

私はリストの横にボタンを表示したかったので、flexboxを使うためにdivをdivで囲むようにしました。

JavaScript
      // div要素を作成
      let wrapper = document.createElement('div');
      wrapper.className = 'wrap';
      let divone = document.createElement('div');
      let divtwo = document.createElement('div');

下のコードで作ったdiv要素にボタンを追加しています。

JavaScrip
      // 作成した要素に追加 
      list.appendChild(wrapper);
      wrapper.appendChild(divone);
      wrapper.appendChild(divtwo);

      // li.button要素の作成 
      let li = document.createElement('li');
      li.className = 'divoneli';

      let btnone = document.createElement('button');
      btnone.innerHTML = '削除';
      btnone.className = 'btnone';
      let btntwo = document.createElement('button');
      btntwo.innerHTML = '作業中';
      btntwo.className = 'btntwo';

      // ②を①に追加
      wrapper.insertBefore(li, divone);
      wrapper.insertBefore(btnone, divtwo);
      wrapper.insertBefore(btntwo, btnone);

ボタンの切替

ボタンの切替(作業中・完了)のために再度addEventListenerを使います。
if文を使って、現在のボタンの表示によって、ボタンのhtmlの書き換えをinnerhtmlでしています。

JavaScript
      btntwo.addEventListener('click', () => {
        if (btntwo.innerHTML === '作業中') {
          btntwo.innerHTML = '完了';
        } else {
          btntwo.innerHTML ='作業中';
        }
      });

削除ボタンを押すとリストから削除される

ボタンを押したときにリストから削除したいので、こちらでもaddEventListenerを使い、追加した要素を取り除くことでリストから削除しています。

JavaScript
btnone.addEventListener('click', () => {
        list.removeChild(wrapper);
      });

ラジオボタンを押すことでリストの表示を切り替える

ここが一番苦労して色々書いては消してを繰り返してやっと機能を追加できたところです(笑)

まず、ラジオボタンをクリックしたときに関数を実行したいので、ラジオボタンのid属性を取得し、addEventListenerを使っていきます。
ただし、今回のtodoリストではラジオボタンの選択肢が3つあるので、それぞれでaddEventListenerを使いました。
if文でボタン(作業中・完了)のinnerhtmlを調べて、classをつけたり外したりすることで表示を切り替えています。

JavaScript
      // チェックボックスの表示の切替
      let all = document.getElementById('r1');
      let working = document.getElementById('r2');
      let done = document.getElementById('r3');

      // 作業中
      working.addEventListener('click', () => {
        if (btntwo.innerHTML !== '作業中') {
          wrapper.classList.add('none')
        } else {
          wrapper.classList.remove('none')
        }
      });

      // 完了
      done.addEventListener('click', () => {
        if(btntwo.innerHTML !== '完了') {
          wrapper.classList.add('none');
        } else {
          wrapper.classList.remove('none')
        }
      });

      // 全て
      all.addEventListener('click', () => {
        wrapper.classList.remove('none');
      });

おわりに

ここまで読んで頂きありがとうございました。
読みにくい箇所やわかりにくい箇所などあったと思いますが、少しでも参考になれば幸いです。
長くなってしまいましたが、ここでおわりにしたいと思います。

あ!最後にもっとこうした方がいいよってアドバイスして頂ける方がいらっしゃいましたらぜひお願い致します!

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

jQueryでonChange属性にJavaScriptコードを埋め込む

こんな経験ありませんか?

ローコード開発を行っていて「基盤の制約により入力値の変更時イベントが設定できない」こんな経験ありませんか?
今回は「jQueryでonChange属性にJavaScriptコードを埋め込む」方法を紹介します。

方法

画面の初期表示時にクライアントサイドJavaScriptにて、以下のコードを実行します。

$('input[name="%任意のname値%"]').on('change',function(){
    // %任意のJavaScriptコード%
});

結果

これで任意の画面項目の入力値の変更時イベントが設定できます。
その他のイベント属性にも応用できると思いますので、試してみてください。

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

Vue.jsでファイル名に連番を付けていく方法

vue-awesome-swiperを使うときにファイル名をバラバラにせず、連番にすればスッキリできるのではと思いそのやり方をまとめてみました。

開発環境はNuxt.js + Vuetifyにvue-awesome-swiperを使った状態です。

Nuxt.jsにvue-awesome-swiperを導入し、カルーセルを表示させてみる
【Nuxt.js】imgファイルの指定方法について
【Nuxt.js】Universalモードで「window is not defined」エラーが出たときの対処法

変数を宣言する

const items = []

ループを作る

for (let i=1; i<=5; i++) {
//ここに処理を入力する
}

ループ内の処理を記入する

itemsに数字を足していく処理が必要なのでpushを使用します。

items.push({
  no: i,
  title: 'title' + i,
  slideimage: `/images/home/slide${i}.jpg`
})

※1.画像ファイルの部分ですがシングルクォートで囲っているのではなくバッククォートで囲っている点注意。
※2.スライドショー等で見せたいような画像はコンパイルの時間も減るでstaticに格納するほうがおすすめです。

ただしこのままだとファイル名に変数iによる連番を付加することができますが、ファイル名の連番部分の前の0を付けた"slide01.jpg"のようなフォーマットだと、変数のiが一桁のときに前0を付加する処理が必要になります。

数値のゼロを埋め、桁を揃える

zeroPadding方を応用します。

zeroPadding(index, size) {
  let tmp = index + ''
  while (tmp.length < size) tmp = '0' + tmp
  return tmp
}

まとめる

上記をまとめたコードになります。

index.vue
<template lang="pug">
    section.py-12
        v-layout(column align-center justify-center)
            v-flex(xs12 sm4 class="my-4")
        swiper(:options="swiperOption")
          swiper-slide(v-for="(item, index) in items" :key="index")
            img(:src="item.slideimage" :alt="item.title" :id="item.no")
</template>

<script>
export default {    
    data () {
        const items = []    
        for (let i=1; i<=5; i++) {
      items.push({
        no: i,
                title: 'title' + i,
        slideimage: `/images/home/slide${this.zeroPadding(i, 2)}.jpg`
            })
        }
        return {
            items,
        }
    },
    methods: {
    zeroPadding(index, size) {
      let tmp = index + ''
      while (tmp.length < size) tmp = '0' + tmp
      return tmp
    }
  }
}
</script>

これでファイルの連番については実装できました。
このやり方だと連番はできたのですが個別ごとのテキスト名とかの設定でできないので
複数の項目があるときはjsonで読み込んでv-forで回したほうがいいかもしれませんね。

参考:
数値のゼロ埋め(桁を揃える)
第一回 Vue.jsでWebアプリをつくろう!

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

【個人開発】ゲージの画像を作るサービスをリリースした

個人開発のサービス まいゲージをリリースしました。

ゲージの画像を作るサービスです。

12.png

作るときに考えたこと

やっぱり、無名の人が頑張っても人気にはならないわけで、有名にするにはSNSを使う必要があります。

ツイートボタンとかを用意して、投稿されたツイートがクリックされるように、twitter cardを使いました。

もともと、ダウンロードさせて自分で画像添付にしようかと思ったけど、手間がかかって途中で諦める人が多くなるので却下。
あと、「リンクが広告臭いのがいやだ」って消される可能性もあるし、、

Twitterカードの失敗

Twitterカードのメタタグをつけて、何回ツイートしても、Twitterカードがでませんでした。

理由はカードがBASE64に対応をしていなかったからです。

file_put_contentsを使って画像にしたら、無事表示されました。

使用言語

JavaScript

表示部分は、JSのCanvasを使っています。
ゲージ部は、くけいで表示しています。

PHP

画像を保存したりする部分です。
後々ログイン処理をすることも考えて、全ページphpにしてあります。

ライブラリーなど

jQuery

これメインで使う というよりは、このあと紹介するライブラリで必要なので入ってます。

spectrum

カラーピッカーです。input colorよりも使い勝手が良さそうでした。

サービスが作りたい人へ

質より量です。たくさん作って公開しましょう。
アイデアについてですが、すでにあるもの+α でもそれっぽくなります。

診断メーカー + 絵を書く でできた(と思われる)作品もありますよね。
とりあえず 作ってアップ、作ってアップ を繰り返してください。

ですが、毎回別ドメインを用意してたら財布が泣くので、同じドメインのサブドメインやディレクトリにしましょう。

人気が出てきたら、ドメインをとってそれにリダイレクト。

最後に

ぜひまいゲージ使ってみてください。

クリックするとサイトに飛びます。

最後まで読んでいただき、ありがとうございました。

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