20210411のJavaScriptに関する記事は27件です。

cssだけで電球を作ったので、jsと組み合わせてwebサイトを明るくしてみた

どうも7noteです。電球を作って、サイトを明るく照らしてみた。 全然関係ないですが、市販の電球って、わざと寿命が設定されているらしいですね。 中心部分のぐるぐるのところ(フィラメント)を超ほそくしておくことで、何年かで切れるとか。 なのでフィラメントがそれなりに太い電球を作れば生きてる間は切れない電球とかも作れるのかも。(実際に100年以上ついてる電球があるらしい) 余計な話をはさみつつ、本題のソースもたいして使い道がないものですが、cssだけで電球を作ってみたくなって、さらにクリックで電気付けられたら面白いのでは?というただの思いつきで作ってみました。 HTML・CSS・javascriptだけでゲーム作ります!ってときくらいにしか使えなさそう。 電気屋さんのホームページを作るのにこのアイデア使ってwebサイトとか作ったら面白そうかも。 (探せばすでにそんなサイトあるかも・・・) ソースはかなり汚いかもですがご容赦ください。 ソース index.html <div class="light"> <div class="grass"> <div class="circle"></div> <div class="neck"></div> <div class="filament"><span></span><span></span></div> </div> <div class="screw"><span></span><span></span><span></span></div> </div> style.css body { background: #000; /* 初期状態はまっくらにしておく */ } .light { width: 200px; } /* 電球のガラス部分のcss */ .light .grass { width: 150px; margin: 0 auto; position: relative; } .light .grass .circle { width: 150px; height: 150px; border: 2px solid #111; border-radius: 100%; } .light .grass .neck { width: 60px; height: 20px; margin: -9px auto 0; border-left: 2px solid #111; border-right: 2px solid #111; } /* 電球のフィラメント */ .light .grass .filament { position: absolute; bottom: 40px; left: 40%; } .light .grass .filament span { width: 30px; height: 15px; border-radius: 15px 15px 0 0; border-top: 2px solid #111; position: relative; display: inline-block; margin-left: -10px; } .light .grass .filament span:first-child::after { content: ""; display: block; width: 10px; height: 5px; border-radius: 0 0 5px 5px; border-bottom: 2px solid #111; position: absolute; right: 0; bottom: -6px; } /* 電球のネジ部分 */ .light .screw { width: 70px; margin: 0 auto; position: relative; } .light .screw::after { content: ""; width: 20px; height: 20px; border: 2px solid #111; border-radius: 50%; display: inline-block; position: absolute; left: 50%; bottom: -10px; transform: translateX(-50%); z-index: -1; } .light .screw span { display: block; height: 7px; border: 2px solid #111; border-radius: 10px; } /* カーソルについてくる文字 */ #cursor{ position: fixed; z-index: 2; pointer-events: none; color: #CCC; } /* 明るくなったときの色を指定 */ body.on { background: #fff; } body.on .light .grass .circle { background: #ECD05D; } body.on .light .grass .neck { background: #ECD05D; } body.on .light .screw::after { background: #A5A3A5; } body.on .light .screw span { background: #A5A3A5; } /* 明るくなったらカーソルについてくる文字は消す */ body.on #cursor { display: none; } script.js /* 電気のスイッチ */ $('body').click(function() { $(this).toggleClass("on"); }) /* カーソルについてくる文字 */ $(function(){ var cursor=$("#cursor"); // カーソルになる要素を指定 $(document).on("mousemove",function(e){ // マウスカーソルが動いた時に実行 var x=e.clientX + 10; // カーソルの横座標を取得 var y=e.clientY + 10; // カーソルの縦座標を取得 // 取得した座標をCSSに反映させる cursor.css({ "top":y+"px", "left":x+"px" }); }); }); (※jQueryを使用しています。jQueryってなんだという方はこちら) まとめ 各種パーツの作り方解説 ・ついてくる文字の作り方はこちら ・またフィラメントのぐるぐるの作り方はこちらから 本当は集中線とか付けて明るくなっている感じを作るのも考えたのですが、きれいに書ける方法が思いつかなかったので保留しました。 また思いついたらいろいろ作ってみようと思います。 おそまつ! ~ Qiitaで毎日投稿中!! ~ 【初心者向け】WEB制作のちょいテク詰め合わせ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript ライブラリ無しでできるだけ簡単にゼロパディング済みの年月日時分秒ミリ秒を個別に取得したい

どんな方法があるかな、とちょっと考えてみました。 まず、素直に考えたらこんな感じでしょうか。 const d = new Date(), year = d.getFullYear(), month = ('0' + (d.getMonth() + 1)). slice(-2), day = ('0' + d.getDate()). slice(-2), hour = ('0' + d.getHours()). slice(-2), minute = ('0' + d.getMinutes()). slice(-2), second = ('0' + d.getSeconds()). slice(-2), millisecond = ('00' + d.getMilliseconds()).slice(-3); console.log(new Date().toString()); console.log( year + '年' + month + '月' + day + '日 ' + hour + '時' + minute + '分' + second + '秒' + millisecond); Sun Apr 11 2021 22:57:46 GMT+0900 (日本標準時) 2021年04月11日 22時57分46秒017 ゼロパディング済みの値が欲しい場合、get系メソッドで取得するならslice()やpadStart()を使っての処理が一般的ですね。 get系メソッド以外の方法だと、toISOString()を使うとゼロパディング済みの値をいい感じに切り出せそうです。 const date = new Date(); date.setMinutes(date.getMinutes() - date.getTimezoneOffset()); const _ = date.toISOString().match(/\d+/g), year = _[0], month = _[1], day = _[2], hour = _[3], minute = _[4], second = _[5], millisecond = _[6]; console.log(new Date().toString()); console.log( year + '年' + month + '月' + day + '日 ' + hour + '時' + minute + '分' + second + '秒' + millisecond); Sun Apr 11 2021 22:58:53 GMT+0900 (日本標準時) 2021年04月11日 22時58分53秒871 toISOString()はタイムゾーンがUTC固定なので、戻り値からそのまま数値を文字列として切り出しても時差によるずれが発生しないよう、getTimezoneOffset()で実行環境のタイムゾーンに合わせて補正しています。 IEを無視しても大丈夫なら、もう少し短くなりますね。 const date = new Date(); date.setMinutes(date.getMinutes() - date.getTimezoneOffset()); const [year, month, day, hour, minute, second, millisecond] = date.toISOString().match(/\d+/g); console.log(new Date().toString()); console.log(`${year}年${month}月${day}日 ${hour}時${minute}分${second}秒${millisecond}`); Sun Apr 11 2021 23:00:05 GMT+0900 (日本標準時) 2021年04月11日 23時00分05秒405 変数名なども短くしたいなら const d = new Date(); d.setMinutes(d.getMinutes() - d.getTimezoneOffset()); const p = d.toJSON().match(/\d+/g); console.log(new Date().toString()); console.log(`${p[0]}年${p[1]}月${p[2]}日 ${p[3]}時${p[4]}分${p[5]}秒${p[6]}`); Sun Apr 11 2021 23:00:46 GMT+0900 (日本標準時) 2021年04月11日 23時00分46秒021 こんな感じでしょうか。 getTimezoneOffset()のメソッド名の長さが気になりますが。 toJSON()はDateオブジェクトではtoISOString()のエイリアスと捉えて良さそうですね。 Date.prototype.toJSON() toISOString() を使用して Date を表す文字列を返します。 JSON.stringify() の代わりに使用してください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javascript 備忘録4<関数>

■関数 function a(num) { console.log(num); } // 上の関数名と同じ function a(num) { return num * 2; } →Javascriptの場合は、関数名が被った場合、後に宣言された関数が優先される。 ■コールバック関数 →他の関数に渡して実行する関数のこと // コールバック関数とする function hello() { console.log('hello'); } function fn(cb) { // オブジェクトで渡された関数を実行する cb(); } // オブジェクトとしてhelloの関数を渡す // ()をつけることで関数は実行されるので、渡されただけのhelloは実行されない // 関数fnの中のcb()にhelloオブジェクトが渡されたことにより、hello関数が実行される fn(hello);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascriptでsplitを使ってコンマで分割すると、""で囲まれた内部のコンマまで分割されてしまう。

トラブルの内容 javascriptでsplitを使ってコンマで分割すると、""で囲まれた内部のコンマまで分割されてしまう件 動作環境 Mac OS、Windows10 node v12.15.0 結論 Javaの関数をJavascriptに翻訳することで解決しました。正規表現ではできませんでした。 以下、解決への過程 まず次のWebサイトを参考にしました。 人力検索はてな javascriptでsplitを使ってコンマで分割すると、""で囲まれた内部のコンマまで分割されてしまいます。きれいにCSVに分割する方法を教えてください しかし、このページの回答では、自分のCSVを目的どおりに分割することはできませんでした。自分の場合、以下のようなCSVファイルを分割したいという目的でした。 あいうえお太郎,25歳,"¥1,500",東京都 かきくけこ花子,24歳,"¥1,200",さいたま県 FileMakerで出力したExcel形式のファイルをCSVに変換すると、このように金額の部分にカンマが付くので、明示的にダブルクォーテーションで囲まれます。このようなCSVを分割した場合に"¥1,500"が"¥1と500"に分かれてしまう。というトラブルを解決したかったのですが、このページに書かれている正規表現ではうまくいきませんでした。 このため、上記サイトの正規表現での解決は諦めて別のWebサイトの方法に頼ることにしました。次に参考にしたのはこのページでした。 Qiita CSVファイルにカンマの含まれる項目を分割せずに読み込む(Java) タイトルの通り、Javaで書かれていましたが、このページに書かれているJavaのコードをJavascriptに翻訳して実行したら解決しましたというのが自分の解決方法です。以下、Javascriptのコードを記載します。 /** * CSVの一行を引数に指定すると、 * ""で囲まれた内部のコンマを分割せずに、 * コンマで分割して配列の戻り値を返す関数 * @param {string} line 文字列 * @return {Array<string>} 配列 */ csvSplit(line) { var c = ""; var s = new String(); var data = new Array(); var singleQuoteFlg = false; for (var i = 0; i < line.length; i++) { c = line.charAt(i); if (c == "," && !singleQuoteFlg) { data.push(s.toString()); s = ""; } else if (c == "," && singleQuoteFlg) { s = s + c; } else if (c == '"') { singleQuoteFlg = !singleQuoteFlg; } else { s = s + c; } } return data; } 上記の関数をクラスの中にコピペしてください。 上記csvSplit関数の呼び出し側のコード めんどくさがりやの人のために上記csvSplit関数の呼び出し側のコードも載せておきます。 // CSVで取得したデータを行ごとに配列にしたものをfor文で処理している。 for (var i = 1; i < csvArray.length - 1; i++) { // カンマで区切られた各データに分割する //var csvArrayD = csvArray[i].split(","); // ""で囲まれた内部のコンマまで分割されてしまうのを回避 var csvArrayD = this.csvSplit(csvArray[i]); } 解説をすると、普通はsplit(",")でCSVをカンマで分割して配列に代入するという処理は問題ないのですが、「""で囲まれた内部のコンマを分割せずに」処理するという場合は、this.csvSplit("CSVの行")というように呼び出します。for文が気に入らない人など、ここら辺のコードは、各自の環境・センスに合わせて下さい。 以上です。Good Luck!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascriptでsplitを使ってコンマで分割すると、""で囲まれた内部のコンマまで分割されてしまう件

トラブルの内容 javascriptでsplitを使ってコンマで分割すると、""で囲まれた内部のコンマまで分割されてしまう。 動作環境 Mac OS、Windows10 node v12.15.0 結論 Javaの関数をJavascriptに翻訳することで解決しました。正規表現ではできませんでした。 以下、解決への過程 まず次のWebサイトを参考にしました。 人力検索はてな javascriptでsplitを使ってコンマで分割すると、""で囲まれた内部のコンマまで分割されてしまいます。きれいにCSVに分割する方法を教えてください しかし、このページの回答では、自分のCSVを目的どおりに分割することはできませんでした。自分の場合、以下のようなCSVファイルを分割したいという目的でした。 あいうえお太郎,25歳,"¥1,500",東京都 かきくけこ花子,24歳,"¥1,200",さいたま県 FileMakerで出力したExcel形式のファイルをCSVに変換すると、このように金額の部分にカンマが付くので、明示的にダブルクォーテーションで囲まれます。このようなCSVを分割した場合に"¥1,500"が"¥1と500"に分かれてしまう。というトラブルを解決したかったのですが、このページに書かれている正規表現ではうまくいきませんでした。 このため、上記サイトの正規表現での解決は諦めて別のWebサイトの方法に頼ることにしました。次に参考にしたのはこのページでした。 Qiita CSVファイルにカンマの含まれる項目を分割せずに読み込む(Java) タイトルの通り、Javaで書かれていましたが、このページに書かれているJavaのコードをJavascriptに翻訳して実行したら解決しましたというのが自分の解決方法です。以下、Javascriptのコードを記載します。 /** * CSVの一行を引数に指定すると、 * ""で囲まれた内部のコンマを分割せずに、 * コンマで分割して配列の戻り値を返す関数 * @param {string} line 文字列 * @return {Array<string>} 配列 */ csvSplit(line) { var c = ""; var s = new String(); var data = new Array(); var singleQuoteFlg = false; for (var i = 0; i < line.length; i++) { c = line.charAt(i); if (c == "," && !singleQuoteFlg) { data.push(s.toString()); s = ""; } else if (c == "," && singleQuoteFlg) { s = s + c; } else if (c == '"') { singleQuoteFlg = !singleQuoteFlg; } else { s = s + c; } } return data; } 上記の関数をクラスの中にコピペしてください。 上記csvSplit関数の呼び出し側のコード めんどくさがりやの人のために上記csvSplit関数の呼び出し側のコードも載せておきます。 // CSVで取得したデータを行ごとに配列にしたものをfor文で処理している。 for (var i = 1; i < csvArray.length - 1; i++) { // カンマで区切られた各データに分割する //var csvArrayD = csvArray[i].split(","); // ""で囲まれた内部のコンマまで分割されてしまうのを回避 var csvArrayD = this.csvSplit(csvArray[i]); } 解説をすると、普通はsplit(",")でCSVをカンマで分割して配列に代入するという処理でも問題ないのですが、読み込むCSVの内容によっては、「""で囲まれた内部のコンマを分割せずに」処理しないといけない場合があります。このような場合は、this.csvSplit("CSVの行")というように上記の関数を呼び出して実行することで解決できます。for文が気に入らない人など、ここら辺のコードについては、各自の環境・センスに合わせて修正して下さい。 以上です。Good Luck!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript canvasで線を引く

index.html <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <canvas id="sample" width="500" height="2000"></canvas> <script src="main.js"></script> </body> </html> main.js const draw = () => { const xValue = 10; const yValue = 60; const xMove = 200; const textXaxis = 40; const textYaxis = 20; const canvas = document.getElementById('sample'); const ctx = canvas.getContext('2d'); const width = canvas.getAttribute('width'); const height = canvas.getAttribute('height'); ctx.clearRect(0, 0, width, height); ctx.font = "20px arial"; ctx.fillText('Shuffle Alphabet', textXaxis, textYaxis); const aTozArray = range('A'.charCodeAt(0), 'Z'.charCodeAt(0), 1).map(x => String.fromCharCode(x)); const array = Array.from({length: aTozArray.length}, (v, i) => i); const randomArray = shuffle(array); ctx.beginPath(); for (let i = 0; i < aTozArray.length; i++) { ctx.fillText(aTozArray[i], xValue, yValue * (i + 1)); ctx.fillText(aTozArray[i], xValue + xMove, yValue * (i + 1)); ctx.moveTo(xValue, yValue * (i + 1)); ctx.lineTo(xValue + xMove, yValue * (parseInt(randomArray[i], 10) + 1)); } ctx.stroke(); } const shuffle = (array) => { for(let i = array.length - 1; i > 0; i--){ let r = Math.floor(Math.random() * (i + 1)); let tmp = array[i]; array[i] = array[r]; array[r] = tmp; } return array } const range = (start, stop, step) => Array.from({ length: (stop - start) / step + 1}, (_, i) => start + (i * step)); draw() 参考記事
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Blob と File と TypeScript と私

Blob は JavaScript で生データを扱う際に使用するインタフェース(オブジェクト型)です。 一方、File はファイルについての情報(ファイル名、最終更新時...)を持っていて、Blob インタフェースを継承しています。 MDN では以下のように記述されています: File オブジェクトは特別な種類の Blob オブジェクトであり、 Blob が利用できる場面ではどこでも利用できます。特に、FileReader、URL.createObjectURL()、createImageBitmap() 、XMLHttpRequest.send() は、Blob と File の両方を受け付けます。 File インターフェイスはメソッドを定義せず、Blob インターフェイスからメソッドを継承しています。 実際のところ、Blob インタフェースとの違いは以下の4つの読み取り専用プロパティを持っているかどうかということになります。 File.prototype.name File.prototype.lastModified File.prototype.lastModifiedDate(非推奨) File.prototype.webkitRelativePath(標準外) TypeScript組み込みの型定義情報では、Blob のプロパティ・メソッドに加え標準・推奨の2プロパティを持っていれば File という判定になっています。 typescript/lib/lib.dom.d.ts /** Provides information about files and allows JavaScript in a web page to access their content. */ interface File extends Blob { readonly lastModified: number; readonly name: string; } なるほど、何等かの事情1で Blob から File にキャストしたい場合は、lastModified と name を補ってしまえばよさそうに見えます。 いつわらないでいて ブラウザの指摘は鋭いもの 以下のようなシンプルな例で試してみましょう。 function read(file: File): void { const reader = new FileReader(); reader.readAsDataURL(file); } // 実際には適当なデータを格納する const blob = new Blob(); File 型のオブジェクトを受け取り、FileReader.readAsDataURL(blob: Blob): void で読み取るだけの read 関数と、Blob 型のオブジェクトを用意します。 それでは、read() に blob を与えてみましょう: read(blob) // 型 'Blob' の引数を型 'File' のパラメーターに割り当てることはできません。 // 型 'Blob' には 型 'File' からの次のプロパティがありません: lastModified, name コンパイルエラーになりました。型が違うので当然ですね。ここで、エラーメッセージに従い、プロパティを補ってあげましょう: read({...blob, lastModified: 0, name: "name"}); // コンパイルエラーにならない 型定義に合致しているので、コンパイルエラーにはなりません。しかしこのコードをブラウザ上で実行すると、'readAsDataURL' parameter 1 is not of type 'Blob' として怒られてしまいます。開発者ツールで見ても File オブジェクトではなく単なる Object だと認識されています。 // 本当に有効なコード! read(new File([blob], "name", {lastModified: 0})); ということで、ちゃんと File() コンストラクタを通してあげると実行時エラーにはなりません。追加している情報は同じなのですが、横着をしてはいけませんね。 気が付いてみるとしょうもないことなのですが、エラーメッセージで検索しても事例が見つかりづらいため、記事として残しておきました。 愛する型定義のため コンパイルエラーでいさせて 今回の例の場合は readAsDataURL() が Blob 型を引数に取るため、as で無理やり型アサーションをしても問題なく動作します。 // 型アサーションでお茶を濁す read(blob as File); とはいえ lastModified や name に依存する処理が read() にあった場合は実行時エラーになってしまいます。Blob ではなく File 型の引数を求めている以上、想定されるべき事態でしょう。 参考リンク https://developer.mozilla.org/ja/docs/Web/API/Blob https://developer.mozilla.org/ja/docs/Web/API/File たとえば Blob しか吐き出さない関数から、File しか受け付けない関数にデータを受け渡したい場合など。上述のように、多くの組み込み関数は Blob 全体を受け付けてくれるので、あてはまりません。 ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

5歳娘「パパ、余分なpropsいっぱい書くんだね!」

とある平日 娘(5歳)「パパ、今日は何のお仕事してるの?」 ワイ「おお、娘ちゃん」 ワイ「今日はな、ショッピングサイトを作ってんのや」 今日のお仕事内容 ワイ「↓このデザインの通りに、コーディングをせなあかんのや」 娘「なるほどー」 娘「このショッピングサイトで商品を売りたい!っていうお店があったとして」 娘「そのお店の人が、最初にお店の情報を登録するためのページだね!」 ワイ「せやせや」 まずはデザインを眺めてみる ワイ「この店舗登録ページにはなぁ」 ワイ「↑こんな感じの」 ワイ「項目名と入力欄がセットになったパーツが何度も登場するから」 ワイ「そのためのコンポーネントを作ろうかなー、って」 ワイ「そう思ってたとこなんや」 娘「ふーん」 娘「ラベル付きテキストフィールド的なコンポーネントってことだね」 娘「どこまで作ったの?」 ワイ「まだ、ページ側のファイルを作っただけや」 ワイ「↑これだけや」 /pages/form.tsx import React from 'react' const FormPage: React.FC = () => { return ( <> <h1>店舗登録</h1> <p>店舗情報を入力してください。</p> </> ) } export default FormPage ワイ「↑コードもまだこんだけや」 娘「なるほどね」 ワイ「ほな今から、ラベル付きテキストフィールドのコンポーネントを作っていくで!」 コンポーネント作り開始 ワイ「まずは・・・」 /components/LabeledInput.tsx export const LabeledInput: React.FC<Props> = (props) => { return ( <label> <span>項目名</span> <input type="text" name="項目名" /> </label> ) } ワイ「↑こんな感じや」 ワイ「これをページ側で呼び出すには・・・」 /pages/form.tsx import React from 'react' + import { LabeledInput } from 'src/components/LabeledInput' const FormPage: React.FC = () => { return ( <> <h1>店舗登録</h1> <p>店舗情報を入力してください。</p> + <LabeledInput /> + <LabeledInput /> + <LabeledInput /> + <LabeledInput /> + <LabeledInput /> </> ) } export default FormPage ワイ「↑こうやな」 ワイ「こうすると、画面の方は・・・」 ワイ「↑こうやな」 娘「わー、ちゃんと表示されたね」 ワイ「おお」 娘「でも、ラベル部分が全部項目名になっちゃってるよ?」 ワイ「大丈夫や」 ワイ「初期リリース時はその要件でOKなはずや」 娘「そんなわけないからprops書こうよ」 ワイ「せやな」 ワイ「ワイが会社からリリースされてしまうところやったわ」 まずはpropsの型を書く ワイ「ほんなら」 ワイ「input要素に渡すtypeとかnameと」 ワイ「あと、ラベル文字列もpropsで渡したいから」 ワイ「labelTextってのも定義しておこか」 /components/LabeledInput.tsx import React from 'react' - type Props = {} + type Props = { + type: string + name: string + labelText: string + } export const LabeledInput: React.FC<Props> = (props) => { return ( <label> <span>項目名</span> <input type="text" name="項目名" /> </label> ) } ワイ「↑propsの型はこんな感じやな」 ワイ「そんで、親コンポーネントからもらってきたpropsを」 ワイ「子コンポーネントの中で表示せなあかんから・・・」 /components/LabeledInput.tsx import React from 'react' type Props = { type: string name: string labelText: string } export const LabeledInput: React.FC<Props> = (props) => { + const { type, name, labelText } = props + return ( <label> - <span>項目名</span> - <input type="text" name="項目名" /> + <span>{labelText}</span> + <input type={type} name={name} /> </label> ) } ワイ「↑こうやな!」 娘「なるほど〜」 娘「ページの方からは、このコンポーネントにどうやってpropsを渡すの?」 ワイ「ページの方は・・・」 /pages/form.tsx import React from 'react' import { LabeledInput } from 'src/components/LabeledInput' const FormPage: React.FC = () => { return ( <> <h1>店舗登録</h1> <p>店舗情報を入力してください。</p> - <LabeledInput /> - <LabeledInput /> - <LabeledInput /> - <LabeledInput /> - <LabeledInput /> + <LabeledInput type="text" name="shopName" labelText="店舗名" /> + <LabeledInput type="text" name="repName" labelText="代表者名" /> + <LabeledInput type="text" name="repName" labelText="業種" /> + <LabeledInput type="text" name="repName" labelText="郵便番号" /> + <LabeledInput type="text" name="repName" labelText="住所" /> </> ) } export default FormPage ワイ「↑こんな感じや」 ワイ「こうすると画面は・・・」 ワイ「↑こうやな」 娘「わーい!ほぼ完成だね!」 ワイ「いや、全然まだまだやで」 娘「そうなの?」 propsをどんどん追加していく ワイ「input要素にdisabled属性をつけたり」 ワイ「onClickで関数を渡したり」 ワイ「そういうpropsがまだまだ全然足りてへん」 娘「確かに」 ワイ「せやから、MDNのページとかを見ながら」 ワイ「必要そうなpropsを全部書いていくんや・・・!!!」 /components/LabeledInput.tsx import React from 'react' type Props = { type: string name: string + placeholder: string + autoComplete: string + disabled: boolean + required: boolean + minLength: number + maxLength: number + onChange: React.ChangeEventHandler<HTMLInputElement> + onBlur: React.FocusEventHandler<HTMLInputElement> labelText: string } export const LabeledInput: React.FC<Props> = (props) => { - const { type, name, labelText } = props + const { + type, + name, + value, + placeholder, + autoComplete, + disabled, + required, + minLength, + maxLength, + onChange, + onBlur, + labelText, + } = props return ( <label> <span>{labelText}</span> - <input type={type} name={name} /> + <input + type={type} + name={name} + value={value} + placeholder={placeholder} + autoComplete={autoComplete} + disabled={disabled} + required={required} + minLength={minLength} + maxLength={maxLength} + onChange={onChange} + onBlur={onBlur} + /> </label> ) } ワイ「↑こんなもんや!!!!」 ワイ「(ドヤァ・・・・)」 ワイ「(パパはいつも、こんな大変なお仕事をしてるんやでぇ・・・!?)」 5歳娘「パパ、余分なpropsいっぱい書くんだね!」 ワイ「ファッ!?」 5歳児の書き方 娘「パパ」 娘「input要素の属性一覧を、一生懸命ネットで調べるのもいいんだけどさ」 /components/LabeledInput.tsx type InputProps = JSX.IntrinsicElements['input'] 娘「↑これだけでいいんだよ」 ワイ「ファーーー・・・(失神)」 娘「これだけで、input要素の属性一覧の型情報を取得できるの」 娘「つまり、propsの型定義の部分は」 /components/LabeledInput.tsx - type Props = { - type: string - name: string - placeholder: string - autoComplete: string - disabled: boolean - required: boolean - minLength: number - maxLength: number - onChange: React.ChangeEventHandler<HTMLInputElement> - onBlur: React.FocusEventHandler<HTMLInputElement> - labelText: string - } + type InputProps = JSX.IntrinsicElements['input'] + type Props = InputProps & { labelText: string } 娘「↑この2行で済むわけだね」 ワイ「ぐぬぬ・・・」 ワイ「あ、ありがとうやで娘ちゃん・・・」 ワイ「だいぶコードがスッキリしたわ・・・」 娘「え、まだまだ余分なところがいっぱいあるよ?」 ワイ「ファファファ・・・」 propsはスプレッド構文で渡せる 娘「propsはね、スプレッド構文を使うことで・・・」 /components/LabeledInput.tsx <input {...props} /> 娘「↑こんな感じで一気に渡せるの」 ワイ「おお・・・」 娘「つまり、さっきのコンポーネントのコードは・・・」 /components/LabeledInput.tsx export const LabeledInput: React.FC<Props> = (props) => { - const { - type, - name, - value, - placeholder, - autoComplete, - disabled, - required, - minLength, - maxLength, - onChange, - onBlur, - labelText, - } = props + const { labelText } = props return ( <label> <span>{labelText}</span> - <input - type={type} - name={name} - value={value} - placeholder={placeholder} - autoComplete={autoComplete} - disabled={disabled} - required={required} - minLength={minLength} - maxLength={maxLength} - onChange={onChange} - onBlur={onBlur} - /> + <input {...props} /> </label> ) } 娘「↑こうまとめられちゃうね!」 ワイ「......シテ......コロシテ......」 しかし、このままだとlabelTextもinput要素に渡ってしまう 娘「でもね、これだけだと」 娘「ページ側から渡されたlabelTextっていうpropsも」 娘「まとめてそのままinput要素に渡されてしまうの」 娘「そうすると・・・」 Warning: ReactはDOM要素のlabelTextというpropを認識しません。 意図的にカスタム属性としてDOMに表示させたい場合は、代わりに小文字の labeltext と綴ります。 誤って親コンポーネントから渡してしまった場合は、DOM要素から削除してください。 娘「↑こんな警告がコンソールに出ちゃうの」 ワイ「なるほどな」 娘「だからlabelTextとそれ以外を分割して」 娘「必要なものだけinput要素に渡したいわけ」 ワイ「そのやり方を教えてください(観念)」 娘「はい」 /components/LabeledInput.tsx export const LabeledInput: React.FC<Props> = (props) => { - const { labelText } = props + const { labelText, ...inputProps } = props return ( <label> <span>{labelText}</span> - <input {...props} /> + <input {...inputProps} /> </label> ) } 娘「↑こうだね」 ワイ「おお、なるほど」 /components/LabeledInput.tsx const { labelText, ...inputProps } = props ワイ「↑こう、分割代入と残余構文を使うことで」 ワイ「labelTextとそれ以外に分割してやるんか」 娘「そう」 娘「こないだ@suinさんが似た感じのことをやってたのを、さっき思い出したの」 ワイ「おお、いつもお世話になっております・・・」 最終的に 娘「ってことで、パパの書いたコンポーネントのコードは」 /components/LabeledInput.tsx import React from 'react' type InputProps = JSX.IntrinsicElements['input'] type Props = InputProps & { labelText: string } export const LabeledInput: React.FC<Props> = (props) => { const { labelText, ...inputProps } = props return ( <label> <span>{labelText}</span> <input {...inputProps} /> </label> ) } 娘「↑こんな感じに変わったね!」 ワイ「いやほぼ全部変わっとるやないかい!!!」 まとめ input要素の属性の型は、まとめて取得できる → JSX.IntrinsicElements['input'] propsを渡すとき、スプレッド構文を使って一気に書ける → <input {...props} /> propsを何かとそれ以外に分けたい場合は、分割代入&残余構文で書く → const { labelText, ...inputProps } = props ワイ「ってことやな!」 娘「そうだね!」 その日の夜 ワイ「娘ちゃんは凄いな〜」 ワイ「5歳なのにあんなコードが書けるなんて」 ワイ「もしかして、もう算数とかもできるんちゃうか?」 娘「できるよ!」 娘「パパ、問題出してみて!」 ワイ「ええで!」 ワイ「ほな行くで〜」 ワイ「りんご1個 + みかん1個、合わせていくつ?」 娘「えぇ〜と・・・」 娘「わかった!」 娘「答えは、"りんご1個みかん1個"!」 ワイ「いや文字列結合!」 ワイ「JavaScriptのやり過ぎか!!!」 〜おしまい〜 余談
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

imgタグにImageFluxを適用するjQueryプラグインを作った

ピクシブ株式会社とさくらインターネット株式会社が開発した画像変換配信サービス ImageFlux をimgタグに簡単に適用できるjQueryプラグインを作ってみました。 ImageFluxの仕組み ImageFluxにはオリジンサーバを設定し、それに対応したドメインが割り当てられます。CDNに似てますね。 ImageFluxのドメインに対して変換パラメータ付きで画像のパスを指定することで、変換パラメータ通りに変換され、キャッシュされます。 最も簡単で効果的な使い方はWebPへの変換でしょう。 例えば、オリジンが https://www.example.com/、 対応するImageFluxのドメインが p1-XXXXXXXX.imageflux.jp の場合、imgタグを以下のようにすることで、WebPに対応したブラウザに対してWebPで画像を配信することができます。 変更前 <img src="https://www.example.com/images/some-image.jpg"/> 変更後 <img src="https://p1-XXXXXXXX.imageflux.jp/f=webp:auto/images/some-image.jpg"/> ImageFluxではいろいろと変換パラメータを指定することができます。詳しくはドキュメントをご覧ください。 (フォーマット webp:auto は、ブラウザがWebPに対応している場合はWebP、対応していない場合はオリジナル画像のフォーマットで出力されるオプションです。) jQuery ImageFlux Plugin 上記のようなパラメータをsrc属性に指定するのはめんどくさいので、まとめて指定できるjQueryプラグインを作りました。 github.com/chibiegg/jquery-imageflux 詳しい使い方はREADME.mdに書きましたが、簡単に紹介したいと思います。 imgタグの src 属性の代わりに、data-imgflx-src属性に画像へのパスを指定し、プラグインを実行するという形になります。 変換パラメータはプラグインへのオプション辞書として渡すか、imgタグのdata属性として渡すことができます。 例えばこんな感じの使い方になります。 JavaScript <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script> <script type="text/javascript" src="jquery.imageflux.min.js"></script> <script type="text/javascript"> $(function(){ $('img[data-imgflx-src]').imageflux({ host: 'p1-XXXXXXXX.imageflux.jp', format: 'webp:auto', allowUpscale: false, width: 300 }); }); </script> HTML <img data-imgflx-src="/images/some-image.jpg" /> <img data-imgflx-src="/images/awsome-image.jpg" data-imgflx-width="500" data-imgflx-quality="60" /> この例では、オプション辞書により、フォーマットは webp:auto、拡大はしない、幅は300px、とし、2つめの画像では幅と品質をdata属性で上書きしています。 このようにすることで、以下のようなsrc属性と同様の効果が得られます。 変換後 <img src="https://p1-XXXXXXXX.imageflux.jp/f=webp:auto,u=0,w=300/images/some-image.jpg" /> <img src="https://p1-XXXXXXXX.imageflux.jp/f=webp:auto,u=0,w=500,q=60/images/awsome-image.jpg" /> さいごに 現在ImageFluxは問い合わせ対応によってサービス提供がされていますが、簡単に利用できるようにしたいですね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptとJavaくらべてみました

JavaScriptとJavaをちょいちょい比較しつつJavaScriptの特徴をまとめていきます JavaScriptとJavaは関係しているの? 結論、JavaScriptとJavaは全く別物です! JavaScriptとは 最大の特徴はWebブラウザ上で動作する  例えばクライアントの画面(HTMLやCSS)やブラウザの機能(ポップアップや戻るボタン)を自由に操ることができ、動きのあるWebページが作成可能 Webブラウザ上で動作し、サーバとの通信が発生しない  ページ内で完結する動作を組むとき JavaScript  ページを跨ぐ動作を組むとき Java  というイメージを持つとよいでしょう! scriptタグを使ってHTMLに埋め込む  scriptタグはheadタグ直下かbodyタグ直下に記述(※どちらでもOK、どちらにもメリットデメリットがある)  HTML同様JavaScript処理も同様に1行ずつ上から順に読み込み&実行していく  この1行ずつ読み込んで実行していく実行方式をインタプリンタという  Javaはインタプリンタ方式ではありません、Javaはファイルごとコンパイルして実行していく  なのでJavaScriptとJavaは処理の方式が違う。 JavaScriptの文法 変数(プリミティブ) 変数の扱いはJavaとはかなり違います! JavaScriptは動的型付け言語という変数に型を持たない言語です ※変数というデータを入れるための箱はあるが、数値型でも文字型でも論理型でもどんな型でも入れることができる不思議な箱ですね 変数宣言方法 var 変数名 ・・・ 通常の変数宣言 let 変数名 ・・・ ローカル変数(スコープ内のみ有効)の宣言 const 変数名・・・ 読み取り専用定数の宣言(※finalとおなじ) 変数(オブジェクト) 基本的にオブジェクトはJavaとおなじイメージでOK→複数の変数と複数のメソッドをまとめて置ける JavaScriptでは変数はプロパティと呼ばれる(※Javaではフィールド) データ型 ※変数にはなんでも入る箱ですが、データ自体についはちゃんと型が用意されています ※typeof演算子を用いると変数内のデータ型を確認できる Number型 ・・・ 整数、小数点、Infinity(無限)etc String型  ・・・ 文字列 Boolean型 ・・・ true/false Null型   ・・・ null Undefined型・・・ undefined(変数宣言時、とりあえず設定される初期値) Symbol型 ・・・ 識別子 文字リテラル 文字列の囲いは「’ ’」「” ”」どちらでも可 ※Javaでは「" "」 コメント JavaScriptのコメントはJavaとおなじで「//」「/* */」を用いる スコープ JavaScriptのスコープは  ・グローバルスコープ ・・・ 関数の外(scriptタグ直下)  ・ローカルスコープ  ・・・ 関数の中  ※JavaScriptでもifやforなどの制御構文はあるがスコープとしてみなされない Javaは  ・ifの後の{ }、forの後の{ }などの制御構文内 デバック ブラウザ上で動作する言語であり、挙動のチェックもブラウザ上 デバックもブラウザで提供されているデバックツールを用いることが一般的 例)Google Chromeのディベロッパーツール 関数(function) JavaにおけるメソッドはJavaScriptでは関数(function)と呼ばれます   function関数名( 引数 ) { まとめたい処理 } Javaと違い戻り値に関する定義がありませんが、処理中にreturn文を書けば戻り値が返る ※返すデータが何でも入る箱に入るので型を指定する意味が無いため型を指定しないだけ ※定義された関数は、HTML全体の解析時に先に読み込まれているため定義されている行より上の処理からでも利用できる 関数オブジェクトと無名関数 JavaScriptでは関数をオブジェクトとして扱うことが可能(関数オブジェクト)、つまり変数に入れることができる   var 関数名 = function( 引数 ) { まとめたい処理 };   ※関数の実行時は、関数名(引数)というように引数情報が必要 この右辺で作成されたオブジェクト(関数名なしの関数を右辺で作成している)を名前がない関数、無名関数と言います //注意// 関数定義されたものは、事前に読み込まれているので初めからあるものとして扱えるが、無名関数は定義された行で生成されるため、この行より上の処理からは利用できません! JavaScriptにおけるオブジェクトとは Javaでは変数とメソッドがあるが、 JavaScriptにおけるオブジェクトは全て変数になる。 ・変数の中身が問題で、中身が関数オブジェクトであればメソッドとして扱う ・中身が関数オブジェクト以外(データ等)であればプロパティとして扱う(Javaで言うフィールド) オブジェクトの直下は変数しかない。 そして変数の中身が関数オブジェクトかそれ以外で扱いが変わる DOM(Document Object model) DOM/DOMツリー ・HTMLやXMLといったWebページを構成する文書情報をアプリケーションから操作するために用意されたAPIをDOMと言う ※JSというプログラムからHTMLという画面情報をいじくるための橋渡しとなるツール、DOMを介して画面情報をいじくれる DOMツリー・・・HTMLの情報をオブジェクトとして表現したもの       ツリーと言うのは階層構造をもった樹形図のようなもの       一つ一つの要素をNode(ノード)と呼ぶ       Nodeの中でも文書内のタグ情報と連動しているものはElement(エレメント)といい、       JavaScriptから操作することになるのは基本的にElement(エレメント)になります ※Elementの階層構造は文書のタグ構成と一致しており、htmlを頂点としてその下にheadとbody、headの下にはtitleなど、bodyの下にh1やformなどで構成されます ・ブラウザ全ての機能と情報をアプリケーションから操作可能とするオブジェクトをブラウザオブジェクトと言う  DOMツリーはその中でも画面表示情報を管理するdocumentオブジェクトの直下に構築されていく ブラウザオブジェクトとDOMツリー プログラムで操作できるようブラウザの全ての情報をオブジェクト化したものを ブラウザオブジェクトと言う 具体的にどうやっ使っていくかというと・・・ 例えばalertメソッドを使ってアプリケーションからダイアログの表示をさせたい場合はwindow.alert()と書きます 「window.」は省略可能のため、 alert() だけでも大丈夫です Windowオブジェクトの直下にはブラウザの機能ごとに複数のオブジェクトが存在します 例) history(履歴)/location(URL)/document(表示情報)/ screen(モニタのサイズ)/navigator(利用状況) point DOMツリーはブラウザオブジェクトであるdocumentオブジェクトの直下に構築されている DOMの操作 Elementは文書内のタグの情報をプロパティとして持つオブジェクトです  このプロパティの値を書き換えるとブラウザ上の情報に即時反映される  例えばinputタグのプロパティ「value」は入力値を管理する変数ですが、このプロパティに文字列を代入する処理を書いておくと実行時にテキストボックスへと入力される Elementのプロパティを書き換えるためにはまずElementにアクセスする必要がある  アクセスする方法はいくつかありますが、まず覚えていただきたいのがid属性を用いたアクセス法です  id属性はElementを一意に特定するための目印のようなもので、 DOMツリー全体で重複して設定することが許されていません 以下のようにgetElementByIdメソッドで取得したいElementの id属性を指定すると該当のElementオブジェクトを取得できる javaScript.js document.getElementById( "取得したいElementのid属性" ) ※このオブジェクトは参照型変数なので、取得したオブジェクト内の プロパティを書き換える=大元のDOMツリーの内容を書き換えるということになります ※他では例えばname属性を用いたアクセス法などもありますが、 name属性はフォームによるサーバーとの通信で用いるもの、HTML操作はそれ以外の要素(特に初心者はid属性)で行う方がベターと思っておくと混乱せずに 済むかもしれません。 〜コード例〜 HTML.html <p>ユーザーID: <input type="text" name="USER_NAME" maxlength="20" id="USER_ID"> </p> <p>パスワード:  <input type="password" name="PASSWORD" maxlength="20" id="PASSWORD_ID"> </p> JavaScript.js var elmSubmit = document.getElementById("ID_SUBMIT"); elmSubmit.onclick = function(){ var elmUserId = document.getElementById("USER_ID"); var elmPassword = document.getElementById("PASSWORD_ID"); var canSubmit = true; if(elmUserId.value == "" || elmPassword.value == ""){ alert("入力漏れの項目があります。"); canSubmit = false; } return canSubmit; } イベントハンドラ ブラウザ上では以下のような様々な出来事が起きます ・ページの読み込みが完了した ・フォームを送信しようとした ・ユーザーがボタンの上にマウスを乗せた ・ユーザーがウィンドウを閉じた ・エラーが発生した etc... こうした出来事のことをイベントと言い、イベントを検知した際に 実行される処理のことをイベントハンドラと言います イベントハンドラは以下のようなイベントに対応したプロパティが用意されており、これを検知した後に実行させたい関数オブジェクトを仕込むことで設定完了となる ・ページの読み込みが完了した → onLoad ・フォームが送信された → onSubmit ・ユーザーがボタンの上にマウスを乗せた → onMouseOver ・ユーザーがウィンドウを閉じた → onUnload ・エラーが発生した → onError JavaScriptの外部ファイル化 JavaScript、HTMLのファイルを分けて作成する 分離することで様々なメリットがあります! ・HTML部隊とJavaScript部隊で並行して開発することができる ・JavaScript処理実行中にエラーが起きた場合JavaScriptファイルだけ確認すれば良い ・管理の意味でもメンテの意味でも開発効率の意味でもいろんな意味でいいことがある
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LINE bot を作ってみた

何番煎じかわからんけど、興味が湧いたの作ってみました 環境 Google Spread Sheet Google Apps Script LINE Developers 経緯 とりあえず LINE Developers に登録したけど、サーバーを一から作るのはめんどくさい GAS でできないか?と思い立って調べてみたら、多くの先人が同じ道を通っていた じゃあ少し前に流行ったズンドコキヨシを作ってみよう 手順(概要) LINE Developers 登録 LINE Developers 設定 GAS 構築 Google Spread Sheet 公開 LINE Developers に Webhook URL を登録 動作確認 手順(詳細) 1. LINE Developers 登録 酔っ払いながら登録したので、特に調べずにできるはず ここから登録 2. LINE Developers 設定 https://developers.line.biz/console/channel/{id}/ でトークンを発行する [MessagingAPI設定] > [チャネルアクセストークン] の「発行」を押下 https://manager.line.biz/account/{account_id} ページでメッセージに関する設定する [応答メッセージ] にあるデフォルトメッセージを disable [トークルーム管理] > [あいさつメッセージ] を編集 以下のようなリッチメッセージを作成 3. GAS 構築 以下にソースを置きました https://github.com/soso555BoBs/linebot_with_gas 4. Google Spread Sheet 公開 エディタ右上 [デプロイ] > [新しいデプロイ] 注意点 Google アカウントでのログインを求められた場合、ログイン後の画面で「詳細」リンクを押して承認する必要がある 5. LINE Developers に Webhook URL を登録 Google Spread Sheet を公開した際に URL が発行される 発行された URL を LINE Developers Console の [MessagingAPI設定] > [Webhook URL] に入力 [Webhook の利用] を有効化 6. 動作確認 動いた! 感想&注意点 GAS のデバッグ実行時に、関数への引数を注入する方法がわからなかった… ので、デバッグする時には引数にデフォルト値を設定して実行してました もっと良い方法は無いのだろうか GAS 修正後に WEB アプリケーションに反映させたい場合は以下手順が必要になる エディタ右上の [デプロイ] > [デプロイを管理] 鉛筆マークを押して編集画面を開く [バージョン] で「新しいバージョン」を選択 [デプロイ] ボタンを押す 実行時エラーのログを見る方法がわからない… グワーっと実装して動作確認しながら微調整していたので、エラーが発生しても原因がわからず結構口数がかかってしまった LINE Developers への登録含めて、おそらく 4 時間くらいかかった とりあえず動かすことが目的なので、コピーコードも多くて恥ずかしい
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

書きながら覚えるJavaScript

自分用勉強メモ 随時更新中 配列 const characterName = ['Luffy','Zoro','Nami','Usopp','Sanji']; console.log(characterName[0]); // >> Luffy index(0から開始する数字)で管理 配列[index]で参照 オブジェクト const character1 = { name: 'Luffy', age: 19, from: 'EastBlue' }; const character2 = { name: 'Zoro', age: 21, from: 'EastBlue' }; const character3 = { name: 'Nami', age: 20, from: 'EastBlue' }; const character4 = { name: 'Usopp', age: 19, from: 'EastBlue' }; const character5 = { name: 'Sanji', age: 21, from: 'NorthBlue' }; const character6 = { name: 'Chopper', age: 17, from: 'GrandLine' }; const character7 = { name: 'Robin', age: 30, from: 'WestBlue' }; const character8 = { name: 'Franky', age: 36, from: 'SouthBlue' }; const character9 = { name: 'Brook', age: 90, from: 'WestBlue' }; const character10 = { name: 'Jimbei', age: 46, from: 'FishManIsland' }; const characters = [character1, character2, character3, character4, character5, character6, character7, character8, character9, character10]; key: value の形で定義 オブジェクト.key または オブジェクト[key]で参照 クラス class Character { // クラス宣言 // クラスの中身を定義 constructor(name,age,home){ this.name = name; this.age = age; this.home = home; } } const character1 = new Character('Luffy','19','EastBlue'); const character2 = new Character('Zoro','21','EastBlue'); const character3 = new Character('Nami','20','EastBlue'); const character4 = new Character('Usopp','19','EastBlue'); const character5 = new Character('Sanji','21','NorthBlue'); const character6 = new Character('Chopper','17','GrandLine'); const character7 = new Character('Robin','30','WestBlue'); const character8 = new Character('Franky','36','SouthBlue'); const character9 = new Character('Brook','90','WestBlue'); const character10 = new Character('Jimbei','46','FishManIsland'); console.log(character1.name); // >> Luffy for文 for (初期化式 ; 条件式 ; 加算式 ){ // 処理内容を記載 } for文の色々な例 let devilFruit = ['gomu','gomu','gomu','hana','hana','hito']; let gomu = 0; for(let count = 0 ; count < devilFruit.length ; count++ ){ if(devilFruit[count] === 'gomu') { gomu++; } } console.log(gomu); // >>3 // 3. 該当する一味をpushするための空配列を作る let strawHatArray = []; // 1. charactersの配列の数だけ繰り返すfor文を作成 for (let count = 0; count < characters.length ; count++){ // // 2. 順に取り出せていることを確認 // console.log(characters[count].name); // 4. 抽出対象の条件式を作成 if(characters[count].age >= 20 && characters[count].home !== 'EastBlue') { // // 5. 対象者を順に取り出せていることを確認 // console.log(characters[count].age + ':' + characters[count].from); // 6. 空配列に対象者をpush strawHatArray.push(characters[count]); } } // 対象者の名前を抽出 for (let count = 0; count < strawHatArray.length ; count++){ console.log(strawHatArray[count].name); } forEach文 let strawHatArray = [] characters.forEach(element => { // // elementの名前を表示 console.log(element.name); if(element.age > 20 && element.home !== 'EastBlue') { strawHatArray.push(element); } }); // 一人目を抽出 console.log(strawHatArray[0].name); // >> Sanji // 対象者の名前のみを順に抽出 strawHatArray.forEach(element => { console.log(element.name); }); // >> Sanji // >> Robin // >> Franky // >> Brook // >> Jimbei 関数 関数宣言からアロー関数への変換 // 元の関数宣言 function getTriangleArea(base,height){ const area = base * height / 2; return area; } // 1. function を const にする const getTriangleArea(base,height) { const area = base * height / 2; return area; } // 2. 関数の後に '='、引数の後に => をつける const getTriangleArea = (base,height) => { const area = base * height / 2; return area; } // 処理が複数行かつ 引数が複数であれば これで終了 // 3. 処理が1行なら 1行で書くことができる // // 1. 一列にする const getTriangleArea = (base,height) => { return base * height / 2; } // // 2. 波括弧とreturnをとる const getTriangleArea = (base,height) => base * height / 2; console.log(getTriangleArea(30,10)); // >> 150 // // 4. 引数が'1つ'なら () を省略可 const getEquilateralTriangleArea = base => base * base / 2; // 複数行に直すと const getEquilateralTriangleArea = base => { return base * base / 2; } console.log(getEquilateralTriangleArea(30)); // >> 450 // 引数がない場合 const consoleLog = () => console.log('引数がないよ'); consoleLog(); // >> 引数がないよ つまり、どんなに省略してもこの形はマスト!貫く!! = 引数 => // ちなみに元の関数宣言を関数式で書くと let getTriangleArea = function(base,height){ const area = base * height / 2; return area; } // 関数呼び出し let triangle1 = getTriangleArea(30,10) // >> 150
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

noteのAPIを使用して、記事の詳細を取得する方法

開発環境 vue.js (vue-cli 2.0) firebase bootstrap noteのAPIの利用方法 こちらの方法を用いて、noteの評価掲示板サービスを作りました。 https://www.note-2channel.work/ このサービスを作る際に、noteのAPIを使用したのでその方法を記載します。 検索すると、こんな記事を見つけました https://note.com/hagure_melon/n/n964ff6f7ad0e その中に ↑このようなnoteのAPIを取得するコードがありました。 (nafc8a5836d3fは記事のkey) というわけで実際に取得できるか試してみました。 test.js var request = require("sync-request"); var url = "https://note.com/api/v1/notes/nafc8a5836d3f"; var res = request("GET",url); var res_json = res.getBody("utf-8"); // console.log(res.getBody("utf-8")); const obj = JSON.parse(res_json);//オブジェクトをJSONとして文字列として出力 //parse=JSON形式の文字列を文字列として置き換える ターミナルで node test.js と実行したところ、問題なくnoteの記事の詳細がjson形式でparseされました! nodeを実行するにはfirebaseのfunctionsを利用して、関数をデプロイすれば良いということだったので、そのためにコードを書きます。 プロジェクトのfunctions内にindex.jsを用意して、そこに記述します。 index.js const functions = require('firebase-functions'); exports.helloOnCall = functions.https.onCall((data, context) => {//ここに書いたものがfirebase経由でしかアクセスできない var request = require("sync-request"); var url = "https://note.com/api/v1/notes/" + data; var res = request("GET",url); var res_json = res.getBody("utf-8"); const obj = JSON.parse(res_json);//オブジェクトをJSONとして文字列として出力 //parse=JSON形式の文字列を文字列として置き換える const return_res = { name: obj.data.name }; return return_res; }); //firebase経由でしか接続されないようにする。 まずはこれでapiを呼び出す関数を定義します。 次にfirebase functionsにhelloOnCall関数を登録します。 note.js const firebase = require("firebase"); var firebaseConfig = { apiKey: "XXX", authDomain: "XXXX", projectId: "XXX", storageBucket: "XXXX", messagingSenderId: "3XXX", appId: "XXXXXX", measurementId: "XXXXXXX", }; // Initialize Firebase firebase.initializeApp(firebaseConfig); const functions = firebase.functions(); functions.useFunctionsEmulator("http://localhost:5001"); //ローカルでfirebaseにアクセスするもの const main = async () => { const helloOnCall = functions.httpsCallable("helloOnCall"); const res = await helloOnCall("n40187a5c671b"); console.log(res); }; main(); //firebaseでAPIアクセス そしてターミナルを別枠で開き、 firebase emulators:start --only functions を起動しておく。 node note.jsを実行 これでしっかりコンソールにデータが表示されたら、helloOnCall関数は機能しています。 あとは適宜呼び出すだけ! call: function() { var search_note = firebase.functions().httpsCallable("helloOnCall"); search_note("noteのkey").then((res) => { console.log(res.data); } この方法で私はAPIを利用しました! 注意点 functionsを使用する際に、プランを従量課金のBlazeに変更しなくてはいけませんでした。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React】レンダリング制御について (React.memo / useCallback / useReducer)

はじめに React のコンポーネントのレンダリングの条件とどのように不必要な再レンダリングを防ぐかについて、手元で動かして確認しながらまとめました。 誤り等ありましたらコメントでご指摘いただけますと幸いです レンダリングの条件 まず、再レンダリングとはどのようなことを指すのでしょうか。 コンポーネントは以下のような流れで画面上に表示、更新されます。 レンダリング -> マウント -> 【state の更新など何かしらのイベント】 -> 再レンダリング -> 仮想DOMの差分を検知 -> リアルDOMに差分を反映 -> ... -> アンマウント 一度 DOM にマウントされたコンポーネントに state の更新などが生じると、React はコンポーネントを再度レンダリングします。state の更新以外にも、親コンポーネントがレンダリングされた場合や渡される props が更新された場合などもレンダリングが走ります。 state の更新によるレンダリング useState のセッター関数が呼ばれたときなど、state が更新されることによりレンダリングが走ります。 例えば、以下のようなコンポーネント。 const App = () => { console.log("Appレンダリング!") const [count, setCount] = useState(0); const incrementCount = () => { setCount((count) => count + 1); } return ( <> <div> {count} </div> <button onClick={incrementCount}> CountUp! </button> </> ); } CountUp ボタンを押すたびにこのコンポーネントのレンダリングが走ります。この再レンダリングは描画の変更の有無に関わらず実行されるので、例えば {count} をdivタグごと削除したとしても CountUp ボタンを押した時にレンダリング処理が走ります。 親コンポーネントのレンダリングによるレンダリング 親コンポーネントがレンダリングされると、渡される props の変更の有無に関わらずレンダリングが走ります。つまりコンポーネントツリーのルートコンポーネントがレンダリングした場合、その子コンポーネント、孫コンポーネントと連鎖的にレンダリング処理が走ります。 以下のように親コンポーネントにあたる App コンポーネントから子コンポーネントにあたる Child コンポーネントを呼んでみます。 const Child = () => { console.log("Childレンダリング!") return ( <div> Childコンポーネント! </div> ); } const App = () => { console.log("Appレンダリング!") const [count, setCount] = useState(0); const incrementCount = () => { setCount((count) => count + 1); } return ( <> <div> {count} </div> <button onClick={incrementCount}> CountUp! </button> <Child /> </> ); } 親コンポーネントにあたる App コンポーネントにレンダリングが走ると、子コンポーネントである Childコンポーネントにもレンダリング処理が走ります。例え props に変更がなくても(props 自体がなくても)子コンポーネントは再レンダリングします。 再レンダリングの防止方法 レンダリングコストの高いコンポーネントの場合、不必要な再レンダリングをメモ化で未然に防ぐことによりパフォーマンスの向上が見込めます。 (メモ化自体がコストのかかる処理であるため、必要以上にメモ化で再レンダリングを防ごうとすると返ってパフォーマンスが悪くなる可能性もあります。) React.memo による再レンダリング防止 React.memo() 関数にコンポーネントを渡すことにより、props の変更の有無を確認して再レンダリングが制御されるラッパーコンポーネントが返されます。 const Child = React.memo(({message}) => { console.log("Childレンダリング!") return ( <div> {message} </div> ); }) const App = () => { console.log("Appレンダリング!") const [count, setCount] = useState(0); const incrementCount = () => { setCount((count) => count + 1); } return ( <> <div> {count} </div> <button onClick={incrementCount}> CountUp! </button> <Child message={"Childコンポーネント!"}/> </> ); } 上のように React.memo() で Child コンポーネントをラップすることにより、親コンポーネントのレンダリング による再レンダリングを防ぐことができます。 (props として count を渡すようにすると、ボタンクリックの度に渡される props の値が変わるので、ちゃんと Child コンポーネントも再レンダリングされるようになります。) ここで、props として渡している値が関数であった場合では React.memo() のみの制御ではうまくいかなくなります。なぜなら、関数はレンダリングごとに別参照のオブジェクトとして生成されるため、props が等価でないと判断されるためです。 const Child = React.memo(({action}) => { console.log("Childレンダリング!") return ( <button onClick={action}> Action! </button> ); }) const App = () => { console.log("Appレンダリング!") const [count, setCount] = useState(0); const incrementCount = () => { setCount((count) => count + 1); } const sayHello = () => { console.log('Hello!'); } return ( <> <div> {count} </div> <button onClick={incrementCount}> CountUp! </button> <Child action={sayHello}/> </> ); } CountUp! ボタンを押して App コンポーネントが再レンダリングする度に新しい参照の sayHello 関数が渡され、Child コンポーネントのレンダリングも走ります。 このような場合は useCallback の使用を検討します。 useCallback による再レンダリング防止 useCallback に関数を渡すことにより、メモ化をおこなってくれます。また第二引数に依存配列を渡すことによりそのメモ化をコントロールすることができます。今回はただの文字列をコンソール出力する関数なので依存配列は空配列とします。 const Child = React.memo(({action}) => { console.log("Childレンダリング!") return ( <button onClick={action}> Action! </button> ); }) const App = () => { console.log("Appレンダリング!") const [count, setCount] = useState(0); const incrementCount = () => { setCount((count) => count + 1); } const sayHello = useCallback(() => { console.log('Hello!'); }, []) return ( <> <div> {count} </div> <button onClick={incrementCount}> CountUp! </button> <Child action={sayHello}/> </> ); } このようにして関数をメモ化することにより、CountUp! ボタンを押すたびに Child コンポーネントが再レンダリングされるのを防ぐことができます。 また、渡す props をメモ化しても Child コンポーネント自体がメモ化されていなかったら普通に再レンダリングが走ってしまうので注意が必要です。(親コンポーネントがレンダリングされていることに変わりはないため) 最後に、もう一捻り加えてみます。 message という state を追加して、count を2倍した値を使って message を更新する関数を props として渡してみます。依存配列には count を入れる必要があります。( count を依存に設定しないと、いくら CountUp! ボタンをクリックしても Action! ボタンをクリックしたときの出力が Double : 0 のままになってしまいます) const Child = React.memo(({action}) => { console.log("Childレンダリング!") return ( <button onClick={action}> Action! </button> ); }) const App = () => { console.log("Appレンダリング!") const [count, setCount] = useState(0); const [message, setMessage] = useState("initialized..."); const incrementCount = () => { setCount((count) => count + 1); } const updateMessage = useCallback(() => { setMessage(() => `Double : ${count * 2}`); }, [count]) return ( <> <div> {count} </div> <div> {message} </div> <button onClick={incrementCount}> CountUp! </button> <Child action={updateMessage}/> </> ); } CountUp! ボタンを押すたびに updateMessage 関数が新しい count に基づいて作られるため、どうしても Child コンポーネントのレンダリングが走ってしまいます。 props として渡している関数が state に依存しているのが再レンダリングの原因であり、このような場合に Child コンポーネントの再レンダリングを避けたいときは、useReducer により「 state に非依存な関数」を props として渡すようにする方法があります。 useReducer での state 管理 使い方については省略しますが、useReducer は useState のような state を管理するためのフックのひとつ(というか、useState は内部的に useReducer を使っています。)です。重要なのは、今回の場合 useReducer を使用することにより dispatch という state に依存していない関数を props として渡すことができるようになる、ということです。updateMessage 関数の中で count を参照していたような処理は reducer の中に閉じ込めることができます。 まずは、reducer を下のように作成します。 const initialState = { count: 0, message: "initialized..." }; const reducer = (state = initialState, action) => { switch (action.type) { case 'count/increment': return { ...state, count: state.count + 1 } case 'message/update': return { ...state, message: `Double : ${state.count * 2}` } default: return state; } }; count と message をシングルツリーの state オブジェクトの中にまとめることにより、「既存の state を受け取って新しい state オブジェクトを返す」というピュアな reducer 動きの中に処理を押し込むことができます。 あとは、しかるべき action を dispatch する関数をコンポーネントに配置すればよいだけなので、以下のように依存のない関数を porps として渡すことができるようになります。 const Child = React.memo(({action}) => { console.log("Childレンダリング!") return ( <button onClick={action}> Action! </button> ); }) const App = () => { console.log("Appレンダリング!") const [{ count, message }, dispatch] = useReducer(reducer, initialState); const incrementCount = () => { dispatch({ type: 'count/increment' }); } const updateMessage = useCallback(() => { dispatch({ type: 'message/update' }); }, []) return ( <> <div> {count} </div> <div> {message} </div> <button onClick={incrementCount}> CountUp! </button> <Child action={updateMessage}/> </> ); } これで、CountUp! ボタンを押してもその度に Child コンポーネントがレンダリングされるのを防げるようになりました。 Action! ボタンを押しても正常に動作します。 今回は dispatch を呼ぶ場所を App コンポーネントにまとめていますが、<Child dispatch={dispatch}/> のようにして Child コンポーネントから dispatch するやり方もあります。その場合、useCallback のラップがなくなりすっきりしそうです。 まとめ 今回挙げたもの以外にも、useMemo というメモ化された値を返すフックもあります。 繰り返しになりますが、メモ化自体にコストがかかるため、パフォーマンス計測などを通して適切にコンポーネントのレンダリングを制御することが大切だと思います! 参考 ・https://qiita.com/hellokenta/items/6b795501a0a8921bb6b5 ・https://qiita.com/soarflat/items/b9d3d17b8ab1f5dbfed2 ・https://qiita.com/uhyo/items/cea1bd157453a85feebf
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js】簡単なToDoアプリの制作と学習メモ

きっかけ これまでVueを用いてアプリを作ったりしていたのですが,Vueについての基本的なことが全然理解できていなかったことやFirebaseでサイトを公開してみたかったため学習を兼ねて作ってみました. 完成物 ToDoアプリ 構成 "Index.vue(src/pages/Index.vue)"という大枠のページの中に, Todoを書きこむ"TodoForm.vue(src/components/TodoForm.vue)" 書き込んだTodoを表示させる"TodoList.vue(src/components/TodoList.vue)" の2つのコンポーネントを組み込んだ形になります. スタイルはBootstrap-Vueを使いました. インポート Bootstrapの公式サイトに従ってコマンドを打ち込みます. npm install vue bootstrap bootstrap-vue 次にindex.js内でimport index.js import Vue from 'vue' import Router from 'vue-router' import Index from '@/pages/Index' import { BootstrapVue, IconsPlugin } from 'bootstrap-vue' // Import Bootstrap an BootstrapVue CSS files (order is important) import 'bootstrap/dist/css/bootstrap.css' import 'bootstrap-vue/dist/bootstrap-vue.css' // Make BootstrapVue available throughout your project Vue.use(BootstrapVue) // Optionally install the BootstrapVue icon components plugin Vue.use(IconsPlugin) Vue.use(Router) export default new Router({ routes: [ { path: '/', name: 'Index', component: Index } ] }) Index.vue 大枠となるページのファイル Index.vue <template> <div> <h1>ToDoリスト</h1> <todo-form @handleParentAddTodo="handleParentAddTodo" /> <todo-list :todos="todos" @handleParentDeleteTodo="handleParentDeleteTodo" @handleParentCompleteTodo="handleParentCompleteTodo" /> </div> </template> <script> // TodoForm,TodoList読み込み import TodoForm from '@/components/TodoForm' import TodoList from '@/components/TodoList' export default { name: 'Todo', components: { TodoForm, TodoList }, data () { return { // データバインディング todos: [] } }, methods: { // v-onでメソッド呼び出し // Todoリスト追加処理 handleParentAddTodo (value) { if (value) { this.todos.unshift({ text: value, complete: false }) } }, // Todoリスト完了処理 // この処理でcompleteの真偽を逆転させる handleParentCompleteTodo (index) { this.todos[index].complete = !this.todos[index].complete }, // Todoリスト削除処理 // spliceで引数の一行を消去 handleParentDeleteTodo (index) { this.todos.splice(index, 1) } } } </script> <style> </style> TodoForm.vue Todoリストを書き込み送信するファイル handleAddTodo関数の$emitで子コンポーネント(TodoForm.vue)から親コンポーネント(Index.vue)へイベントを放っている TodoForm.vue <template> <b-container> <b-input-group> <!-- v-model属性で双方向ディバイディング --> <b-form-input type="text" v-model="value"/> <b-input-group-append> <b-button variant="primary" @click="handleAddTodo()">送信</b-button> </b-input-group-append> </b-input-group> </b-container> </template> <script> export default { name: 'TodoForm', data () { return { value: '' } }, methods: { // 送信ボタンクリック時に処理される関数 handleAddTodo () { // Index.vueのhandleParentAddTodoにフォーム内の値を引数に発火 this.$emit('handleParentAddTodo', this.value) this.value = '' } } } </script> <style> </style> TodoList.vue フォームで送信されたTodoリストを表示したり,進捗を操作するファイル TodoFormと同様にhandleCompleteTodo関数,handleDeleteTodo関数の$emitで子コンポーネント(TodoList.vue)から親コンポーネント(Index.vue)へイベントを放っている TodoList.vue <template> <b-container> <!-- for文でTodoリストをひとつづつ処理 --> <b-row :key="index" v-for="(todo, index) in todos" class="mt-2"> <!-- v-bindのclass属性によりcomplete時にstyleが適用 --> <b-col cols="8" :class="[{line: todo.complete}, 'text-left']"> <h5>{{todo.text}}</h5> </b-col> <b-col cols="4" class="text-right"> <b-button @click="handleCompleteTodo(index)" :variant="todo.complete ? '' : 'success'"> {{ todo.complete ? '完了' : '未完了'}} </b-button> <b-button @click="handleDeleteTodo(index)" variant="danger">削除</b-button> </b-col> </b-row> </b-container> </template> <script> export default { name: 'TodoList', // 親コンポーネントからv-bindで渡された値(todos)を受け取る props: ['todos'], methods: { // 完了ボタンクリック時に処理される関数 handleCompleteTodo (index) { // Index.vueのhandleParentCompleteTodoにフォーム内の値を引数に発火 this.$emit('handleParentCompleteTodo', index) }, // 削除ボタンクリック時に処理される関数 handleDeleteTodo (index) { // Index.vueのhandleParentDeleteTodoにフォーム内の値を引数に発火 this.$emit('handleParentDeleteTodo', index) } } } </script> <style scoped> .line { text-decoration: line-through; } </style> FirebaseでHosting 以下の記事を参考にさせていただきました まとめ Todoアプリの制作を通して,v-model,v-bind,v-onについて一通りさわることができた. propsと$emitという親コンポーネントと子コンポーネントをつなぐ概念について理解できた. Firebaseで簡単にサイト公開をすることができた.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactハンズオン

Reactハンズオン はじめに この記事では、ハンズオン形式でReactの基礎を学ぶことができます。JavaScriptがなんとなく読める程度の知識があると読みやすいかと思います。 研修の流れ Reactとは Reactの環境構築 Reactでカウントアプリ/タスク管理アプリを作ってみる Reactとは ReactはJavaScriptのライブラリです。Reactを使うことでWEBページを独自の記法で簡単に作成することができます。 HTMLとかCSSで作らないの? WEBページの作り方を学ぶ際、はじめにHTMLやCSSを使った作成方法を学ぶかと思います。ReactでWEBページを作成するときも、最終的にはReactの記述を元にhtmlやcssなどのファイルが自動で生成されます。具体的にはnpm run buildを実行するとbuildフォルダが生成され、その中にindex.htmlやその他諸々のファイルが生成されます。つまり、buildフォルダをサーバに置けばサイトを公開することができます。 Reactの特徴 コンポーネント単位でページを作成することができる コンポーネントとは、WEBページの各パーツのことです。例えば、ヘッダーとかボタンとかその他諸々 コンポーネントを再利用することで簡潔なコードになる Reactの環境構築 Node.jsが入っている前提です。 npmを最新にする。 npm update npm create-react-appをインストール。 npm install -g create-react-app これでReactの開発環境が整いました。 Reactでカウントアプリ/タスク管理アプリを作ってみる Reactアプリを作成して表示する デスクトップに移動。 cd デスクトップのパス アプリを作成する。 npx create-react-app my-app 作成したアプリに移動。 cd my-app サーバを立てる。 npm start これでサーバが立ち上がります。ブラウザを立ち上げてlocalhost:3000を開くとWEBサイトが表示できているかと思います。 Reactアプリの構成 ここで、my-appがどのような構成になっているか見てみます。my-appをエディタで開いてみてください。 フォルダの中にはいくつかのファイルやフォルダがありますが、まず知っておくべきものはpublicとsrcの2つです。 public:Webで公開されるファイルを置いておくフォルダ src:Reactのソースコードやそれに関連するデータを置いておくフォルダ HelloWorldを表示してみる では、今表示されているページの代わりにHelloWorldという文字だけのページを表示してみましょう。 1. src/App.jsの<div className="App">...</div>を削除する。 2. 代わりに<h2>HelloWorld</h2>と書き込む。 ブラウザを確認してみると、HelloWorldが表示されているかと思います。このように、表示する内容を変更する場合にはreturn内を変更することになります。ここではHTMLの記法で記述することができます。 厳密にはJSXという記法になります。HTMLとの違いはほとんどありません。強いて言えば、JSXではタグは必ず閉じなければならないので<img src="...">は<img src="..."></img>とするか<img src="" />とする必要があります。 表示処理の流れ 先ほどApp.jsを編集しましたが、変更したデータはどのような流れで処理され表示されるのでしょうか。結論から言うと、先ほど変更したApp.jsのデータは、同じ階層にあるindex.jsに渡された後、ReactDOM.render関数によってpublic/index.htmlの<div id="root"></div>の子要素として追加されます。 カウントアプリを作ってみる 表示までの流れがわかったところで、実際にカウントアプリを作成してみましょう。今回作成するカウントアプリは、ボタンを押すと表示されている数が+1されるシンプルなものです。 function App() { ... }を削除 代わりに以下のコードを貼り付け class App extends React.Component { render() { return ( <div> <h2>タイトル</h2> <div>0</div> <button>COUNT UP</button> </div> ); } } 冒頭部分にimport React from 'react';を追記。 ブラウザにカウントアプリのページが表示されているか確認してください。ここでは、function App() { ... }を削除しclass App extends React.Component { ... }に置き換えましたが、どちらもreturn内に記述した要素が表示されるという点は同じです。なぜ置き換えたかや細かい違いは後ほど説明します。 さて、表示されたページにはタイトルとカウント、ボタンが表示されています。現在はボタンを押してもカウント数は変わりません。ではここからReactの記法を使って機能を追加していきましょう。 まずはタイトルを変更していきます。 1. render() { ... }の上に以下を追加 constructor(props) { super(props); this.title = "カウントアプリ"; } タイトルを{ this.title }に置き換え ブラウザを確認するとタイトルだった所がカウントアプリになっていると思います。これは、{ this.title }の中のthis.titleがカウントアプリに置き換えられたからです。this.titleはconstructor内で定義されています。 Reactではこのように、{}の中にJavaScriptの変数を記述するとReactがhtml要素としてレンダリングしてくれます。 コメントアウトは{/* コメント文 */}で記述できます。 constructor(props) { ... }やsuper(props);の記述はJavaScriptのclassの用法になります。気になる方は「javascript class 書き方」などと調べてみてください。また、Reactではconstructorとsuperの引数にpropsを渡すことになっています。はじめのうちはこういうものなんだなぁと思っていただいて大丈夫です。 Stateを使う 次に、ボタンを押したらカウントアップしてくれる機能を追加します。 super(props);の下に以下を追加。 this.state = { count: 0 }; render() { ... }の上に以下のメソッドを追加。 countUp() { this.setState({count: this.state.count + 1 }); } <div>0</div>を<div>{ this.state.count }</div>に置き換える。 <button>COUNT UP</button>を <button onClick={ () => { this.countUp() } }>COUNT UP</button>に置き換える。 ブラウザに戻り、ボタンを押してみてください。カウントが増加しているかと思います。ここでは、ReactのStateという機能を使ってcountの値を管理しています。Stateは、変数を管理する場所のようなイメージです。 使い方は以下の通りです。 constructor内でthis.state = { 変数名: 値, 変数名: 値, ... }として初期値を指定 this.state.変数名で値にアクセス this.setState({ 変数名: 新しい値 })で値を変更 countの値を増加させるにはsetStateメソッドを実行する必要があります。今回のアプリでは、ボタンを押すとcountUpメソッドが実行され、countUpメソッドの中でsetStateメソッドが呼ばれています。 ボタンが押された時に関数を実行したい場合は、onClick={ 実行したい関数 }を属性に追加します。今回はonClick={ () => { this.countUp() } }としています。 カウントアプリの作成は以上ですが、countDownのような関数を作成して、ボタンのonClick属性にあてることでカウントダウン機能を実装することもできます。 タスク管理アプリを作ってみる では次にタスク管理アプリを作成してみましょう。今回作成するタスク管理アプリはタスク一覧の表示、タスク追加の機能があります。 class App extends React.Component { ... }を以下に置き換える。 class App extends React.Component { render() { return ( <div> <h2>タスク管理アプリ</h2> <input /> <button>追加</button> <div>1. 本を買う</div> <div>2. ランニングする</div> </div> ); } } ブラウザでタスク管理アプリが表示されていることを確認してください。今表示されているタスクは直書きです。そのため、一覧機能や追加機能は実装されていません。 では、一覧機能を追加していきましょう。まずはStateを使って、タスクの内容をまとめる配列を管理していきます。 render() { ... }の上に以下を追加する。 constructor(props) { super(props); this.state = { tasks: [ "本を買う", "ランニングする", "牛乳を買う" ] } } 以下のコードを <div>1. 本を買う</div> <div>2. ランニングする</div> 以下に置き換える。 { this.state.tasks.map( (task, index) => { return <div>{ index + 1 }. { task }</div> }) } ブラウザを確認すると、Stateのtasksで管理しているタスクが表示されているかと思います。書き換えた内容を順を追って説明していくと、 1.で、タスクを管理するtasks配列をStateの変数として定義。 2.で、tasks配列の中身を取り出して表示。 となります。2.はやや複雑ですが分解して考えるとそれほど難しくありません。 ポイントは、 アロー関数表記 mapメソッド returnの値 の3つです。 アロー関数表記 まずはアロー関数表記ですが、これは従来の関数の書き方を簡潔にした表記方法です。 // 従来 function sayHi(name) { const msg = "hi " + name; console.log(msg); } sayHi("Bob"); // hi Bob // アロー関数表記 const sayHi = (name) => { const msg = "hi " + name; console.log(msg); }; sayHi("Bob"); // hi Bob 慣れてしまえば難しくはありません。今はこういう書き方があるんだなぁくらいに思ってもらえれば大丈夫です。 mapメソッド mapメソッドは、配列の各要素に対してある処理を行うメソッドで、 配列.map(処理が書かれた関数)のように書きます。 例えば、配列array1の各要素を2倍した値からなる配列array2を得る場合は以下のように書けます。 const array1 = [2, 4, 5, 8]; const array2 = array1.map( (value) => { return value * 2; }); console.log(array2); // [ 4, 8, 10, 16 ] mapメソッドが引数にとっている関数の第一引数には配列の要素が入ります。第二引数をとると、取り出した要素のインデックスを取得できます。 returnの値 最後にreturnの値についてです。Reactではhtml要素を一つの値のように扱うことができます。 例えば、 const element = <h2>タスク管理アプリ</h2> といった扱いができます。 以上を踏まえ、改めて2.で置き換えた箇所を見てみると、 { this.state.tasks.map( (task, index) => { return <div>{index + 1}. {task}</div> }) } tasks配列に対してmapメソッドを使用し、mapメソッドの引数に、アロー関数表記で処理したい内容を記述していることがわかります。mapメソッドのreturnではhtml要素が返されています。 では次に、タスクの追加機能を実装したいと思います。 StateにtaskContent: ""を追加する。 inputタグの属性に value={ this.state.taskContent }と onChange={(e) => { this.setState({ taskContent: e.target.value }) }} を追加。 buttonタグの属性に onClick={ () => { this.addTask() } }を追加。 render() { ... }の上に以下を追加。 addTask() { let tasks = this.state.tasks; tasks.push(this.state.taskContent); this.setState({tasks: tasks}); this.state.taskContent = ""; } ブラウザで、テキストフィールドに適当なタスク内容を記入して追加ボタンを押してみてください。タスクが追加されたのがわかります。 追記した内容を順に説明すると、 1.でテキストフィールドの値を保持するtaskContent変数をStateにて初期化 2.では、 taskContentの値をテキストフィールドに表示 テキストフィールドに入力された値をtaskContentに反映 3.でボタンをクリックした時にaddTask関数を実行するように指定 4.でaddTask関数を定義 addTask関数でtaskContentの値をStateのtasks配列に追加しています。 コンポーネントとProps ここまでで、一覧機能と追加機能を実装することができました。ここで、コンポーネント設計の考え方を導入してタスクの表示パーツをコンポーネント化してみましょう。コンポーネント化することでより簡潔なコードで記述することができます。 srcフォルダの中にcomponentsフォルダを作成。 作成したcomponentsフォルダの中にTask.jsというファイルを作成。 Task.jsに以下を貼り付ける。 import React from 'react'; class Task extends React.Component { constructor(props) { super(props); this.number = props.number; this.task = props.task; } render() { return ( <div> { this.number }. { this.task } </div> ); } } export default Task; App.jsの冒頭部分に import Task from './components/Task.js'; を追記してTask.jsをインポート。 mapメソッドの戻り値になっている<div>...</div>を <Task number={index + 1} task={task}/>に置き換える。 ブラウザを確認して問題なくページが表示されているか確認してください。 手順を順に解説していくと、 タスクを表示するパーツをTaskコンポーネントにして置き換えたい コンポーネントをまとめるcomponentsフォルダを作成し、その中にTask.jsを作成 Task.jsの中身もApp.jsと同様の構造で、Taskクラスコンポーネントをエクスポートしている そのエクスポートされたTaskクラスをApp.jsの冒頭部分でインポート App.jsのmapメソッドの戻り値としてを置く という流れになります。 ここで、<Task .../>の...に当たる部分を見てみます。number={index + 1} task={task}とありますが、ここでTaskにnumberとtaskというpropsを渡しています。propsとはコンポーネントに渡す値のことです。 この2つのpropsがどのように使われているかTask.jsを確認してみると、constructor内でthis.number = props.number; this.task = props.task; としてクラス内の変数に渡されていることが分かります。このようにコンポーネントに渡されたprops の値は、props.props名とすることでコンポーネント内で使用することができます。 今回の例では、コンポーネント化のメリットをあまり感じられなかったかもしれませんが、もう少し大きな開発になってくるとコードの見易さや管理のし易さ、再利用性の観点からメリットが大きくなります。 以上で、2つのアプリの実装が完了しました! 関数コンポーネントとは Reactのコンポーネントにはクラスコンポーネントと関数コンポーネントの2種類のコンポーネントがあります。 今まで書いてきたコンポーネントはクラスコンポーネントです。 class コンポーネント名 extends React.component { render() { return ( <div> 表示内容 </div> ); } } 一方、関数コンポーネントは次のような書き方です。 // アロー関数表記で書かれることもある function コンポーネント名() { return ( <div> 表示内容 </div> ); } 関数コンポーネントはクラスコンポーネントよりも簡潔に書けるといったメリットがあります。クラスコンポーネントで出来ることは基本的には関数コンポーネントでも実装できますが、少し難解な記法を使ったりするのでクラスコンポーネントに慣れてから関数コンポーネントを触ってみると理解しやすいかと思います。 はじめにfunction App() { ... }を削除してクラスコンポーネントに置き換えたのはこのためです。 以上でReactハンズオンは修了です。お疲れ様でした。 紹介しきれなかったこと ReactHooks ReactRouter Redux
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

typeof についてTypeScript独自の用法とJavaScriptと共通の用法を整理。

typeof演算子は、型コンテキストでは型定義、その他の場所ではデータ型の種類の文字列を返す。 前者はTypeScript独自の用法、後者はJavaScripと共通の用法。 型コンテキストでは 型コンテキスト(TypeScriptで型定義を書く箇所)では、typeofは対象の型定義を返す。 const hito = { name: 'Taro', age: 18 } const syuto = 'Tokyo'; let kuni = 'nihon'; const jinkou = 1000; let jinkou2 = 1000; const hito3: typeof hito = { name: 'hide', age: 20 } // const hito3: { // name: string; // age: number; // } const syuto2: typeof syuto = 'Tokyo'; // const syuto2: "Tokyo" type s = typeof syuto; // type s = "Tokyo" const syuto3: s = 'Tokyo' // const syuto3: "Tokyo" const kuni2: typeof kuni = 'amarica' // const kuni2: string type k = typeof kuni; // type k = string const kuni3: k = 'amarica' // const kuni3: string const jinkou3: typeof jinkou = 1000; // const jinkou3: 1000 const jinkou4: typeof jinkou2 = 1001; // const jinkou4: number その他の場所では 型コンテキスト以外の場所では、typeofは対象のデータ型の種類を文字列で返す。 返しうる文字列は"string", "number", "bigint", "boolean", "symbol", "undefined", "object", "function" const hito = { name: 'Taro', age: 18 } const syuto = 'Tokyo'; const hito2 = typeof hito; console.log(typeof hito); // object console.log(typeof syuto); // string console.log(hito2); // object console.log(typeof hito2); // string (文字列'object'の型の種類string) if (typeof hito2 === 'string') { console.log("hito2 is 'string'") } // hito2 is 'string'
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

jQuery UI で要素をドラッグしよう

はじめに みなさん、要素をドラッグして移動したいと思ったことはありませんか? これからjQuery UIを使って要素をドラッグしていきます。 jQuery UI jQuery UIを使うためには読み込む必要があります。 <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script> 適当な場所からjQueryとjQuery UIを読み込みます。今回は Google Hosted Libraries から読み込むことにしました。 ご自身でダウンロードして読み込んでいたただいても構いません。 移動させたい <div class="drag"> <div class="drag-content">content1</div> <div class="drag-content">content2</div> <div class="drag-content">content3</div> </div> See the Pen Qiita-drag by Risuney (@risuney) on CodePen. こうしても当然の如く要素の移動はできません。 jQuery UIを使う $('.drag').sortable() See the Pen Qiita-drag-2 by Risuney (@risuney) on CodePen. jQuery UIを使って要素の並べ替えができました。 要素をまたいでで移動 ただ並び替えただけではつまらないですね。 親要素とその他の要素間をまたいで移動させましょう。 $('.drag').sortable({ connectWith: '.drag', items: '.drag-content' }) See the Pen Qiita-drag-3 by Risuney (@risuney) on CodePen. 要素をまたいで移動することができました。 connectWithでまたぐ要素、itemsでドラッグする要素を指定しています。 追記 コメントで報告してくださいました。 この機能はスマートフォンなどのタッチディスプレイでは非対応のようです。 まとめ sortableで簡単に要素の移動ができる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【2021年4月版】Node.js v15 の npm 7 で TypeScript の MonoRepo に入門

NPM にもやってきましたワークスペース YarnやLernaでお馴染みのワークスペース管理ですが、Node v15 の nvm 7 からこれに対応した模様です! Medium: Migrating to a Monorepo Using NPM 7 Workspaces 上記の記事では exprimental とありますが、現在では GA の模様です。 自分も最近では Serverless と SPA での開発がメインでどちらも TypeScript を使うので、Typescript でも宜しく Mono Repository できないものかと考えておりましたら・・・ いい感じの Typescript での Monorepo サンプルを発見いたしましたので、これに沿って Typescript での Monorepo に入門してみたいと思います。 1. まず JS から TypeScript を導入する前に、JS で Workspace の破壊力を試してみましょう。 1-1. プロジェクトの階層構造を構築 以下のようなコマンドでプロジェクトのフォルダを作成します。 $ mkdir project1 && cd $_ $ npm init -y $ mkdir app $ mkdir -p lib/component-a $ mkdir -p lib/component-b $ cd app && npm init -y $ cd ../lib/component-a && npm init -y $ cd ../component-b && npm init -y $ cd ../.. 結果、以下のようなフォルダ構成のプロジェクトが出来上がります。 project1 ├── app │  └── package.json ├── lib │  ├── component-a │  │ └── package.json │  └── component-b │  └── package.json └── package.json 1-2. ワークスペースの宣言 トップの package.json に workspaces を追加します。 project1/package.json { ... "workspaces": [ "libs/*", "app" ], ... } 上記のような指定で、app と libs 以下のpackage.jsonを含むサブフォルダ全てを"個別のモジュール"として認識してくれます。 これで、npm i をして、結果を表示してみましょう。 $ pwd /xxx/xxx/project1 $ npm i ... $ npm ls project1@1.0.0 /xxx/xxx/project1 ├── app@1.0.0 -> /xxx/xxx/project1/app ├── component-a@1.0.0 -> /xxx/xxx/project1/libs/component-a └── component-b@1.0.0 -> /xxx/xxx/project1/libs/component-b のような感じで app と component-a、component-bが認識されていることがわかります。 ついでに node_modules 内はどうなっているか見てみましょう。 $ tree . ├── app │ └── package.json ├── libs │ ├── component-a │ │ └── package.json │ └── component-b │ └── package.json ├── node_modules │ ├── app -> ../app │ ├── component-a -> ../libs/component-a │ └── component-b -> ../libs/component-b ├── package.json └── package-lock.json このような感じで、app と libs 以下の component-a、component-b が個別のモジュールとして依存関係に追加されているのが確認できました。 1-3. 動作確認用プログラムの追加 プログラム上、ちゃんと認識されているのか、確認してみたいと思います。 シンプルなスクリプトを以下のように作成します。 component-a には以下の index.jsを作成してみます。 libs/component-a/index.js module.exports = 'A だよ!'; 続いて component-b には以下の index.jsを作成してみます。 libs/component-b/index.js module.exports = 'B どす〜'; そしてこれらをappから呼び出してみましょう。 app/index.js console.log("start app"); const moduleA = require('component-a'); console.log(moduleA); const moduleB = require('component-b'); console.log(moduleB); 1-4. 動作確認 では、app/index.js を起動してみます。 $ pwd /xxx/xxx/project1 $ node app/index.js start app A だよ! B どす〜 $ cd app && node index.js start app A だよ! B どす〜 トップレベルのディレクトリからも app ディレクトリからでも相対パスなどなしでちゃんと component-aとcomponent-bが読み込めています。 app/package.json の Dependencies には component-aやcomponent-bは追加してませんよ!? さらにlibs以下の js を書き換えても即座に反映されます。(シンボリックリンクだから当たり前だけど・・・) これまでのバラバラのリポジトリでのモジュール開発ではあっちこっちpullしてビルドして、エラー出たら依存してるバージョン上げてまたビルドして・・・大変でした。 いや、素晴らしいですね! 2. いよいよ TypeScript それではいよいよ Typescript で Workspace に挑戦してみます。 上記のサンプルリポジトリで紹介されている手順 を参考にやってまいりましょう。 2-1. プロジェクトの階層構造を構築 まずは以下の手順でプロジェクトのディレクトリ構成を js の時と同じ手順で構築します。 $ mkdir sample_ts_ws && cd $_ $ npm init -y $ mkdir app $ mkdir -p lib/component-a $ mkdir -p lib/component-b $ cd app && npm init -y $ cd ../lib/component-a && npm init -y $ cd ../component-b && npm init -y $ cd ../.. はい、以下のようになりますね。 結果、以下のようなフォルダ構成のプロジェクトが出来上がります。 sample_ts_ws ├── app │  └── package.json ├── lib │  ├── component-a │  │ └── package.json │  └── component-b │  └── package.json └── package.json 2-2. ワークスペースの宣言 トップの package.json に workspaces を追加します。 sample_ts_ws/package.json { ... "workspaces": [ "libs/*", "app" ] } 続いてnpm iを叩きます。ここまでの結果は同じです。 sample_ts_ws $ npm ls sample_ts_ws@1.0.0 /xxxx/xxxxx/sample_ts_ws ├── app@1.0.0 extraneous -> /xxxx/xxxxx/sample_ts_ws/app ├── component-a@1.0.0 extraneous -> /xxxx/xxxxx/sample_ts_ws/lib/component-a └── component-b@1.0.0 extraneous -> /xxxx/xxxxx/sample_ts_ws/lib/component-b npm ERR! code ELSPROBLEMS npm ERR! extraneous: app@1.0.0 /xxxx/xxxxx/sample_ts_ws/node_modules/app npm ERR! extraneous: component-a@1.0.0 /xxxx/xxxxx/sample_ts_ws/node_modules/component-a npm ERR! extraneous: component-b@1.0.0 /xxxx/xxxxx/sample_ts_ws/node_modules/component-b ... あの。。。なんかエラー出てるんすけど(NPM problem: npm ERR! extraneous)。。。 依存関係がうまく認識されていないっぽいので明示的に依存を追加しました。さっきは大丈夫だったんだけどなぁ。。。なんかあるのでしょう。。。 sample_ts_ws/package.json { .... , "dependencies": { "app": "^1.0.0", "component-a": "^1.0.0", "component-b": "^1.0.0" } } まぁ、これもOKですね。 2-3. TypeScript環境の構築 で、TypeScriptの環境を突っ込んでいきます。 TypeScriptパッケージのインストールとtsconfig.jsonの作成をします。 sample_ts_ws $ npm i -D typescript ... sample_ts_ws $ npx tsc --init message TS6071: Successfully created a tsconfig.json file. 2-3-1. トップのプロジェクトの設定調整 トップのプロジェクトの設定変更です。 以下のように、"compilerOptions"の"composite": true をコメントアウトを解除して有効にします。 sample_ts_ws/tsconfig.json { "compilerOptions" : { .... "composite": true, /* Enable project compilation */ .... } } これがTypescript設定の起点となります。 また"tsconfig.json"とは別にビルド用の設定 "tsconfig.build.json"を作成します。こちらには全ての子コンポーネントを参照に追加しておきます。 sample_ts_ws/tsconfig.build.json { "files": [], "references": [{ "path": "app" }, { "path": "lib/component-a" }, { "path": "lib/component-b" }] } そしてこのファイルを参照するビルドスクリプトを package.json に追加しましょう。 json { ... "scripts": { "compile": "tsc -b tsconfig.build.json", }, ... } トップのプロジェクトはこれでOKです。 2-3-2. ワークスペース側のプロジェクトの設定調整 続いてコンポーネント側に tsconfig.json を追加していきます。 まず、lib/component-a と component-b は依存関係がないので、以下のようなシンプルな tsconfig.json でOKです。 lib/component-a/tsconfig.jsonとlib/component-b/tsconfig.json { "extends": "../../tsconfig.json", "compilerOptions": { "rootDir": "src", "outDir": "dist" } } extendsでトップのtsconfig.jsonを継承しています。 続いて app には上記のコンポーネントを参照したtsconfig.jsonを追加します。 app/tsconfig.json { "extends": "../tsconfig.json", "compilerOptions": { "rootDir": "src", "outDir": "dist" }, "references": [{ "path": "../lib/component-a" }, { "path": "../lib/component-b" }] } で、今回は outDir に dist を指定しました。このパスと package.json の main 属性は合わせておかないとビルドが通らないので以下のように合わせておきましょう。 lib/component-a/package.jsonとlib/component-b/package.json { ... "main": "dist/index.js", ... } 2-4. 動作確認用プログラムの追加 続いてtsのプログラムを作成します。 まずはコンポーネント側から lib/component-a/src/index.ts export const ComponentA = { A : 'A だよ!' }; lib/component-b/src/index.ts export const ComponentB = { B : 'B どす〜' }; もはやscrフォルダを作る方がめんどくさい位ですが。。。 で、このコンポーネントを参照する app 側のコードを作成します。 app/src/index.ts import { ComponentA } from "component-a"; import { ComponentB } from "component-b"; console.log("start app"); console.log(ComponentA.A); console.log(ComponentB.B); VS Code 上ではimport のところで"component-a"と"component-b"ともにコード補完は効くのですが、型が見つからないエラーが出てしまいます。 2-5. 動作確認 これは現時点では放置でコンパイルと動作確認をしましょう。 まずは現時点でのファイル構成をおさらいしておきます。 sample_ts_ws $ tree -I node_modules . ├── app │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── lib │ ├── component-a │ │ ├── package.json │ │ ├── src │ │ │ └── index.ts │ │ └── tsconfig.json │ └── component-b │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── package.json ├── package-lock.json ├── tsconfig.build.json └── tsconfig.json 7 directories, 13 files node_modulesを除外すると上記のような構成になっているかと思います。 それでは以下のコマンドでコンパイルします。 sample_ts_ws $ npm run compile > sample_ts_ws@1.0.0 compile > tsc -b tsconfig.build.json 続いて、生成された index.js を叩いてみます。 sample_ts_ws $ node app/dist/index.js start app A だよ! B どす〜 sample_ts_ws $ cd app && node dist/index.js start app A だよ! B どす〜 ということで無事に module の export がちゃんと効いていることが確認できました! 番外. VS Code でのエラー解消・・・? コマンドラインではちゃんと動いていますが、手元の環境の VS Code では相変わらず"型が見つからないエラー"が出ています。 自分の場合は app/tsconfig.json の設定を適当にいじってエラーを起こし元に戻す で index.ts 側のエラーの表示が消えました。何かしらキャッシュ的な問題があるのでしょう。node v15から対応したばかりだし、ということで大目に見ておきましょうか。。。 それでは、今回は以上といたします。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

某名探偵アニメの公開までのカウントダウンを作る

カウントダウンを作ったときのメモをまとめておきます。 某名探偵アニメ公開までの公開を計算します。 カウントダウン 設定した時刻から、現在時刻を引いて計算結果を表示する処理を記述します。 countdown.js function countDown(due) { const now = new Date(); console.log(now); const rest = due.getTime() - now.getTime(); const sec = Math.floor(rest / 1000) % 60; const min = Math.floor(rest / 1000 / 60) % 60; const hours = Math.floor(rest / 1000 / 60 / 60 ) % 24; const days = Math.floor(rest / 1000 / 60 / 60 / 24 ); const count = [days, hours, min, sec]; return count; } const goal = new Date(2021, 4 - 1, 16); function calc() { const counter = countDown(goal); document.querySelector('#day').textContent = counter[0]; document.querySelector('#hour').textContent = counter[1]; document.querySelector('#min').textContent = counter[2]; document.querySelector('#sec').textContent = counter[3]; refresh(); } function refresh() { setTimeout(calc, 1000); } calc(); ①new Dateにより、初期化したものをnowに代入する。 ②渡された時間と現在時刻の差のミリ秒をrestに代入している。 const rest = due.getTime() - now.getTime(); ③restに代入した値から秒の計算を行う。restにはミリ秒が代入されているため、1000で割り、60で割った余りをsecに入れる。 const sec = Math.floor(rest / 1000) % 60; ④分、時、日を以下のように計算することで求めることができる。 (ミリ秒 / 1000 / 60) % 60の値が、差のmin (ミリ秒 / 1000 / 60 / 60) % 24の値が、差のhour (ミリ秒 / 1000 / 60 / 60 / 24 )の値が、差のdays const min = Math.floor(rest / 1000 / 60) % 60; const hours = Math.floor(rest / 1000 / 60 / 60 ) % 24; const days = Math.floor(rest / 1000 / 60 / 60 / 24 ); ⑤配列として返す const count = [days, hours, min, sec]; return count; ⑥目標日を設定する。 const goal = new Date(2021, 4 - 1, 16); ⑦calc内でHTMLNに出力する ⑧setTimeoutによって1秒ごとにcalcを実行する。 以上で名探偵アニメ映画の公開までのカウントダウンタイマーの完成となります。 ありがとうございました。 参考 確かな力が身につくJavaScript「超」入門 第2版 https://www.sbcr.jp/product/4815601577/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【material-ui】「要素hover→別要素pop」を簡単に記述するコンポーネント

環境 React.js TypeScript material-ui 目的 お気持ち: - シンプルなwebサイトにしたいが、詳細も載せたい。 - 別リンクに飛ぶ詳細は面倒。 - 要素にhoverした時に詳細がpopしてほしい。 - ↑を可能にするPopper(material-ui)はコード量が30行くらいと多い。 - pop付要素を手軽に実装できるようにラップしよう。 概要 Popper(material-ui)をラップし、手軽な実装を実現。 作成したコンポーネントAddPopper AddPopperの子要素にhoverすると、props.popに渡したReact要素が表示される。 使用例 Webサイトに表示した、カプースチンのソナタファンタジーいいよねという文字列の「カプースチン」と「ソナタファンタジー」の部分にhoverした時、補足説明が現れる(popする)ようにする。 app.tsx function App() { return ( <div className="App"> <AddPopper //捕捉説明① pop={<div>作曲家: Nicolai Kapustin(1937-2020, ウクライナ)</div>} > {/*このspanにhoverすると捕捉説明①が表示される。*/} <span>カプースチン</span> </AddPopper> の {/*補足説明②*/} <AddPopper pop={<div>Op.39 4楽章構成</div>}> {/*このspanにhoverすると捕捉説明②が表示される。*/} <span>ソナタファンタジー</span> </AddPopper> いいよね。 </div> ); } propsの定義 interface Props { pop: React.ReactElement<any> //hover時に表示する要素 type: 'span' | 'div' //被hover要素がinlineかどうかを指定 children: React.ReactElement<any> //この要素にhoverすると発火 placement?: PopperPlacementType; //popの位置を決定 } AddPopperのソースコード import { Fade, PopperPlacementType } from "@material-ui/core"; import { Popper } from "@material-ui/core"; import React, { useCallback, useState } from "react"; interface Props { pop: React.ReactElement<any>; children: React.ReactElement<any>; placement?: PopperPlacementType; type?: "span" | "div"; } export function AddPopper(props: Props) { //被hover要素の位置を受け取る。 const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null); //hover時に発火 const openPop = useCallback((event: React.MouseEvent<HTMLElement>) => { setAnchorEl(event.currentTarget); }, []); //hoverが外れた時に発火 const closePop = useCallback(() => { setAnchorEl(null); }, []); //hoverの有無をbooleanで持つ const open = Boolean(anchorEl); //被hover要素に、カーソル設定とhover時背景色設定を追加している。 const Base = () => React.cloneElement(props.children, { style: { cursor: "default", backgroundColor: open ? "lightgrey" : undefined, }, }); //被hover要素とPop要素にマウスイベントを付加 return React.createElement( props.type ?? "div", { onMouseEnter: openPop, onMouseLeave: closePop }, [ <Base key="base" />, <Popper key="popper" open={open} anchorEl={anchorEl} transition placement={props.placement ?? "bottom-start"} > {({ TransitionProps }) => ( <Fade {...TransitionProps} timeout={350}> {props.pop} </Fade> )} </Popper>, ] ); } ソースコードの説明 基本的にはPopper(material-ui)の書き方に倣って書いている。 props.childrenとして受け取った要素(AddPopperの子要素)には、hover時のカーソルと、hover時の背景色が自動的に指定される。 被hover要素が、inlineの時はspanを、blockの時はdiv、という使い分けのために、React.CreateElementによってtype(タグ名)をpropsに応じて指定可能にしている。 props.placementでpopの位置を調整。defaultはbottom-startとなっている。参照->PopperPlacementTypeの定義
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

スプレッド構文について

JavaScriptを触りはじめの時にスプレッド構文の理解をすることがとても苦手でした。 今でも完璧に理解できてはいないですが現段階で理解している部分を言語化してみたいと思います。 スプレッド構文の理解で苦しんでいる方の一助になれれば幸いです! スプレッド構文とは スプレッド構文 (...) を使うと、配列式や文字列などの反復可能オブジェクトを、0 個以上の引数 (関数呼び出しの場合) や要素 (配列リテラルの場合) を期待された場所で展開したり、オブジェクト式を、0 個以上のキーと値のペア (オブジェクトリテラルの場合) を期待された場所で展開したりすることができます。 MDNによると上記のように定義されていました。 正直どういうことか理解するの難しいですよね...(私は全く理解できませんでした) ※MDN(https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Spread_syntax) コードをみた方が理解できるかと思うので実際にコードと一緒に説明していきたいと思いますが、 ざっくりとした機能としては...(ピリオド3つ) を書くことで個々の値に展開ができ、それを順番に処理して結合させることができます。 ※ちなみに「codesandbox」という手軽に環境構築などができるサイトがあるので、そちらで実際にコードを打ちながら試してみると理解度が上がるかもしれません。 コードと一緒に解説 const arr1 = ["hoge", "foo"]; console.log(arr1); //["hoge", "foo"] //スプレッド構文を使ってこちらを展開してみます。 console.log(...arr1); // hoge foo //最初のコンソールでは配列で出力されてましたが、次のコンソールでは個々の値(hoge,foo)で出力されます。 上記のように配列の中身を順番に処理してくれます。 もう少し配列の例をあげてみます。 const sum = (x, y, z) => { console.log(x + y + z) }; const num1 = 1; const num2 = [2, 3]; sum(num1, ...num2) //6 //スプレッド構文でnum2の要素を順番に設定しているので2、3が設定されています。 //なのでsum(1, 2, 3)と同じことをしている状態です。 ※参照とコピーの違いについても解説したいのですが長くなってしまうので今回は省略します。 最後に スプレッド構文は私としてはかなり難しく感じました。色々とスプレッド構文で書いていくことで覚えられるようになると思うのでコードサンドボックスなどで是非試して見て欲しいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

deck.glのTile3dLayerでPLATEAUの3D都市モデルを表示してみた

はじめに 今話題のPLATEAUのデータをdeck.gl+Mapbox GL JSで表示してみるぞ! Tile3dLayer みんな大好きdeck.glにTile3dLayerというクラスがあり、これはいわゆる3d-tilesを読むためのレイヤーです。 点群でも3Dモデルでも、3d-tilesならこのレイヤーで表示する事が出来ます(一応Experimentalです)。以下のように定義します。 import { Tile3DLayer } from '@deck.gl/geo-layers'; import { Tiles3DLoader } from '@loaders.gl/3d-tiles'; const tile3dLayer = new Tile3DLayer({ id: 'tile3dlayer', pointSize: 1, data: 'https://s3-ap-northeast-1.amazonaws.com/3dimension.jp/13000_tokyo-egm96/13101_chiyoda-ku_notexture/tileset.json', // PLATEAU千代田区 loader: Tiles3DLoader, onTilesetLoad: (tileset) => { const { cartographicCenter } = tileset; const [longitude, latitude] = cartographicCenter; console.log(longitude, latitude); // 3dtilesの中心座標を取れるぞ }, }); Mapbox GL JS上に表示してみる deck.glレイヤーをMapbox GL JS上で表示する手法はhttps://deck.gl/docs/api-reference/mapbox/mapbox-layerを見ればわかります。 表示は出来ましたがなんかモデルが宙に浮いています(静止画像なのでわかりにくいですがかなり浮いています)。 モデルが浮く問題 PLATEAU-Viewをみてみると、モデルは標高とリンクしている事がわかります。おそらく標高と建物は同時に計測されているのかと思われます。 Mapbox GL JSは基本的に平面地図で標高はゼロです(v2なら3D表現ができるが…)。仮に3Dだとしても、PLATEAUで使われている標高データはすぐウェブで使える形式では公開されていないので、やはり基準の標高値がゼロでも、マシな見た目にする事を考えた方がいいでしょう。つまり全体の高度を一律に下げる事を考えます。 標高データはCityGMLで公開されていて中身をみる限りTINなので、何かしらウェブで使える標高データに変換する方法はあるでしょう 高度を調整してみた さっきのtile3dLayerにオプションを追加します。 import { Tile3DLayer } from '@deck.gl/geo-layers'; import { Tiles3DLoader } from '@loaders.gl/3d-tiles'; import { Vector3 } from 'math.gl'; const tile3dLayer = new Tile3DLayer({ id: 'tile3dlayer', pointSize: 1, data: 'https://s3-ap-northeast-1.amazonaws.com/3dimension.jp/13000_tokyo-egm96/13101_chiyoda-ku_notexture/tileset.json', loader: Tiles3DLoader, onTilesetLoad: (tileset) => { const { cartographicCenter } = tileset; const [longitude, latitude] = cartographicCenter; console.log(longitude, latitude); }, onTileLoad: (tileHeader) => { tileHeader.content.cartographicOrigin = new Vector3( tileHeader.content.cartographicOrigin.x, tileHeader.content.cartographicOrigin.y, tileHeader.content.cartographicOrigin.z - 40, ); }, }); タイル単位で保持しているらしい基準標高値を、一律40m下げました。 さっきよりも地に足のついた見た目になりましたね。 終わりに 3Dデータを表示するなら素直にCesium使っておいた方が良い気がしていますが、ベクタータイルや2D地図表現はやはりMapbox GL JSの強みだと思います(最近では3D地形も表示出来ますし…)。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WEBサイトを作成するまでの道のり03

管理しているサイトがwordpressだったので自分で作り変えよ~~と思ったのでそれまでの道のりを記録したものです。 最近はめっぽうウマ娘にはまってます。 DOMとは? ・HTMLをJSから操作できるようにしたインターフェイス ・JSからはDOMAPIを通じて ・HTML情報を取得、変更 ・情報の取得、変更、イベント登録など可能 ベースHTML <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>Document</title> </head> <body> <h1 id="main-title">しまねっち伝説</h1> <h2 class="sub-title">DOMとは?</h2> <ul> <li class="item item-1">コメクイテー<strong class="v1">JavaScript</strong>から操作できるようにしたインターフェイス。</li> <li class="item item-2">しまねっち伝説2<strong class="v2">しまねっち伝説2</strong></li> <li class="item item-3"><strong class="v3">しまねっち伝説2</strong>などができます。</li> </ul> </body> </html> コンソール上でHTMLを取得 このようにコンソール上でDOMのAPIを取得する事ができます。 基本的にDOMのAPIを使用する場合documentというコマンドを利用していきます。 次にdocumentの代表的な便利な機能について説明していきます。 querySelector = DOMツリーの検索 document.body.children ボディーの中の子要素を表示 querySelectorall = 指定した要素すべてを取得 このように指定したものに対して安易に取得が可能となりました。 HTML要素の書換え このように、 const h1 = document.querySelector('#main-title') と変数に変えてinner.htmlに対して書き換えたい要素を追加すると HTMLが変化していきます。 でカラーをしていすると追加と色の配色なども追加が可能となります。 textContent h1.textContent "しまねっち伝説2 帝王偏," h1.classList クラス情報の取得、更新が可能になります。 では、最後にdocument.querySelectorallを使いli要素の配色を変更していきます。 forEachを使いループを行いliに対して赤色を付与しました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[備忘録]APIのレスポンスなどの非同期処理が早過ぎる場合でも、ローディングを1秒間は表示する方法

APIレスポンスが早すぎてローディングが一瞬しか出ない問題 APIのレスポンスに限らず、Promiseの処理が一瞬で終わってしまい、ローディングがほとんど表示されないため、正常に処理が終了したのかわかりにくい場合があると思う。 その際に、いい感じに書く方法をメモっておく。 ReactNativeのRefreshControlの例 重要なのは、const promise = refreshFunc()とsetTimeoutの部分。 ここで、refreshFunc()は非同期処理である。 非同期処理のPromiseオブジェクトを変数に入れておくことで、非同期処理の実行は先に行うが、 どんなに早く非同期処理が終了しても、setTimeoutの時間が経過した後でしか結果をチェックしないようにする。 export const ScrollViewWithRefresh = ({refreshFunc, children, ...props}) => { const [isRefreshing, setIsRefreshing] = useState(false) return ( <ScrollView refreshControl={ <RefreshControl refreshing={isRefreshing} colors={[primary]} tintColor={primary} onRefresh={async () => { setIsRefreshing(true) const promise = refreshFunc() setTimeout(() => { promise.then(() => setIsRefreshing(false)) }, 1000) // 最短でも1秒間はローディングを表示する }} /> } {...props} > {children} </ScrollView> ) } ググる力がなさすぎて、いい方法が見つからなかったので、どうにか自分で書いてみた。 もっといい方法があれば教えて下さい。。。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

非相対パスでimportした

何をしたのか これを参考に非相対パス(~/*)の形でimportをできるように設定した。 モジュール解決とは何か importが何を参照するのかを解決するために、コンパイラを利用するプロセス 相対importと非相対import 相対importは / や ./ や ../ などで始まるやつ import {} from "../foo" import {} from "./bar" 非相対importは基本的にこれ以外のやつ import React from "react" import Sute from "hage" めっちゃざっくり非相対importはどのように解決されるか 大きく2つの方法がある。それはNodeとクラシックである。デフォルトはNode。 tsconfigの"module"や"moduleResolution"で設定可能。 非相対importの場合は基本的にまずそのディレクトリの node_modules を探してその中にあるパッケージを探す。 そして見つからなかったら、ディレクトリチェーン上(現在のディレクトリの1つ上、そのまた上...)から node_modules を見つけようとする。 TypeScriptだとどうなるか TSも基本的には同じ。ただ、TS特有のファイルがあったりするので若干違う。省略。 そして、設定によっても若干変わるところがある。 baseUrl tsconfigで設定できる。 baseUrlはコンパイラにモジュールの入手先を教える。 tsでの非相対importはbaseUrlからの相対的であると仮定する。 相対importに対しての影響はない。 paths tsconfigで設定できる。 パスをマッピングしてくれる。 相対importだと深くなってしまうところを、非相対importでbaseUrlから相対的な設定をすることができる。 tsconfig.json { "baseUrl": "./src" "paths": { "~/*": ["*"] } } import モジュール from "~/src直下のディレクトリやファイル/..." 参照
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptだけでCORSによるCanvas汚染を回避する

通常別オリジンの画像をcanvasに描画するとCORS制限に引っ掛かりHTMLCanvasElement.toDataURL()などが使えなくなります。 例えば以下のような場合です。 const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); const imgUrl = "https://example.com/a.png" // 別オリジンのURL const img = new Image(); img.onload = () => { ctx.drawImage(img,0,0); canvas.toDataURL(); // <- ここでSecurityError } img.src = imgUrl; これに対する正しい対処法としてMDNの「画像とキャンバスをオリジン間で利用できるようにする」がありますが、サーバーの設定がいじれないなどでこの方法が使えない場合があると思います。 これに対する回避法としてググるとサーバーをプロキシとして使うなどといった方法が出てきますが、ここではjs単体で完結する方法を書こうと思います。 方法は簡単で、blobとして取得し、それをURL.createObjectURL()でそのURLを作成し、それをsrcに設定するだけです。以下ではaxiosを使用していますが、fetchでも同様に可能だと思います。 const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); const imgUrl = "https://example.com/a.png" // 別オリジンのURL const img = new Image(); //blobとして取得してblobへのURLを作成する const blob = (await axios.get(imgUrl, { responseType: "blob" })).data const url = URL.createObjectURL(blob); img.onload = () => { ctx.drawImage(img,0,0); canvas.toDataURL(); // SecurityErrorが発生しなくなる } img.src = url; 因みに、ArrayBuffer -> Uint8Array -> base64 としてCORSを回避する方法もあります。こちらは処理が増え時間がかかるのであまりお勧めしません。(CORSの回避自体あまりおすすめできないですが)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む