- 投稿日:2021-01-18T23:50:21+09:00
俺の論理演算子、条件演算子
演算子
javaScriptで定義されている演算子は様々あります。
JavaScript primer演算子その中でReactでよく用いる演算子をまとめました。
論理演算子
AND演算子(&&)
左辺がtrueなら右辺を。
左辺がfalseならそのまま左辺を返します。OR演算子(||)
&&の逆で左辺がtrueなら左辺を。falseなら右辺を返します。
条件演算子
条件式 ? trueの時の評価式 :flseの時の評価式
const a = 3; const b = 7; const inNum = true; console.log(inNum ? a : b); // aが評価されて「3」出力!! tspeScriptのオプショナル用の打ち消し
!!を付ける事で
boolean | undefined
をboolean
にすることが出来る。
オプショナルでa?:boolean;
と定義してエラーが出ている時などは、これで対応できます。
フリーランスでフロントエンドエンジニアをしています。
お仕事のご相談こちらまで
gunners6518@gmail.com
- 投稿日:2021-01-18T23:25:35+09:00
ml5.jsでPoseNetで手旗信号読取りWebを作る
はじめに
前回 TensorFlow.js version of PoseNetで手旗信号読取りサイト というPoseNet(機械学習によって姿勢を推定できる技術)を使った手旗信号読取りWebサイトを作った話を書きました。
記事中にもありますが、推定された肩や肘の角度から判定しているのですが、実測値を見ながら手動で判定式を書いた感じなので精度に納得がいっていないんです。せっかくTensorFlowとか使ってるなら手旗信号のポーズ自体も学習させた方が良いな…とか何となしには思っていたのですが、そんなスキルがあるわけでもなく。
と半ば諦めつつもググっていると、
https://thecodingtrain.com/learning/ml5/7.2-pose-classifier.html
いや、まさにコレじゃないすか!
というわけでこのml5.jsとPoseNetを使った手旗信号読取りWeb 機械学習Verの作成を紹介します。
結果から言うと読取り精度のイマイチさはあまり変わっていません?ただ学習方法の最適化など、まだ精度向上の余地はあるな、という感じなので前向きです。参考にしたもの
ちなみにわたくしのバックグラウンドはフロントエンドエンジニアとか機械学習専門とかではないので、全て勉強しながらの備忘録という感じです。
PoseNet
前回と同じです。前回の記事参考にしていただければと思います。
ml5.js
TensorFlow.jsをベースにした機械学習用のライブラリです。アーティストやクリエイティブ、学生向け、とありますね。
このライブラリを使って手旗信号を学習させて、得られたモデルを使って判定させる、といった具合です。p5.js
はじめは前回のコードの判定部分のみを上記ml5.jsによる判定に置き換えようと思ったのですが、描画の部分が上手く動かないため、先のCoding Trainのダニエル先生が使っている
というライブラリをそのまま使うことにしました。
Processingという映像や音楽に使われているライブラリをJavaScript用のライブラリにしたものだそうです。実装!
ぶっちゃけ手旗信号の原画の判定は、条件付きであればCoding Trainサイトのサンプルのみで実現できます。
条件というのは、画面の大きさが固定だったり画面内の位置に縛りがあったりとか、その辺の課題への対応を中心に作業の流れを書きます。姿勢データの収集
まず手旗信号の各原画1~14の17姿勢(詳しくは前記事参照)のデータを収集します。
Coding Trainの「Data Collection」というサンプルがこれにあたるのですが、こちらではPoseNetを使って、判定したい姿勢の17ポイントの座標を収集して、jsonファイルで出力されます。
操作方法としては、「poseNet ready」という表示後、アルファベットのキー("s"と"t"以外)を押下すと5秒後「collecting」となって収集を開始します。10秒後「not collecting」に表示が変わると押したキーの文字に割り当てられるデータの収集終了です。これを必要な姿勢分実行し、最後に"s"を押すと結果がjsonファイルに出力されます。ですがこれで得られる座標が絶対座標というか、収集する時の画面の座標なので、これが画面を変えたときに判定が渋い原因なのかなと。
ですので下図の通り、身体の中心(赤点)と鼻を結んだ距離(緑線)を単位距離とし、鼻、上半身の中心それぞれのポイントから各関節の距離(黄線)を正規化した値を収集することにしました。
flag_sem_util.jsconst distance = (x0, y0, x1, y1) => Math.hypot(x1 - x0, y1 - y0); function pose_normalize(keypoints){ inputs = []; let x0 = keypoints[NOSE].position.x, y0 = keypoints[NOSE].position.y; let x1 = getCenterCoord(keypoints).x, y1 = getCenterCoord(keypoints).y; if(x1 == undefined || y1 == undefined){ return null; } let basedist = distance(x0, y0, x1, y1); if(basedist == float.NaN || basedist == 0){ return null; } for (let i = RIGHTEYE; i <= RIGHTHIP; i++) { let l0 = distance(x0, y0, keypoints[i].position.x, keypoints[i].position.y) / basedist; let l1 = distance(x1, y1, keypoints[i].position.x, keypoints[i].position.y) / basedist; inputs.push(l0); inputs.push(l1); } return inputs; }上記のソースコードで手旗原画の17姿勢を収集しjsonファイルが得られます。
姿勢データを学習させる
これは簡単です。上記収集で得られたjsonファイルをml5のニューラルネットワークオブジェクトに食わせるだけです。
Coding Trainのサンプルをほぼそのまま使えるのですが、読み込ませるポイント数と出力数を変更する必要があります。今回は2つの距離を正規化したものと、上半身の判定に使う関節分12ポイントなのでinputsを24、17姿勢なのでoutputsを17にしました。
loading_data/sketchs.jslet options = { inputs: 24, outputs: 17, task: 'classification', debug: true }これを実行し学習が終了するとモデルファイルが出力されます。
- model.json: //モデルのデータ
- model_meta.json: //メタデータ(モデルの付随情報)
- model.weights.bin: //読み取った姿勢と正解の結びつきの重みバイナリデータ(多分)
姿勢判定
学習して得られた3つのモデルファイルをml5に読み込ませます。
flag_semaphore_reading.jsbrain = ml5.neuralNetwork(options); const modelInfo = { model: 'model/model.json', metadata: 'model/model_meta.json', weights: 'model/model.weights.bin', }; brain.load(modelInfo, brainLoaded);あとはclassifyPose()をコールバックしてリアルタイムに読み取り距離で正規化したデータで分類判定させます。
判定された原画でカナを判定するのは前回のロジックをそのまま使いました。まとめ
ソースコード
読取りサイト
https://hiroshi32yoshida.github.io/flag_semaphore_reading/
課題
心なしか前回版より精度は上がっている気がしないでもないですが…うーーん…気のせいだなぁ?
スマホで使えない
ml5.jsの影響かな?
私の環境iPhone8+Safariでは動きませんでした。
そもそもAppleはブラウザからカメラの使用は推奨しないという噂もどこかで見ましたが…前回版は動くんだよなぁ手旗を持っていると読み取れない
手旗信号なのに!
関節が手旗に隠れてしまっているとPoseNetの姿勢推定が渋くなるみたいです。
ですので原画「6」とか手旗を持っていなくても場合によって読取りが渋いです。「11」ももちろんですが、「9」とかも。画面の端に近いと読取りが上手くいかない
このための対策をしているはずなんだけどな…
誤判定
似ている原画や体格の違いやカメラのアングルによってまだまだ不安定です。
学習させるパラメータを増やすとか、子供のデータを学習させるとか何か方法はあると思います。勉強します。さいごに
(日本の)手旗信号を考えた人に言いたいこと
何で「11」をジェスチャーにしたの!?
全国のボーイスカウト指導者のみなさん
練習程度には使えると思います。おうちスカウティングなどに是非!
- 投稿日:2021-01-18T22:43:21+09:00
GASでシフト自動作成ソフト作ってみた
作成したスプレッドシート↓↓
https://docs.google.com/spreadsheets/d/1D1Shq5GaVnSMqiSf3hxXynWdYd79-gQXjZP221GnVQ4/edit?usp=sharing-仕様-
従業員は
R(レギュラー)、H(派遣)、P(パート)、A(アルバイト)の4種類
Rは基本営業時間内でシフトタイム、それ以外は希望時間内で早(早番)、中(中番)、遅(遅番)に振り分けるそれぞれに希望休(日)、希望休(曜)、有給、週何回出勤、勤務時間の入力欄を準備その入力内容をもとにシフトを作っていく。
シート(月予定表)の「シフト作成ボタン」を押して月の出勤日を作成、
その後ドロップダウンリストで作成したい日数分(最大7日)の日にちを選択し「日にちシフト作成ボタン」を押して日にちごとのシフトを作成。-改善点-
少人数であれば問題なく動くが人数が多くなるにつれてエラーが出やすくなる。 → 元々のアルゴリズムを考え直さないといけない。
- 投稿日:2021-01-18T21:10:39+09:00
「lazyload 対応」スムーズスクロールの書き方
どうも7noteです。lazyloadを入れていてもスムーズにページ内リンクできる方法
以前表示速度が少し心配なサイトがあり、lazyload属性を使いつつもでもページ内リンクをさせたい場面がありました。
もし対策せずにどちらも入れてしまうと、画像が読み込まれていないかページ内リンクの高さ取得が上手くできず
画像が後から読み込まれることで高さが変わってしまい、本来の位置よりも上の方に移動してしまいます。ですがjQueryのwhenを使うことでこの問題を解決することができます。
lazyload対応スムーズスクロールの書き方
※jQueryを使用しています。jQueryってなんだという方はこちら
$(function () { $('a[href^=#]').click(function(e) { var headerHight = 50; /* ヘッダーの高さ(50px) */ var href = $(this).attr("href"); var target = $(href == "#" || href == "" ? 'html' : href); var position = target.offset().top - headerHight; $.when( $("html, body").animate({ scrollTop: position }, 400, "swing"), e.preventDefault(), ).done(function() { var diff = target.offset().top - headerHight; if (diff === position) { } else { $("html, body").animate({ scrollTop: diff }, 10, "swing"); } }); }); });詳しい動きの解説は参考にさせていただいたページの書き方に載っていますので省略させていただきます。
参考:https://kaiteki-chokin.com/anchor-link-lazy-loading/
whenについて簡単に解説
whenを使うことで、複数の非同期処理が全部終わったら、続く処理を行うことができます。
今回の処理ではこれを利用し、画像がlazyloadでちゃんと読み込まれた後に正しい位置までのスクロール処理を行なっています。まとめ
jsは様々な便利なライブラリやソースがネットにあるので1つのページに様々な種類のソースを書くことは珍しくありません。
ただ、その動き方やソースの内容や特性をしっかり理解できないと相性が悪く上手く動かない事もあります。なので自分がソースを書くことも大事ですが、他人のソースを読めることも大事だと思います。
おそまつ
~ Qiitaで毎日投稿中!! ~
【初心者向け】WEB制作のちょいテク詰め合わせ
- 投稿日:2021-01-18T20:45:07+09:00
React-Calendarで超簡単にカレンダーを作ってみた
会社のイベントとして、2020年のアドベントカレンダーで記事を書いたのですが、個人のQiitaでも同じものを公開したいな〜と思ったので、ちょっとだけ変えて公開します!
去年の案件で急にReactでカレンダーを作ることになり、有識者もいない中、四苦八苦したのですが、ライブラリって便利だな〜と感じたので、Reactのreact-calendarを使ってカレンダーを作成したときのことを書きます。
※環境構築とかは端折ります。
react-calendarとは
カレンダー実装だともっとメジャーなライブラリがあるかも知れないんのですが、今回、私はreact-calendarを使ってみたのですが、簡単にReactでカレンダーを実装できちゃいました。
条件を絞って、日付ごとに任意の内容を表示させたり、スタイルのカスタマイズなど 柔軟に対応できます。
react-calendarで実装してみる
react-calendarをインストール
yarnで「react-calendar」をインストールします。
yarn add react-calendarreact-calendarをimportする
react-calendarをimportしてCalendarコンポーネントを呼び出します。
CalenderCmponent.jsximport Calendar from 'react-calendar' export default class Calendar extends Component { render() { return( <Calendar /> ) } }これだとまだ動きません!
カレンダーに日付を表示する
カレンダーのタイルに日付(日本時間)を表示します。
CalenderCmponent.jsximport Calendar from 'react-calendar' export default class Calendar extends Component { render() { return( <div> <Calendar locale="ja-JP" value={this.state.date} /> </div> ) } }それぞれのPropsについては公式だと以下のように説明されています。(英語のドキュメントのみ、、、)
Description Description Default value Example values locale Locale that should be used by the calendar. Can be any IETF language tag. User's browser settings "hu-HU" value Calendar value. Can be either one value or an array of two values. If you wish to use React-Calendar in an uncontrolled way, use defaultValue instead. "n/a" ・Date: new Date()
・An array of dates: [new Date(2017, 0, 1), new Date(2017, 7, 1)]これで、カレンダーの作成は完了です。とても簡単!!
カレンダーに任意のアイテムを表示してみる
これだけだと寂しいので、日付タイルに任意のアイテムを表示させてみたいと思います。
CalenderCmponent.jsximport Calendar from 'react-calendar' export default class Calendar extends Component { constructor(props) { super(props); this.state = { date: new Date(), //テストデータ month_item: { 2020-12-01: { text: 'work' }, 2020-12-10: { text: 'hangout' }, 2020-12-24: { text: 'Christmas Eve' }, 2020-12-25: { text: 'Christmas' }, } } }; //日付の内容を出力 getTileContent({ date, view }) { if (view === 'month') { const targetDate = moment(date).format('YYYY-MM-DD') return month_item[targetDate] && month_item[targetDate].text ? <div> <p>{month_item[targetDate].text}</p> </div> : null } } render() { return( <div> <Calendar locale="ja-JP" value={this.state.date} tileContent={this.getTileContent.bind(this)} //ここを追加 /> </div> ) } }tileContentの詳細の説明は以下です。functionを呼び出して、returnされたものを渡せば任意の日付に内容を表示させることができます。
Description Description Default value Example values tileContent Allows to render custom content within a given calendar item (day on month view, month on year view and so on). n/a ・String: "Sample"
・React element:
・Function: ({ activeStartDate, date, view }) => view === 'month' && date.getDay() === 0 ? It's Sunday! : nullまとめ
Reactって便利〜! 今回の記事では説明していませんが、Reduxと合わせてDBからデータを引っ張ってきてstateで制御すれば、スケジュールを登録したり、表示させたりすることも可能でした!
今回記載している以外にもたくさんPropsの種類もあるので、気になる方はぜひ公式URLから見てみてください!
参考
・React-Calendar が便利 | バシャログ。
・Reactでカレンダー作成(React-Calendarライブラリ)
- 投稿日:2021-01-18T20:28:19+09:00
ポートフォリオサイトの制作
はじめに
自分のスキルや成果物の可視化やGithub以外のアウトプットを一元管理するため、ポートフォリオサイトを作成しました。
今回は作成する上で気をつけたことや使用したツール、ホスティング方法について備忘録としてまとめたいと思います。
URL: https://seiyaiwanabe.github.io/portfolio_site/
目次
1.記載内容
2.使用したサービス
3.Github Pages1. 記載内容
ポートフォリオサイトを作成した理由は主に企業の採用担当者の方に見てもらうためです。
「誰にポートフォリオサイトを見せたいのか」という要素は最も大事だと思うのでここを決めることでサイト全体の構成も決まってくると思います。今回は以下の要素を記載しました。
- 使用した言語(技術)
- 作品紹介
- 開発に使用したツール
- アウトプットへのリンク(今回はQiita)
また工夫した点としてサイト全体に
アニメーション
を盛り込みました。
JSの練習になるのと少しでもビジュアルを魅力的にするためです。ただし、多様しすぎるとエゴになってしまうので、線引が難しいですが、
情報を整理するという目的を忘れないことが大事だと思いました。アプリ自体は
Ruby on Rails
で作成し、インフラにAWSを使ってデプロイしています。2. 使用したサービス
コーディングはイチからしました。
bootstrap
やテンプレートの類は使用していません。
自分の手を動かして少しでもプログラミングに慣れたかったためです。そのため使用した外部サービスは
FontAwesome
とFavicon作成ツール
にとどまりました。FontAwesome(ヘッダーの中のアイコンに使用)→https://fontawesome.com/
Faviconの素材(159,700のフリーアイコンが集まるサイト)→https://icons8.jp/
Faviconは拡張子がjpgやpngでも表示させることが出来ます。
まずダウンロードした素材のファイル名をicon.png
に変更しimagesディレクトリ
に格納します。
図にするとこのような階層になります。↓あとはhtmlファイルに一行追記するだけです。
index.html<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="style.css"> <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css"> # FontAwesome導入 <link rel="icon" type="image/x-icon" href="images/icon.png"> # 追記 <title>Document</title> </head>これでアイコンが表示されます。
3. Github Pages
GitHub Pages は、GitHub のリポジトリから HTML、CSS、および JavaScript ファイル を直接取得し、任意でビルドプロセスを通じてファイルを実行し、ウェブサイトを公開できる静的なサイトホスティングサービスです。(Github Dogs)
こちらの引用でも記載してる通り、静的なサイトのためのホスティングサービスなので動的なサイトをホスティングさせることは出来ません。なのでRailsアプリはAWSでデプロイを行っています。
今回のポートフォリオサイトのようにサーバーサイドに関与しないコンテンツを世界に公開する手段としては
Github Pagesは無料で利用できて、プロセスが簡単なのでおすすめです。おわりに
最後まで読んでいただきありがとうございました!
お疲れさまでした。。。
- 投稿日:2021-01-18T20:16:41+09:00
Node.jsでAzure IoT Hubにテレメトリを送信するサンプルコードを動かすメモ
はじめに
デバイスから IoT ハブに利用統計情報を送信してバックエンド アプリケーションで読み取るを参考にサンプルコードを動かしてみます。
接続文字列はAzure CLIを使わずに、コンソールから取得します。Azure IoTについてはこちらを参照。
準備
IoTデバイスを登録する
Azure IoT Hubコンソール > サイドバー > IoTデバイス > 新規
からデバイスを追加する。追加できたら、追加したデバイスを選択して
プライマリ接続文字列
をコピーしておく。データを送信する
ダウンロードしたサンプルコードから、
SimulatedDevice.js
を開き、17行目あたりのconnectionString
に先程コピーしたプライマリ接続文字列
を貼り付ける。コードはこんな感じになる。
var connectionString = '{Your device connection string here}'; // ←ここに`プライマリ接続文字列`を貼り付ける。 var Mqtt = require('azure-iot-device-mqtt').Mqtt; var DeviceClient = require('azure-iot-device').Client var Message = require('azure-iot-device').Message; var client = DeviceClient.fromConnectionString(connectionString, Mqtt); setInterval(function(){ // 送信するデータを生成する。(本当はセンサーで測定した値を入れるがシミュレーションのため乱数を使用) var temperature = 20 + (Math.random() * 15); var message = new Message(JSON.stringify({ temperature: temperature, humidity: 60 + (Math.random() * 20) })); message.properties.add('temperatureAlert', (temperature > 30) ? 'true' : 'false'); console.log('Sending message: ' + message.getData()); // イベントを送信する client.sendEvent(message, function (err) { if (err) { console.error('send error: ' + err.toString()); } else { console.log('message sent'); } }); }, 1000);以下のコマンドをターミナルで実行する。
$ node SimulatedDevice.jsデータが送信されている。
送信したデータを受信する
ダウンロードしたサンプルコードから、
ReadDeviceToCloudMessages.js
を開き、38行目あたりのconnectionString
にAzure IoT Hubコンソール > サイドバー > 組み込みのエンドポイント > イベントハブ五感エンドポイント
の値を貼り付ける。
(26行目あたりにeventHubsCompatibleEndpoint
などの変数が用意されていますが、無視します。)コードはこんな感じになる。
const connectionString = `Endpoint=**********************;SharedAccessKeyName=*****************;EntityPath=********************`; var printError = function (err) { console.log(err.message); }; var printMessages = function (messages) { for (const message of messages) { console.log("Telemetry received: "); console.log(JSON.stringify(message.body)); console.log("Properties (set by device): "); console.log(JSON.stringify(message.properties)); console.log("System properties (set by IoT Hub): "); console.log(JSON.stringify(message.systemProperties)); console.log(""); } }; async function main() { console.log("IoT Hub Quickstarts - Read device to cloud messages."); const clientOptions = { // webSocketOptions: { // webSocket: WebSocket, // webSocketConstructorOptions: {} // } }; const consumerClient = new EventHubConsumerClient("$Default", connectionString, clientOptions); // イベントハブのストリームからデータを取得する consumerClient.subscribe({ processEvents: printMessages, processError: printError, }); } main().catch((error) => { console.error("Error running sample:", error); });実行する。
$ node ReadDeviceToCloudMessages.js
受信できた。
まとめ
公式のデバイスから IoT ハブに利用統計情報を送信してバックエンド アプリケーションで読み取るを動かしました。
また、Azure CLIを使わずにコンソールから接続文字列を取得しました。Azure IoT Hubの
S1
スケールは月に2~3000円ほどかかるので、試し終わったら忘れずに止めておきましょう。次回は、M5系のデバイスからデータを送信してみます。
- 投稿日:2021-01-18T19:53:41+09:00
自分の町の避難所情報をGoogle Mapsに表示する
オープンデータの活用とGoogle Maps APIの練習を兼ねて自分の町の避難所情報をGoogle Mapに表示してみました。さらに避難所までのルートも表示させてみます。
避難所情報の取得
まずは大刀洗町オープンデータカタログ https://odcs.bodik.jp/405035/ から避難所情報を取得してみます。
CKAN Data APIのサンプルを参考に以下のようにAPIにアクセスします。$.ajax({ url: 'https://data.bodik.jp/api/3/action/datastore_search', data: { resource_id: '1bb3a1f5-db97-488f-92f4-aa72e497e6d1', limit: 5 }, dataType: 'jsonp', cache: true, // これを付けないと引数のタイムスタンプで何故かエラーが出る success: function(data) { console.log(data.result) } });CORSに対応するためにjsonpを使っていたりしますがその辺り今回は割愛。
ブラウザのコンソールにjson情報が表示されたら成功です。Googleマップに表示させる
取得したjson情報をGoogleマップに表示させてみましょう。
Google Maps APIを使用するには、
https://cloud.google.com/maps-platform?hl=ja
にてAPIキーを取得しなければなりませんが詳細は割愛します。緯度と経度を取得してマーカーを立てます。さらにマーカーをクリックしたときに簡単な詳細情報ウィンドウを表示させてみました。
<!DOCTYPE html> <html> <head> <title>大刀洗町避難所マップ</title> <script src="https://code.jquery.com/jquery-1.11.1.min.js"></script> <script src="https://maps.googleapis.com/maps/api/js?key=APIキー&callback=initMap&libraries=&v=weekly" defer ></script> <style type="text/css"> #map { height: 100%; } html, body { height: 100%; margin: 0; padding: 0; } </style> <script> function initMap() { const map = new google.maps.Map(document.getElementById("map"), { zoom: 14, center: { lat: 33.3881, lng: 130.6127}, }); // 避難所情報取得 $.ajax({ url: 'https://data.bodik.jp/api/3/action/datastore_search', data: { resource_id: '1bb3a1f5-db97-488f-92f4-aa72e497e6d1', limit: 30 }, dataType: 'jsonp', cache: true, success: function(data) { // マーカー for (const record of data.result.records) { const marker = new google.maps.Marker({ position: {lat: record.緯度, lng: record.経度}, map: map, title: record.名称, }); marker.addListener('click', function(){ // 詳細ウィンドウ const infoWindow = new google.maps.InfoWindow({ content: `<h3>${record.名称}</h3><p>${record.住所表記}<br>${record.避難施設種別}</p>` }); infoWindow.open(map, marker); }); } } }); } </script> </head> <body> <div id="map"></div> </body> </html>避難所までのルートを表示する
これだけだと物足りないので、避難所までのルートを表示させてみましょう。
ルート表示を行うためには事前にGoogle Map APIのDirections APIを有効化しておく必要があります。
今回の交通手段はWALKING(徒歩)を設定します。function initMap() { var currentPos = { lat: 33.3881, lng: 130.6127 }; const map = new google.maps.Map(document.getElementById("map"), { zoom: 14, center: currentPos, }); const directionsService = new google.maps.DirectionsService(); const directionsDisplay = new google.maps.DirectionsRenderer(); directionsDisplay.setMap(map); $.ajax({ url: 'https://data.bodik.jp/api/3/action/datastore_search', data: { resource_id: '1bb3a1f5-db97-488f-92f4-aa72e497e6d1', limit: 30 }, dataType: 'jsonp', cache: true, success: function(data) { for (const record of data.result.records) { const marker = new google.maps.Marker({ position: {lat: record.緯度, lng: record.経度}, map: map, title: record.名称, }); marker.addListener('click', function(){ const infoWindow = new google.maps.InfoWindow({ content: `<h3>${record.名称}</h3><p>${record.住所表記}<br>${record.避難施設種別}</p>` }); infoWindow.open(map, marker); // ルート情報 var request = { origin: currentPos, destination: new google.maps.LatLng(record.緯度, record.経度), travelMode: google.maps.TravelMode.WALKING } directionsService.route(request, function(result, status) { if (status == google.maps.DirectionsStatus.OK) { directionsDisplay.setDirections(result); } }); }); } } }); }さらなる発展
現在位置の取得(navigator.geolocation.getCurrentPosition)を使うとさらに実用的になるのですが、コードが長くなったのでまたの機会に。
- 投稿日:2021-01-18T19:48:08+09:00
JavaScriptフレームワークのプロジェクト作成コマンドを列挙した
はじめに
JavaScript のフレームワークには npm や Yarn などのパッケージマネージャーを使い簡単に依存関係を解決し、プロジェクトの雛形を作成してくれるコマンドが用意されていることが多いです。
スクラッチでの作成もできますが、コマンドラインツールによって簡単にプロジェクトの自動作成、開発サーバーでの実行ができ便利だと感じたため、有名な JavaScript フレームワークの雛形作成コマンドを備忘録として挙げてみます。各コマンドの
<project-name>
の部分が作成されるディレクトリ名、デフォルトのプロジェクト名になります。作成するプロジェクトに応じて適宜置き換えてください。公式のドキュメントに沿って基本となるコマンドをまとめています。プロジェクト作成時に対話形式で設定可能な項目については記事内で扱いません。項目の説明や、利用できるオプションについては公式のドキュメントを参照のうえ実行をお願いします。
前提条件
- node.js がインストールされていること(バージョン 8 以降、最新の LTS バージョンを推奨)
- npm もしくは Yarn が利用可能であること(npm は node.js にデフォルトで搭載)
Vue
npm
npm install -g @vue/cli vue create <project-name> cd <project-name> npm run serveYarn
yarn global add @vue/cli vue create <project-name> cd <project-name> npm run serve
http://localhost:8080 で開発サーバが立ち上がります。
Nuxt.js
npm
npm init nuxt-app <project-name> cd <project-name> npm run dev (yarn dev)npx
npx create-nuxt-app <project-name> cd <project-name> npm run dev
Yarn
yarn create nuxt-app <project-name> cd <project-name> yarn dev
http://localhost:3000 で開発サーバーが立ち上がります。
React
npm
npm init react-app <project-name> cd <project-name> npm start
npx
npx create-react-app <project-name> cd <project-name> npm start
Yarn
yarn create react-app <project-name> cd <project-name> yarn start
npm、npx でインストールする場合は Yarn がインストールされているとデフォルトで Yarn が使用されます。
http://localhost:3000 で開発サーバーが立ち上がります。
Next.js
npm
npx create-next-app <project-name> cd <project-name> npm run dev
Yarn
yarn create next-app <project-name> cd <project-name> yarn dev
React と同じく npm、npx でインストールする場合は Yarn がインストールされているとデフォルトで Yarn が使用されます。
http://localhost:3000 で開発サーバーが立ち上がります。
Angular
npm
npm install -g @angular/cli ng new <project-name> cd <project-name> ng serve --openYarn
npm install -g @angular/cli ng config -g cli.packageManager yarn ng new <project-name> cd <project-name> ng serve --open
ng serve --open
とすることでデフォルトのブラウザで起動します。
http://localhost:4200 で開発サーバーが立ち上がります。Svelte
公式で公開されているテンプレートから新規プロジェクトを作る。
npx
npx degit sveltejs/template <project-name> cd <project-name> npm install npm run devhttp://localhost:5000 で開発サーバーが立ち上がります。
おわりに
有名な JavaScript フレームワークの雛形プロジェクト作成コマンドを公式ドキュメントをもとに列挙してみました。簡単なコマンド 1 つですぐに開発を始める環境が作成できるのはとても便利です。フレームワークがここまで発展し、多くの人に選ばれる理由わかった気がします。
公式ドキュメントを見ながら記事を執筆する中で得た気づきが多くあるため、今後 JavaScript フレームワークについて更に理解を深めていきたいです。
以上、ここまで読んでいただきありがとうございました。
参考ドキュメント
Creating a Project | Vue CLI
インストール - NuxtJS
新しい React アプリを作る - React
facebook/create-react-app: Set up a modern web app by running one command.
Create Next App | Next.js
Angular 日本語ドキュメンテーション - ローカル環境とワークスペースのセットアップ
【2019 年 5 月】yarn で @angular/cli
The easiest way to get started with Svelte
sveltejs / template Template for building basic applications with Svelte
- 投稿日:2021-01-18T18:19:58+09:00
【React初心者】 #3 ルーティング・ページ遷移を作る! react-router-dom
Reactでreact-router-domのルーティング設定
3回目の今回は、Reactでreact-router-dom
を使用したルーティングを使います。
URLを変えることで、表示するページを変える(レンダリングする)ことができます。前回までで、
- Material-UIで最低限のデザインを一から作成
- データをAPIから取得して表示
しました。コードは前回の続きとなります。
シリーズ記事
やること
- react-router-domでのReactでのルーティング
- ルーティングするためのリンクの付け方
react-router-dom導入
まず、インストールが必要です。
今回はreact-router-dom: 5.2.0
を使用しています。$ npm install react-router-domTypeScriptの場合は
npm install react-router-dom @types/react-router-dom
みたいです。とりあえず、aboutページを作成します。
ただ、文字を表示するだけにします。ということで、Aboutコンポーネントを作成。
$ touch src/components/About.js文字を表示するだけになります。
src/components/About.jsimport React from 'react' function About() { return ( <div> aboutページ </div> ) } export default About前回までのコードにRouterとSwitch、Routeを足して、
URLによって、レンダリングするコンポーネントを変えています。material-react/src/App.jsimport './App.css'; import { Grid } from '@material-ui/core'; import Header from './components/Header'; import Content from './components/Content'; import About from './components/About'; import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; function App() { return ( <Grid container direction="column"> <Grid item> <Header /> </Grid> <Grid item container> <Grid sm={2} /> <Grid xs={12} sm={8}> <Router> <Switch> <Route exact path="/"> <Content /> </Route> <Route path="/about"> <About /> </Route> </Switch> </Router> </Grid> <Grid sm={2} /> </Grid> </Grid> ); } export default App;とURLに入れると、
文字だけのAboutコンポーネントが表示されています。
説明と注意
<Route exact path="/">
のexact
は完全に一致しないとダメという意味です。一致するURLを上から探すのだけど、このexactをつけないと、
http://localhost:3000/jasdklfs
とか意味ないものやでもContent`コンポーネントを使うことになります。というか、
http://localhost:3000/
移行にURLに、何があろうがContent
コンポーネントを使います。試しに、exactを消して/aboutにアクセスしてみれば、
Content
コンポーネントが出るので、試してみてもいいと思います。
<Route path="/">
変更後、
http://localhost:3000/jasdklfs
とhttp://localhost:3000/about
と書いてみましょう。どちらも、Contentコンポーネントの内容が表示されます。
「書く順番」を考えましょう。URLのパラメータを取得して個別記事表示
前回、JSON Placeholderからデータを取得しました。
APIをみると、個別記事の取得もできるので、
記事についている「詳細を見る」ボタンを押したら遷移させます。個別記事のコンポーネントを作成
前回までで、左側は作りました。
今回は、右側を作ります。
個別記事IDにアクセスしたときにPostContentコンポーネントをレンダリングすることになります。
$ touch src/components/PostContent.js
/post/1
だったら記事1を表示するようにしてみます。とりあえず
idを受け取れるかテスト。
/post/1
にアクセスしたらuseParams()
に{id: 1}
のようなデータが入るのでid
だけをとるためconst { id }
にしています。そして、個別記事の後に受け取ったIDをくっつけて表示します。
material-react/src/components/PostContent.jsimport React from 'react' import { useParams } from 'react-router-dom' function PostContent() { const { id } = useParams(); return ( <div> <h1>個別記事 {id}</h1> </div> ) } export default PostContent
http://localhost:3000/post/1
にアクセスしたら1
が表示されているのでOK
http://localhost:3000/post/10
とかにしたら10
に変わりますので試してみてください。個別記事データをAPIで取得
Getで取得しましょう。URLの最後に記事のIDを入れればデータを取得できます。
では、JSON PlaceholderでAPIのURLは
{ "userId": 1, "id": 1, "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto" }axiosを使ったAPIのデータ取得は前回やったので簡単に書きます。
material-react/src/components/PostContent.jsimport React, {useState, useEffect} from 'react'; import axios from 'axios'; import { useParams } from 'react-router-dom' import { Grid } from '@material-ui/core' import BodyCard from './BodyCard' const cardContent = { avatarUrl: "https://joeschmoe.io/api/v1/random", imageUrl: "https://picsum.photos/150" } function PostContent() { const { id } = useParams(); const [post, setPosts] = useState([]) useEffect(() => { axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`) .then(res => { setPosts(res.data) }) }, []) return ( <Grid container spacing={2}> <Grid item xs={12} key={post.id}> <BodyCard {...{...post, ...cardContent}} /> </Grid> </Grid> ) } export default PostContent
https://jsonplaceholder.typicode.com/posts/${id}
でURLから取得したパラメータを埋め込んでGET、APIでの取得- useStateを使って、ページを開いた時一度だけ、APIで取得
- 前回作った、
BodyCard
コンポーネントに渡してMaterial-UIのカードの形式で作成
{...{...post, ...cardContent}}
で二つのオブジェクトを結合して渡してますとりあえず、ページを作り込むより、ルーティングの使い方説明メインなので、
- 詳細をみるというボタンがあったり、
- 画像がAPIでランダム取得するので変わってしまう、
- 画像がhight: 150pxだと大きさおかしい
一応、前回の記事から変更はないですが、BodyCardコンポーネントも載せておきます。
BodyCard.jsimport React from 'react'; import { makeStyles } from '@material-ui/core/styles'; import Card from '@material-ui/core/Card'; import CardActions from '@material-ui/core/CardActions'; import CardContent from '@material-ui/core/CardContent'; import Button from '@material-ui/core/Button'; import Typography from '@material-ui/core/Typography'; import CardHeader from '@material-ui/core/CardHeader'; import Avatar from '@material-ui/core/Avatar'; import IconButton from '@material-ui/core/IconButton'; import StarBorderOutlinedIcon from '@material-ui/icons/StarBorderOutlined'; import { CardMedia } from '@material-ui/core'; const useStyles = makeStyles({ bullet: { display: 'inline-block', margin: '0 2px', transform: 'scale(0.8)', }, title: { fontSize: 14, }, pos: { marginBottom: 12, }, cHeader: { height: '50px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', "& .MuiCardHeader-content": { overflow: 'hidden' } }, cContent: { height: '200px', overflow: 'hidden' } }); function BodyCard(props) { const { userId, id, title, body, avatarUrl, imageUrl } = props; const classes = useStyles(); const bull = <span className={classes.bullet}>•</span>; return ( <Card variant="outlined"> <CardHeader avatar={<Avatar src={avatarUrl} />} action={ <IconButton aria-label="settings"> <StarBorderOutlinedIcon /> </IconButton> } className={classes.cHeader} title={title} /> <CardMedia style={{ height: "150px" }} image={imageUrl} /> <CardContent className={classes.cContent}> <Typography variant="body2" component="p"> {body} </Typography> </CardContent> <CardActions> <Button size="small">詳細をみる</Button> </CardActions> </Card> ); } export default BodyCardボタンにリンクをつける
Material-UIにはButtonにhrefつければ遷移できるようになります。
以下のようにすれば大丈夫。BodyCard.js<Button size="small" href={`/post/${id}`}>詳細をみる</Button>今回、Buttonにリンク機能がついていたので使いましたが、
デザインを自分でやりたい、文字にリンクつけるなどの場合は、<Link to="/post/1">記事1の詳細</Link> <Link to=`/post/${id}`>記事{$id}の詳細</Link>などにすればいいと思います。
react-routerの公式サイトは英語ですが、
コードがみやすいのでみながら試してみてください。まとめ
- react-router-domでのReactでのルーティング
- ルーティングするためのリンクの付け方
をやりました。次回はAPIサーバーをPythonのDjangoで自作します。
補足
これまで、RouterをApp.jsに書きました。
ただ、ヘッダーがいらない、デザインを変えたいなどの場合(ログインページ、LP、認証のためのURLとか使う場合など)、
index.js
にrouterを書くこともできるので補足として。index.jsimport React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import About from './components/About'; import reportWebVitals from './reportWebVitals'; import { Route, BrowserRouter } from "react-router-dom"; ReactDOM.render( <React.StrictMode> <BrowserRouter> <div> <Route exact path="/" component={App} /> <Route exact path="/about" component={About} /> </div> </BrowserRouter> </React.StrictMode>, document.getElementById('root') );
- 投稿日:2021-01-18T17:58:51+09:00
【React hooks】噛み砕いて解説してみた~useContext編~
前書き
16.8v
で追加された機能であるreact hooks
を理解を深めるために体系的にまとめました。
- 【React hooks】噛み砕いて解説してみた~useState編~
- 【React hooks】噛み砕いて解説してみた~useEffect編~
- 【React hooks】噛み砕いて解説してみた~useContext編~ ←今ココ
以下、本題です
そもそもContextとは
別コンポーネントに値を動的に渡す方法として、
Props
があると思います。Context
も同じように値を動的に渡すことができます。Contextの存在意義
多重ネストされたコンポーネントに値を渡すときに、下記のようにバケツリレーになってしまうことが多々あると思います。(コードは公式ドキュメントからコピペしました)
<Page user={user} avatarSize={avatarSize} /> // ... Page コンポーネントは以下をレンダー ... <PageLayout user={user} avatarSize={avatarSize} /> // ... PageLayout コンポーネントは以下をレンダー ... <NavigationBar user={user} avatarSize={avatarSize} /> // ... NavigationBar コンポーネントは以下をレンダー ... <Link href={user.permalink}> <Avatar user={user} size={avatarSize} /> </Link>
Context
を使うことで、コンポーネントを超えて動的に値を渡すことができます。実際の使い方(hookなし)
まずは
useContext
を使わないパターンを解説していきます。そのほうが理解が深まるからです。(知っているよ!という人は飛ばしてください。)登場人物
createContext()
Proveider
Consumer
それぞれ軽く説明しておきます。
createContext()
const Context = React.createContext()この関数でコンテキストオブジェクトが作成されます。このコンテキストオブジェクトは下記のようになっています。
{ Provider: <>...<>, Consumer: <>...<>, ... }
Context.Provider
やContext.Consumer
のように使われます。実際に見ていきましょう。Provider
return ( <Context.Provider value={resource}> <Hoge /> </Context.Provider> )上記のように
Provider
のprops
のvalue
に渡したい値を指定してあげます。Consumer
const Hoge = () => ( <Context.Consumer> {(resource)=> ( <h1>{resource.title}</h1> )} </Context.Consumer> )上記のように実際に使いたいコンポーネント(今回は
Provider
ラップしたHoge
コンポーネント)でprops.child
に対して関数の引数で渡したい値(ここではresource
)を入れています。以上で
useContext
を使わないパターンの説明は終了です!以降から
useContext
を使ったパターンの解説をしていきます。useContextを使う場合
簡潔にお伝えすると
Consumer
、つまり実際に値を呼び出すコンポーネントで使うことができます。useContext
を使わない場合は下記のような形ですね。const Hoge = () => ( <Context.Consumer> {(resource)=> ( <h1>{resource.title}</h1> )} </Context.Consumer> )
useContext
を使う場合は下記のようにuseContext()
関数の引数にコンテキストオブジェクトを含めてください。const Hoge = () => { const { resource } = useContext(Context) return ( <h1>{resource.title}</h1> ) }
props.child
が不要になったのでスッキリしましたね!解説は以上です。お疲れ様でした?♂️
参考記事
- 投稿日:2021-01-18T17:40:27+09:00
React+reduxで数字をカウントしてみる
index.jsを用意します
import React from 'react'; import ReactDOM from 'react-dom'; import { createStore } from 'redux'; import { Provider } from 'react-redux'; import { persistStore, persistReducer } from 'redux-persist' import storage from 'redux-persist/lib/storage' import { PersistGate } from 'redux-persist/integration/react' import './index.css'; import App from './App'; // ステートの値 let state_value = { counter:0, message:"COUNTER" } // レデューサー function counter(state = state_value, action) { switch (action.type) { case 'INCREMENT': return { counter:state.counter + 1, message:"INCREMENT" }; case 'DECREMENT': return { counter:state.counter - 1, message:"DECREMENT" }; case 'RESET': return { counter:0, message:"RESET" }; default: return state; } } // Redux Persistの設定 const persistConfig = { key: 'root', storage, } // パーシストレデューサーの作成 const persistedReducer = persistReducer(persistConfig, counter) // ストア、パーシスターの作成 let store = createStore(persistedReducer) let pstore = persistStore(store) // 表示をレンダリング ReactDOM.render( <Provider store={store}> <PersistGate loading={<p>loading...</p>} persistor={pstore}> <App /> </PersistGate> </Provider>, document.getElementById('root') );ここではパーシストレデューサーをもとに、ストアとパーシスターを作成しました。
次にパーシスターとAPPコンポーネントをJSXで表示します。APP.jsを作成
import React, { Component } from 'react'; import { connect } from 'react-redux'; import './App.css'; // Appコンポーネント class App extends Component { constructor(props){ super(props); } render() { return ( <div> <h1>Redux</h1> <Message /> <Button /> </div> ); } } // ストアのコネクト App = connect()(App); // メッセージ表示のコンポーネント class Message extends Component { style = { fontSize:"20pt", padding:"20px 5px" } render(){ return ( <p style={this.style}> {this.props.message}: {this.props.counter} </p> ); } } // ストアのコネクト Message = connect((state)=>state)(Message); // ボタンのコンポーネント class Button extends Component { style = { fontSize:"16pt", padding:"5px 10px" } constructor(props){ super(props); this.doAction = this.doAction.bind(this); } // ボタンクリックでディスパッチを実行 doAction(e){ if (e.shiftKey){ this.props.dispatch({ type:'DECREMENT' }); } else if (e.ctrlKey){ this.props.dispatch({ type:'RESET' }); } else { this.props.dispatch({ type:'INCREMENT' }); } } render(){ return ( <button style={this.style} onClick={this.doAction}> click </button> ); } } // ストアのコネクト Button = connect()(Button); export default App;APP.js内に画面に表示するMessageコンポーネントとプッシュボタンのButtonコンポーネントを作成します。
Buttonコンポーネント内には、ボタンを押すINCREMENT、Shiftキー押しながらボタンを押すとDECREMENT、ctrlキーを押しながらボタンを押すとRESETのタイプを作りました。ボタンを押すとタイプごとのアクションをRedux送りそのタイプにあった処理が行われます。読んでくださりありがとうございます。
初学者なので至らないところがあると思います。
おかしい所があればご指摘願います。
- 投稿日:2021-01-18T17:14:10+09:00
【JavaScript】英単語数をカウントする
わりと簡単に作れると思っていましたが、
スペースがあったときに1カウントとすると、スペースが2つ続いたときも1単語としてカウントされてしまうことに頭を悩ませました。解決策
HTML
<textarea id="count-area"></textarea> <span id="output"></span>JavaScript
const input = document.getElementById('count-area'); input.addEventListener('keyup', countWords); function countWords() { // \S+の意味は「空白、タブ、改行以外が1回以上続く」 const spaces = input.value.match(/\S+/g); let words; if (spaces) { words = spaces.length; } else { words = 0; } document.getElementById('output').textContent = words + " words"; }参考
Count words as user type - Textarea and Javascript (Youtube)
正規表現入門 レッスン7 Space (Youtube)
- 投稿日:2021-01-18T17:08:49+09:00
Mapbox入門
Mapboxを使って地図を表示
CDN
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v/mapbox-gl.js'></script> <link href='https://api.tiles.mapbox.com/mapbox-gl-js/v/mapbox-gl.css' rel='stylesheet' />Mapbox GL JS / マイアカウントでアクセストークンを取得
mapboxgl.accessToken = 'hoge';mapboxglのMapオブジェクトを生成(インスタンス化)
const map = new mapboxgl.Map({ container: 'map', style: 'mapbox://hoge', center: [lon, lat], zoom: 14 });オプション
container
マウントするdiv要素(not ネスト)<div id="map"></div>style
マップのスタイルcenter
[longitude, latitude]中心地の緯度経度
zoom
ズーム度現在地を追跡
map.addControl( new mapboxgl.GeolocateControl({ positionOptions: { enableHighAccuracy: true }, trackUserLocation: true }) );オプション
addControl
UIでmapを操作GeolocateControl
位置情報を操作(類似:Geolocation API)ジオロケーション
ユーザーの位置情報を扱う技術enableHighAccuracy
・false:デフォルト
・true:高精度で現在地を取得trackUserLocation
詳細不明
trueにするとアニメーションがつくギアマークをクリックで現在地まで追跡
next
リアルタイムに自分の居場所を保存し取り出せる
Webページにいる人それぞれの位置をマップに表示
- 投稿日:2021-01-18T17:06:32+09:00
JavaScriptのreduce()がしていること
配列の合計値を出したいときなどに便利な
reduce()
。
基本的な使い方は以下。const arr = [1, 2, 3, 4, 5]; console.log(arr.reduce((n, m) => n + m)); // 15勉強をしていて、「なんとなく合計値を出せるもの」という理解をもう一歩深められましたのでシェアします。
reduce()の構文と処理内容
僕がいつもお世話になっているMDNによると、構文は次の通りです。
arr.reduce(callback( accumulator, currentValue[, index[, array]] ) { // return result from executing something for accumulator or currentValue }[, initialValue]);参考:https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
callback
:配列のすべての要素に対して実行される関数
accumulator
:reduce()の返り値を記憶する一時的な変数(繰り返しにおける前回の実行結果)
currentValue
:現在処理される配列要素最初の例だとそれぞれが次のように対応しています。
callback
:(n, m) => n + m
accumulator
:n
currentValue
:m
そして、反復処理の実行手順をひとつひとつ見ていくと以下のようになります。
実行回数 n m 実行結果(次のn) 1回目 1 2 3 2回目 3 3 6 3回目 6 4 10 4回目 10 5 15 こうして簡単に配列の合計値を出すことができました。
昔C言語でint sum = 0;
で合計値を保持する変数を用意してforループを回していた時代はなんだったのか…。どうして要素が5個なのに処理が4回なのか
要素が5つなのに処理が4回である理由は、上で紹介した構文中の
initialValue
が指定されていない場合、最初の要素を飛ばしてインデックス番号1から実行されるためです。
配列の要素の合計値を求める場合は0
を初期値として指定しておく、といった具合ですね。MDNのドキュメントでも、通常は初期値を指定するほうが安全だと書いてあります。
より詳しく知りたい方は、ぜひドキュメントを参照してみてください。なぜ名前が"reduce"なのか
多くの人が英単語のreduceの意味を「減らす」で覚えていますよね。
それ自体は正しいのですが、この関数reduce()
のイメージとはちょっと合致しないな~と感じます。そこで、いまいちどreduceの意味を調べてみると、次のような意味が出てきました。
3〔+目的語+to+(代)名詞〕〈ものを〉(整理して)〔簡単な形に〕変える,まとめる.
7【数学】〈…を〉換算する,通分する,約する.
参考:https://ejje.weblio.jp/content/reduceJavaScriptの関数で使われる
reduce()
としての意味は上記のものがふさわしそうです。
reduce()
に関数を渡し、配列要素に対してその関数を実行していき、最終的に出た結果のみを返す。ふむふむ。
要素ひとつひとつをギュッとまとめてひとつの値のみにするというイメージですかね。英単語の意味を押さえると、グッとわかりやすくなりました。
- 投稿日:2021-01-18T16:59:27+09:00
Safariで<input>のonchangeが発火しない場合の対処法
TL;DR
Safariではなぜかinputのchangeを拾えないことがある(リロード時の挙動により差分を検知出来ない事があるんだと思う)
その場合はonclickイベントでinputのvalueを空にしてやればよい
onclickはonchangeよりも先に発火するのでこれで常にnullと比較でき、変更を検知できるconst input = document.createElement('input'); input.type = 'file'; input.onclick = () => { this.value = null; }; input.onchange = () => { // なんか処理 };
- 投稿日:2021-01-18T15:23:29+09:00
【小ネタ】JavaScriptでPHPのlist()っぽいことをやってみる。
はじめに
ドット連結の文字列を分割してそれぞれを変数にできないかなーって考えたときに、もしかしたらJSでもPHPのlist()みたいなことできるんじゃね?って思ったんですよ。
PHPのlist()とは
右辺が配列になるとき、左辺側に列挙した変数に値を入れれるというやつです。
https://www.php.net/manual/ja/function.list.phplist($jeffy, $tockey, $fagimaru) = ['犬', '猿', '雉']; echo $jeffy; // 犬 echo $tockey; // 猿 echo $fagimaru; // 雉実はPHP7.1以降は以下の書き方でもいけるんですよね。全然気づかなかった……
[$jeffy, $tockey, $fagimaru] = ['犬', '猿', '雉']; echo $jeffy; // 犬 echo $tockey; // 猿 echo $fagimaru; // 雉JSではどうするの?
実はPHPの省略構文と同じです。
const [jeffy, tockey, fagimaru] = ['犬', '猿', '雉']; console.log(jeffy); // 犬 console.log(tockey); // 猿 console.log(fagimaru); // 雉「分割代入」っていうんですね。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
- 投稿日:2021-01-18T14:42:36+09:00
Emotion v11とtypescriptが喧嘩する ~jsx設定方法~
emotionの環境構築をおこなったところ、
TypeScript絡みの設定が案外手強かったので備忘録とさせていただきます。ざっくり環境
React v17.0.1
typescript v4.1.3
@emotion/react v11.1.4
@babel/core v7.12.10jsx属性認識しない問題
import { jsx } from '@emotion/react' import { FooterContainer } from '../../../style/components/block/Footer' export default function Footer() { return ( <div css={FooterContainer}> <p>Footer</p> </div> ) }emotion v11においては、
上記のようにjsx
をimportして、reactからやってくるjsxを使わない(reactを直でimportしない)記述となります。
しかし、早速問題が...なんとか型定義をimportしているjsxの方に向かせたいですね。
そもそもreactのJSXの型定義はどこからきているのでしょうか?答えはTypeScriptのコンパイルのオプションである
jsxFactory
からきています。
https://www.typescriptlang.org/tsconfig/#jsxFactory
こちらはデフォルトではReact.createElement
になっています。
jsxFactory
をemotionのjsxで呼べるよう変更しておきます。
ちなみにトランスパイルをbabelに任せている場合、"jsx": "preserve"
でOKです。tsconfig{ "compilerOptions": { "jsx": "preserve", "jsxFactory": "jsx", ... }これでエラーが解消されます。
別解:プラグマを使う
公式のドキュメントでは以下のようなコメントが差し込まれています。
/** @jsx jsx */このコメントはプラグマというTSでサポートされている機能になります。
この機能は、ファイル毎にjsxFactoryを変更できる機能です。
こちらを追加するとエラーが解消されます。/** @jsx jsx */ import { jsx } from '@emotion/react' import { FooterContainer } from '../../../style/components/block/Footer' export default function Footer() { return ( <div css={FooterContainer}> <p>Footer</p> </div> ) }https://github.com/Microsoft/TypeScript/pull/21218
ただ、ファイル毎にプラグマを記述しないといけないのが微妙ですね。
わがまま
jsxを直で上書きしてしまうのはちょっと不安です。
毎度jsxを読まないといけないのもなんか微妙です。
もっといい方法があれば教えていただけると嬉しいです
- 投稿日:2021-01-18T14:21:01+09:00
Javascriptのスコープに関して理解を深める
そもそも、スコープとは
プログラミングにおけるスコープ(英: scope, 可視範囲)とは、ある変数や関数などの名前(識別子)を参照できる範囲のこと。
(参照:wikipedia https://ja.wikipedia.org/wiki/%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%97)スコープを使うことでできること
・範囲外のものは参照をさせないような設計ができる(変数をスコープの外では変更できないような、安全性を確保)
・スコープがあることにより、関数名や変数名等同じものを使用してもスコープの内外で有効な範囲が変わってくるので、コード全体の命名が簡潔に済むスコープの種類
グローバル変数とローカル変数の二種類に分かれる。
グローバル変数 ローカル変数 関数の外(トップレベル)で宣言した変数 関数の中で宣言した変数, 関数の仮引数 プログラム全体から参照できる その関数の中でのみ参照できる グローバルスコープ
プログラムのどこからでもアクセス可能。
JavaScript においては、一般的にすべてのコードが実行されている Web ページがグローバルスコープとなる。aとbは関数からでも、ブロック内でもどこでも呼び出せる。
ローカルスコープ
ローカルスコープは二種類あり、
関数スコープ
とブロックスコープ
に分かれる。関数スコープ
関数スコープはその名前の通り、関数ごとに作られるスコープ。
↑関数内で定義したhelloに対して、関数の外から呼び出しをしようとすると、上記のようなエラーが出てしまう。↑こちらは関数内で定義したものを関数内で呼び出している為、エラーの表示がなく、結果もしっかりと"hello world"の文字列が取得できました。
このように関数内で定義したものは関数内でしか実行できないものを関数スコープと呼ばれています。ブロックスコープ
ブロックスコープも関数スコープと同じくローカルスコープの仲間で、if文やfor文と組み合わせることが多いです。
↑ブロック内で宣言しているinBlockはブロックの外から呼び出してもスコープ外の為、未定義エラーが出てしまう。
↑こちらも同様にfor文の外で回数を出力しようとすると、未定義エラーが出てしまう。
上記のスコープで注意する点
letとvarの違い
変数宣言をする際、letとvar(とconst)があるが、それぞれスコープに違いがある。
↑このようにブロックスコープを定めても、letはブロックごとに読み取りの制限をすることが出来ているのですが、varを使用している場合ブロック内の
foo
を読みとってしまっている為、外部から意図しない値に書き変わってしまいバグが発生する可能性がある。また、varにはホイスティングと呼ばれる機能があり、関数の内部で宣言されている変数は、その関数の先頭で宣言されたものと見なされるというものです。
このようなサンプルコードではすぐ近くにあるので気づけますが、関数の行数が多い場合
foo
がトップレベルのfooと勘違いしてしまう可能性もあり、さらにコードがエラーを出さない為、対処が大変になってしまう可能性があります。最後に
Javascriptに苦手イメージがあり、ほとんど勉強をしてこなかったのですが、業務で取り扱う機会が多いので基礎から勉強しようと思いスコープを題材に取り上げました。
まだまだ、わからないことだらけなので、整理しながら勉強をしていきます。
- 投稿日:2021-01-18T13:30:39+09:00
オブジェクトのディープコピー
メモとして。
const obj = [ { name: 'taro', age: 24 }, { name: 'sato', age: 22 }, ] const copyObj = obj.map(person => ({name : person.name ,age : person.age})) for(let i = 0; i < copyObj.length; i++){ copyObj[i].name = "keiko"; copyObj[i].age = i + 20; } console.log(obj); console.log(copyObj);結果
[[object Object] { age: 24, name: "taro" }, [object Object] { age: 22, name: "sato" }] [[object Object] { age: 20, name: "keiko" }, [object Object] { age: 21, name: "keiko" }]配列も同様
const obj = [ { answer: [ [1,1,1], [1,1,1] ], remain: [2,2,2] } ] const copyObj = obj.map(list => ( { answer : [...list.answer], remain : list.remain } ) ); copyObj[0].answer[0] = [1,2,3]; copyObj[0].answer[1] = [4,5,6]; console.log(obj); console.log(copyObj);結果
[[object Object] { answer: [[1, 1, 1], [1, 1, 1]], remain: [2, 2, 2] }] [[object Object] { answer: [[1, 2, 3], [4, 5, 6]], remain: [2, 2, 2] }]
- 投稿日:2021-01-18T12:20:57+09:00
Google Chartで散布図を描画してみた
はじめに
授業のチーム開発で散布図を作ることがあり、そこの開発に携わっていたので、アウトプットしようと思いました。
成果物
このように、散布図を作成することができました。
参考元
Google Charts (Scatter Charts)
また、散布図の元データとして、ぐるなびAPIから得たデータを利用しています。
ぐるなびAPI
※2021/1/15以降、新規利用ができなくなったようです開発環境
エディタ:vscode
ソースコード全体
index.html<div id="target" class="box" style="width: 700px; height: 500px;"></div> <script src="https://www.gstatic.com/charts/loader.js"></script> <!-- targetタグのdivに散布図を挿入--> <script src="js/googleScatter.js"></script>googleScatter.jsconst drawScatter = (result, userLatlng) => { google.charts.load('current', { 'packages': ['corechart'] }); google.charts.setOnLoadCallback(drawChart); var mapLatlng = [ ] function drawChart() { var data = new google.visualization.DataTable(); // [距離、金額、店名]のデータテーブルを作成 data.addColumn('number', '距離'); data.addColumn('number', '金額'); data.addColumn({ type: 'string', role: 'tooltip' }); // ぐるなびAPIから情報を取得し、行を追加 result.rest.map(item => { var latlng = new google.maps.LatLng(item.latitude, item.longitude); mapLatlng.push([item.latitude, item.longitude]) var distance = google.maps.geometry.spherical.computeDistanceBetween(userLatlng, latlng); data.addRow([distance, item.budget, item.name]); }); // mouseOnの時に表示される吹き出しを調整 var formatter = new google.visualization.PatternFormat('店名 {2} :{0}m :{1} 円 '); formatter.format(data, [0, 1, 2], 2); // 実際の散布図の設定 var options = { title: '店の距離と金額', hAxis: { title: '距離', format: '###m', minValue: 0, maxValue: 15 }, vAxis: { title: '金額', format: '###円', minValue: 0, maxValue: 15 }, legend: 'none' }; // コンテナに散布図を挿入 var chart = new google.visualization.ScatterChart(document.getElementById('target')); // 散布図の店が押された時の処理を記入 google.visualization.events.addListener(chart, 'select', function () { var selection = chart.getSelection(); // console.log(selection[0].row); map.panTo(new google.maps.LatLng(mapLatlng[selection[0].row][0], mapLatlng[selection[0].row][1])) }); chart.draw(data, options); } }index.htmlの中身
- 散布図の挿入元となるコンテナを用意
- Google Chartsの利用の宣言
- googleScatter.jsを呼び出す
をしています。
googleScatter.jsの中身
1. 散布図のデータの元となる表の構造を記述
var data = new google.visualization.DataTable(); // [距離、金額、店名]のデータテーブルを作成 data.addColumn('number', '距離'); data.addColumn('number', '金額'); data.addColumn({ type: 'string', role: 'tooltip' });2. 表にデータを入れていく
// ぐるなびAPIから情報を取得し、行を追加 result.rest.map(item => { var latlng = new google.maps.LatLng(item.latitude, item.longitude); mapLatlng.push([item.latitude, item.longitude]) var distance = google.maps.geometry.spherical.computeDistanceBetween(userLatlng, latlng); data.addRow([distance, item.budget, item.name]); });3. 散布図のプロパティを設定
// mouseOnの時に表示される吹き出しを調整 var formatter = new google.visualization.PatternFormat('店名 {2} :{0}m :{1} 円 '); formatter.format(data, [0, 1, 2], 2); // 実際の散布図の設定 var options = { title: '店の距離と金額', hAxis: { title: '距離', format: '###m', minValue: 0, maxValue: 15 }, vAxis: { title: '金額', format: '###円', minValue: 0, maxValue: 15 }, legend: 'none' };formatterの部分では、{0}{1}{2}がそれぞれ表しているのは、表の[列番号-1]です。
つまり今回なら、
{0} = 距離
{1} = 金額
{2} = 店名optionsの部分では、散布図の「タイトル、横軸、縦軸」などを設定します。
また、表の1列目がhAxisの要素、2列目がvAxisの要素になります。4. コンテナに作成したchartを埋め込む
// コンテナに散布図を挿入 var chart = new google.visualization.ScatterChart(document.getElementById('target'));5. 散布図の点がクリックされた時の処理を記述
// 散布図の店が押された時の処理を記入 google.visualization.events.addListener(chart, 'select', function () { var selection = chart.getSelection(); // console.log(selection[0].row); map.panTo(new google.maps.LatLng(mapLatlng[selection[0].row][0], mapLatlng[selection[0].row][1])) });引数に「select」を渡すことで、散布図の点がクリックされた時に動くようになっています。
また、変数の
selection
では表のindex部分を参照しています。6. chartの描画
chart.draw(data, options);反省点
変数の宣言部分の順番がわかりづらい
- targetにchartを入れるというところは、もっと最初の方に記述しておくべき
- index.htmlのjavascriptを呼び出すところがぐちゃぐちゃ
ソースコードの説明の仕方が下手くそ
最後に
今回は、Qiita初投稿でしたが、ソースコードの説明を記述するのがとても難しいということがわかりました。
もう少し、他の投稿者様の書き方を参考にしていきたいと思います。
- 投稿日:2021-01-18T10:25:40+09:00
一列で終わるJavascriptとアルゴリズム
一列で終わるJavascript
1loc.dev.comはウェブ開発(JavaScript)に必要な技術が見られるリアルJavaScript「ライブラリー」である。 利用者は無料で資料を見ることができる。
簡単な例もあり、分かりやすい!一列で終わるJavascript
ウーバーの開発者trekhlebGitHubには
アルゴリズムが必要な時に有用な情報があふれている。ビッグデータの分析や統計関連数値を予測するプログラムを実装する段階で、trekhleb GitHubを好んで探すなら、特定のアルゴリズムを簡単かつ迅速に検索して使用することができる。
何より、日本語になっているので分かりやすい!
https://github.com/trekhleb/javascript-algorithms/blob/master/README.ja-JP.md
- 投稿日:2021-01-18T10:13:35+09:00
フロントで電話番号をハイフン付きに変換する
利用するライブラリについて
ユーザの入力した電話番号に対して、視認性を持たせるためハイフン区切りにしたい、もしくはハイフン付きでAPIサーバに送信する必要があるなど、フロントでハイフン付き電話番号に変換する機会があると思います。
GitHubのGoogleリポジトリに電話番号処理ライブラリlibphonenumberがあります。
こちらのJS版のデモページで、電話番号とカントリーコード(JP
)を入力することで動作検証ができます。こちらのデモと同様の挙動を実現する想定です。
Phone Number Parser DemoJS版の使い方を読むと、コンパイルなどの必要があり直接NPMから利用できずとても面倒です。本家の参照しているサードパーティライブラリを使います。
Alternatives to our own versions:
Javascript: If you don't want to use our version, which depends on Closure, there are several other options, including https://github.com/catamphetamine/libphonenumber-js - a stripped-down rewrite, about 110 KB in size - and https://github.com/seegno/google-libphonenumber - a browserify-compatible wrapper around the original unmodified library installable via npm, which packs the Google Closure library, about 420 KB in size.今回は執筆当時にDL数の多かったgoogle-libphonenumberを利用します。
動作環境
- TypeScript 3.9
- google-libphonenumber "3.2.15"
- NPMから利用
// npm npm install google-libphonenumber // Yarn yarn add google-libphonenumberサンプルコード
TypeScriptでサンプルコードを作成しましたが、変数宣言時の型定義を削除すれば、JavaScriptでも動作するかと思います。
formatUtil.tsimport { PhoneNumberUtil, PhoneNumber, PhoneNumberFormat } from 'google-libphonenumber'; export class FormatUtil { public static formatTelNumber (value: string) { // 日本の国コード const region = 'JP'; const util:PhoneNumberUtil = PhoneNumberUtil.getInstance(); // 番号と地域を設定 const number:PhoneNumber = util.parseAndKeepRawInput(value, region); // 電話番号の有効性チェック if (!util.isValidNumberForRegion(number, region)) { return null; } // ハイフン付きの形式で返却 return util.format(number, PhoneNumberFormat.NATIONAL); } }動作結果
formatUtil.spec.tsimport { FormatUtil } from '~/util/FormatUtil'; describe('FormatUtil', () => { test('formatTelNumber', () => { // 携帯電話番号 expect(FormatUtil.formatTelNumber('09012345678')).toEqual('090-1234-5678'); expect(FormatUtil.formatTelNumber('08099998888')).toEqual('080-9999-8888'); expect(FormatUtil.formatTelNumber('07033336666')).toEqual('070-3333-6666'); expect(FormatUtil.formatTelNumber('09011111111')).toEqual('090-1111-1111'); // 大阪 expect(FormatUtil.formatTelNumber('0611112222')).toEqual('06-1111-2222'); // 愛知 expect(FormatUtil.formatTelNumber('0529991111')).toEqual('052-999-1111'); // 千葉 expect(FormatUtil.formatTelNumber('0431234567')).toEqual('043-123-4567'); // 福岡 expect(FormatUtil.formatTelNumber('0921234567')).toEqual('092-123-4567'); // 東京 expect(FormatUtil.formatTelNumber('0312345678')).toEqual('03-1234-5678'); // IP電話 expect(FormatUtil.formatTelNumber('05012345678')).toEqual('050-1234-5678'); // 無効番号 expect(FormatUtil.formatTelNumber('05012')).toEqual(null); expect(FormatUtil.formatTelNumber('09000000000')).toEqual(null); expect(FormatUtil.formatTelNumber('99999999999')).toEqual(null); }); });参考
GitHub Google/libphonenumber
GitHub ruimarinho/google-libphonenumber
Qiita JavaScriptで電話番号のバリデーション&自動フォーマット
- 投稿日:2021-01-18T08:49:47+09:00
困っている方必見!ページトップからある要素までの高さの取得方法
cssにてpositionを使っているときに、
top
やleft
などを%で指定していると、使っている機種によって%の値が変わってきます。これは厄介。。。
こんな時に役立つのが、Javascriptを使ってページトップからの高さを取得してしまえば、機種によって%の値を変える必要はありません!
結論から言うと、
getBoundingClientRect()
、pageYOffset
の2つの関数を使います。それでは使い方を見ていきましょう!!
要素を取得
index.jsconst topToElement = document.getElementsByClassName('class-name')[0]また、
document.getElementId
などで取得してくださいページトップからの高さを取得
index.jsconst topToElement = document.getElementsByClassName('class-name')[0] const topToElementHeight = topToElement.getBoundingClientRect().top + window.pageYOffset console.log(topToElementHeight)このようにして高さを取得することができます!
また、これらはVue.jsで使う場合、DOM要素と紐づけられた後の
mounted
で行ってください。
created
では取得できないのでご了承ください。以上、「ページトップからある要素までの高さの取得方法」でした!
良かったら、LGTM、コメントお願いします。
また、何か間違っていることがあればご指摘頂けると幸いです。
他にも初心者さん向けに記事を投稿しているので、時間があれば他の記事も見て下さい!!
Thank you for reading
- 投稿日:2021-01-18T07:12:14+09:00
AgregoreWeb を使ってみた (-01)
AgregoreWeb を最近知りました。
以下の Live 配信 で 紹介されたのが切欠なのですが、
とても興味深い技術です。Speakeasy JS - Using Standard Web APIs to Mix P2P Protocols (Mauve)
https://www.pscp.tv/feross/1BRJjBkdqLoJw?t=23m53sWhat is AgregoreWeb
AgregoreWeb は 分散Web用のブラウザーです。
AgregorWeb は https://github.com/AgregoreWeb/agregore-browser で ソースが公開されてる OpenSource です。公式サイトは以下になります。
https://agregore.mauve.moe/ドキュメントは以下
https://github.com/AgregoreWeb/agregore-browser/tree/master/docs
起動してみた
https://github.com/AgregoreWeb/agregore-browser/releases からダウンロードできます
ブラウザーと同じですね!!
こんな感じでアクセスできます。
- 投稿日:2021-01-18T06:14:50+09:00
Redmineでチケットをコピーした時は開始日と期日をそれぞれ+1日加算したい
同じ開始日と期日でチケット作ることほぼ無いからね
RedmineをTODOっぽく使ってるマンです。
んで、使ってるうちにこんなことを思いました。
- 今日のお掃除チケット終了!じゃあ明日用にチケットコピーしようジャマイカ。
- あれ?コピーしたはずのチケットが見当たらない…。
- あ、開始日と期日を 1日ずらすの忘れてた 。
- 面倒くさいなあ…。
ということで記事タイトルのようなことをやりました。
ビューカスタマイズプラグイン使うよ
ビューカスタマイズプラグインのインストールまでは下記でやってるので、この記事ではハショリます。
CentOS8にインストールしたRedmineにビューカスタマイズプラグイン導入設定はこんな感じ
パスのパターン で絞り込んでおくのが大事。
『このJSって別のページに悪影響あるかな?』を考えなくてよくなるです。
コード$(function(){ var start_val_ = $('#issue_start_date').val(); var due_val_ = $('#issue_due_date').val(); //コピー元の値を確認できた方がいいから。 $('label[for="issue_start_date"]').text('開始日(元:' + start_val_.replace(/-/g, '/') + ')'); $('label[for="issue_due_date"]').text('期日(元:' + due_val_.replace(/-/g, '/') + ')'); var y_; var m_; var d_; //チケットコピーは日次のTODOが多いから開始日と期日を+1日加算。 //開始日未設定のチケットをコピーすると、 //開始日の値は現在日付が自動でセットされるので、 //開始日の値が無い場合は考えないことにします。 //開始日 var start_date_ = new Date(start_val_); start_date_.setDate(start_date_.getDate() + 1); y_ = start_date_.getFullYear(); m_ = ("00" + (start_date_.getMonth()+1)).slice(-2); d_ = ("00" + start_date_.getDate()).slice(-2); $('#issue_start_date').val(y_ + "-" + m_ + "-" + d_); //期日未設定のチケットをコピーした時に例外にならないように。 if(due_val_ == '') return; //期日 var due_date_ = new Date(due_val_); due_date_.setDate(due_date_.getDate() + 1); y_ = due_date_.getFullYear(); m_ = ("00" + (due_date_.getMonth()+1)).slice(-2); d_ = ("00" + due_date_.getDate()).slice(-2); $('#issue_due_date').val(y_ + "-" + m_ + "-" + d_); })有効にするとこんな感じ
蛇足
チケットを『日次』『週次』『月次』『年次』『不定期』に分類して、それぞれに対してチケットコピー時の加算日数を切り替えれば、もうちょっと便利になるかもです。
ビューカスタマイズプラグインはこういうプチ改良が手軽にできて便利です。
バージョン
CentOS Linux release 8.3.2011
Redmine 4.0.6.stable
Chrome バージョン: 87.0.4280.141(Official Build) (64 ビット)
- 投稿日:2021-01-18T02:18:33+09:00
JavaScript で文字コードを加減算する方法
自分のサイトがあるのですが、そこを楽曲専用から汎用に大型アップデートしようと思いまして、
そこでリストの番号をイメージリスト ①text1 ②text2 ③text3の様にしたかったんですね。数字が○で囲まれている文字です。
そこで、CSS で合ってるかは知らないけどこんなの.cssol li#list1:nth-child(1)::before { content: "①"; } /* : : */とか文字列配列作ってインデクスに応じて切り替えたり(?)で出来るんですが、
せっかくなら JavaScript で書きたくなったんですよ。
そのままテキストで①、②…と書けばいいものを…C/C++なら分かるぞ!
と思ったので一応書いておきます。C/C++ での実装の場合のイメージです。
C/C++の場合.cppstd::string GetNumByCircle(int num) { std::string res = '①' + num; return res; }実行結果のイメージ(入力は3)④これで今回の主題は達成するのですが、JavaScript で同じ様に書いてみましょうか。
JavaScriptの場合.jsfunction getNumByCircle(num) { var res = '①' + num; return res; }実行結果のイメージ(入力は3)①3JavaScript では '...' も "..." も文字列で、+ は文字列結合演算子なので文字列結合が発生しました。
これでは、僕が実際に使おうとした方法でやると他のバグもありますが、こうなりますよね。...①6①5①4①3①2①1①0JavaScript で文字コードを加減算
では、JavaScript で「'①' + num」を考えてみましょう。
…分かりましたか?
では、僕が最終的に辿り着いた方法を紹介しましょう。JavaScriptでのやり方.jsfunction getNumByCircle(num) { var res = String.fromCharCode('①'.charCodeAt(0) + num); return res; }これで
実行結果のイメージ(入力は3)④となります。長いなぁ…。
解説
解説編です。
何が起こっているか
といえば、
- 文字コードを取得
- 取得した値に num を加算
- その値を文字コードとした文字を取得
とやっているだけです。
因みに C/C++ の場合は、
- char 型の整数値(=文字コード)に num を加算
これだけです。
1. 文字コードを取得
この部分.js/*function getNumByCircle(int num) { var res = String.fromCharCode(*/ '①'.charCodeAt(0) /*+ num); return res; }*/
文字列.charCodeAt(index: number)
文字列の index 番目の文字の文字コードを取得するメソッドです。
これで 文字型から整数型に変換するようなことをしています。2.取得した値に num を加算
この部分.js/*function getNumByCircle(int num) { var res = String.fromCharCode(*/ '①'.charCodeAt(0) + num /*); return res; }*/
文字コード(整数値) + 整数値
書くとすればこういうことになりますね。単純に整数値同士の加算です。
C/C++ の方では「1.char 型の整数値(=文字コード)に num を加算」にあたる式です。1. 文字コードを取得
この部分.js/*function getNumByCircle(int num) { var res = */String.fromCharCode('①'.charCodeAt(0) + num)/*; return res; }*/
String.fromCharCode(...codes: number[])
文字コード配列に対応する文字列を取得するメソッドです。
これで 整数型配列から文字型配列に変換するようなことをしています。まとめ
String.fromCharCode(str.charCodeAt(idx) + num)で
chr + num
の様な事は可能!
…書きながら思いましたが、str[idx] + num
の方が JS に忠実ですね。おわりに
記事を書いている最中にメソッド名で調べたんですが、
どうやらString.charCodeAt(idx)
では値の範囲によっては上手く動かないようです(例えば "?" 等)。
その理由や、どのメソッドを使用すればいいのかは自分で調べてみて下さい。
因みに、String.fromCodePoint(codes)
の方はこれで良いのでしょうか…?さてさて、執筆はこの辺にして、ちゃんと各
<ol>
の<li>:before
に配置されるように修正しないとね。
- 投稿日:2021-01-18T00:26:48+09:00
JavaScript ベスト・オブ・ザ・イヤー 2020
JavaScriptライブラリのトレンドを紹介しているbestofjs.orgが、2020年に最もホットであったJavaScriptライブラリのランキングを発表しました。
選考基準は現在のスター数ではなく、『2020年の一年間で増えたスターの数』です。
過去流行っていたけど落ち目となった技術は出てこないので、最近注目されている技術がわかります。ちなみに2016年の総合ランキング1位はVue.js、2017年の総合ランキング1位はVue.js、2018年の総合ランキング1位はVue.js、2019年の総合ランキング1位はVue.jsです。
以下は2020年のランキング、2020 JavaScript Rising Starsの日本語訳です。
JavaScript ライジングスター 2020
5回目のJavaScript ライジングスターにようこそ!
このランキングのコンセプトは、昨年までと同じです。
すなわち、2020年の一年間でGitHubに追加された☆の数を比較することで、どのプロジェクトが最も注目を集めたかを数字で確認します。以下のチャートは、2020年の一年間にGitHubで増加したスターの数を比較したものです。
Webプラットフォームに関するベストプロジェクトを集めたリストであるBest of JavaScriptからの分析となります。
各プロジェクトをクリックすると、プロジェクトの詳細を閲覧することができます。総合ランキング
1位: Deno
2位: Vue.js
3位: React
4位: Playwright
5位: VS Code
6位: esbuild
7位: Vue Element Admin
8位: eDEX-UI
9位: Next.js
10位: Tailwind CSS2020年は様々な理由で特別な年になりました。
最も目を引くことは、これまで5年間首位を独走してきたVue.jsを抜き去り、Denoが一位になったことです。DenoはNode.jsの生みの親Ryan Dahlによる新たなJavaScriptランタイムです。
Node.jsのこれまでの10年間の経験と反省を生かし、多くを改善しているため、Node.jsの後継と思われがちです。
主な機能としては、
- デフォルトでTypeScript対応。ただしJavaScriptでコードを書くこともできる。
- 一極集中したパッケージマネージャがなく、任意の依存関係を任意のURLから読み込むことができる。
- Deno標準ライブラリは、Node.jsでは個別にパッケージをインストールしなければならなかったような一般的な用途のライブラリを最初から提供する。
- Denoは可能なかぎりWeb標準に従っている。(例:Fetch API)
- インポートはES Modulesを使用。
- テストランナーやデバッガーを標準装備。
Denoのエコシステムはまだまだ発展途上ですが、Denoの話題性を考えると、今後大きく変化することが期待されます。
Denoの成長は、2つの大きなトレンドを裏付けています。
- フロントエンドとクライアントサイドでのTypeScriptの台頭
- Snowpackなどのソリューションによってオンザフライで提供されるES Modulesの成長。
フロントエンド フレームワーク
1位: Vue.js +22.5k
2位: React +19.8k
3位: Angular +13.3k
4位: Svelte +12.0k
5位: Alpine.js +11.5k
6位: vue-next +5.9k
7位: Solid +3.3k
8位: Preact +3.0k
9位: htmx +2.7k
10位: Stimulus +2.0kいつものようにVue.jsとReactが頂上決戦を繰り広げています。
その後ろでは、2019年に3番手をSvelteに奪われたAngularが、ふたたびその位置を奪還しました。
ベスト5の新顔はAlpine.jsで、これはLaravel LiveWireの作者によって作られたミニマルなリアクティブフレームワークです。
Vue.jsとAngularの両方から、カスタムHTMLディレクティブや双方向バインディングといったアイデアを拝借しています。
HTMLに古き良き<script>
タグを追加するだけで簡単に使うことができ、ビルドプロセスも不要で、HTMLマークアップだけで全てを動かすことができます。
本格的なフレームワークを導入することが困難な既存のWebページをさくっと強化する目的については、最も適切なソリューションであるかもしれません。Webページにインタラクティブ性をもたらすだけの非常に軽量なソリューションであるため、Elixir Phoenixのようなフレームワークともうまく連携して同居できます。
Alpine.jsとTailwind CSSを最初からまとめておいたPETALのようなプロジェクトも存在します。
こちらについては後ほど語りましょう。Node.js フレームワーク
1位: Next.js +15.5k
2位: Strapi +11.8k
3位: Nest +10.3k
4位: Nuxt +8.2k
5位: Blitz +6.0k
6位: Redwood +5.5k
7位: Express +4.6k
8位: Fastify +3.8k
9位: umi +2.9k
10位: Koa +2.3kNode.jsフレームワークには大きく2つの種類が存在します。
ひとつはNext.jsやNuxtのようなフルスタックフレームワークで、ReactやVue.jsなどをサーバサイドに持ってくるアプリケーション構築方法については賛否両論があります。
もうひとつは昨年のチャンピオンNestやFastifyなどが属する、サーバ側のみで動作する古典的なフレームワークです。
この分野では2018年にトップだったNext.jsが再びチャンピオンに返り咲きました。
当初はReactをSSRするだけのソリューションとして名を上げましたが、今ではReactでフルスタックWebアプリケーションを構築するソリューションの筆頭になっています。
最新バージョンでは動的ページと静的ページの垣根を取り払うIncremental Static Regenerationにも対応し、多くのユースケースで最適な選択肢になりつつあります。フルスタックといえば、BlitzやRedwoodは、それひとつだけで完全なWebアプリケーションを構築できるという最高の開発者体験を提供することを目的としたプロジェクトです。
それにしても浮沈の激しいJavaScriptの世界で、11年前に誕生したExpressがいまだに一定の地位を保っているのは興味深いですね。
React エコシステム
1位: Next.js +15.5k
2位: REact Query +13.6k
3位: Recoil +11.1k
4位: Ant Design +10.9k
5位: React Hook Form +10.8k
6位: Material UI +10.6k
7位: Create React App +10.1k
8位: Chakra UI +10.0k
9位: swr +8.9k
10位: Gatsby +7.4k2020年のReactエコシステムのテーマは安定性でした。
React17では破壊的変更を行わず、将来に向けての布石を仕込みました。
それがReact Server Componentsです。React Server Componentsはクライアントのバンドルサイズを縮小し、起動にかかる時間を改善します。
さらにデータの取得、データベースやファイルシステムなどデータソースへのアクセスも簡単になります。Next.jsは、Reactアプリケーションを構築するための最も有名なソリューションに成長しました。
React Server Componentsの最初のアプリになることでしょう。
React Query、Recoil、React Hook Formといったサポートライブラリは、hooksを主軸に進化、円熟してきました。
それぞれがReact開発の一部を簡素化してくれます。
これらのコンポーネントライブラリを組み合わせることで、React開発者はこれまで以上に多くのツールを手に入れることができるでしょう。Vue エコシステム
1位: Vue Element Admin +16.0k
2位: Vite +14.1k
3位: Nuxt +8.2k
4位: Element Plus +7.3k
5位: vue-next +5.9k
6位: Vuetify +5.8k
7位: Wiki.js +5.7k
8位: Element +5.5k
9位: Ant Design Vue +4.2k
10位: Vant +4.1k2020年のVueコミュニティ最大のニュースは、Vue3のリリースです。
Vue2に存在した幾つかの問題を解決するために、Composition APIという仕組みが導入されました。
- コンポーネント内の論理的な繋がりによってコードを整理することが難しかった。
- コンポーネントをまたいだコードの再利用が容易になる。 (Vue2のmixin、mixing factory、scoped slots等では不十分だった)
- TypeScriptサポートが改善された。
バージョン3で導入された変更については、マイグレーションガイドをチェックしてみてください。
2020年には新たなWeb構築ツール、Viteが誕生しました。
ES modulesに対応し、コマンドラインからVueアプリケーションを構築する最速の方法です。Angular エコシステム
1位: ngx-admin +2.5k
2位: Material Design for Angular +1.5k
3位: Scully +1.4k
4位: Angular CLI +1.3k
5位: NG-ZORRO +1.2kAngularのランキングは昨年とあまり変わりませんが、3位に新たなプロジェクトが登場しました。
ScullyはAngularにJamstackをもたらす静的サイトジェネレータです。
このプロジェクトは2019年12月に登場し、そしてわかりやすいドキュメントが存在します。Angularは2020年に3つのメジャーバージョンがリリースされました。
2月にはバージョン9がリリースされました。
主な変更点はIvyコンパイラの導入で、これによってバンドルサイズが減少し、またビルドプロセスに大きな改善がもたらされました。
さらに年の後半にはバージョン10とバージョン11がリリースされました。Angularチームの2020年後半の主な仕事は、コミュニティの声に耳を傾けることでした。
コミュニティのニーズを理解するために、issueやPRに対応することに大きな努力を行いました。
また、チームが取り組んでいることの共有や、今後のロードマップの公開も行いました。ビルドツール
1位: esbuild +16.6k
2位: Rome +14.2k
3位: Vite +14.1k
4位: Snowpack +10.1k
5位: Webpack +4.5k2020年はビルドツールの当たり年で、多くの新しいトレンドが産まれました。
SnowpackとViteはES modulesの将来に賭けたアプローチです。
開発中のコードはバンドルせず、プロダクションコードのビルド時のみバンドルする方針で、非常に高速なフィードバックループを持っています。swcとesbuildは、それぞれRustとGoで書かれており、TypeScriptをサポートしていて、そして信じられないほどの高速で動作します。
Webpackは設定が複雑すぎると言われることが多く、よりシンプルに書けるParcelやRollupが成熟してきました。
とはいえビルドツールの中心はいまだWebpackであることは変わらず、そしてWebpackの新たなキャッシングレイヤはビルドのパフォーマンスを大幅に改善します。Monorepoがメインストリームになりつつあります。
YarnとLernaが広く使われ、そしてnpm 7も参加してきました。個人的に2021年の去就を注目しているのはRome、Toast、Turborepoです。
CSSフレームワーク
1位: Tailwind CSS +15.5k
2位: Bootstrap +8.2k
3位: Bulma +4.2k
4位: new.css +3.1k
5位: Halfmoon +2.0k昨年はなかった項目ですが、Tailwind CSSの躍進と、そのユーティリティファーストの姿勢を評してこのセクションを追加しました。
BootstrapやBulmaといった既存のCSSフレームワークに比べて、開発者がクラス名を合成してページやコンポーネントをスタイル化するための命名規則を提供しています。
State of CSSのアンケートにおいても、最も満足度の高いフレームワークになっています。
先日バージョン2がリリースされ、ダークモードなど多くの新機能が追加されました。CSS in JavaScript
1位: Styled Components +4.8k
2位: Twin +2.8k
3位: Emotion +2.5k
4位: Linaria +1.8k
5位: Theme UI +1.8kテスト
1位: Playwright +19.7k
2位: Storybook +12.3k
3位: Puppeteer +10.6k
4位: Cypress +9.0k
5位: Headless Recorder +6.0kモバイル
1位: React Native +8.8k
2位: Expo +4.3k
3位: Quasar +4.0k
4位: Ionic +2.8k
5位: Sonar +1.8kJSコンパイラ
1位: TypeScript +10.4k
2位: swc +3.4k
3位: Babel +2.7k
4位: Reason +818
5位: Flow +799状態管理ライブラリ
1位: Recoil +11.1k
2位: XState +5.1k
3位: Immer +4.2k
4位: Zustand +3.2k
5位: Redux +3.2kGraphQL
1位: Gatsby +7.4k
2位: Hasura GraphQL Engine +5.9k
3位: Redwood +5.5k
4位: Prisma +4.0k
5位: Apollo client +2.4k学習リソース
1位: JS Algorithms & Data Structures +31.9k
2位: Node.js Best Practices +20.2k
3位: You Don't Know JS +18.0k
4位: Clean Code +15.1k
5位: 30 seconds of code +13.3kまとめ
Best of JSが追跡している多くのカテゴリにおいて、幾つかの新しい潮流が発生し、JavaScriptの世界は今年も素晴らしい年になりました。
バックエンド開発者は今すぐDenoを使って、依存を気にすることなくTypeScriptを楽しむことができます。
フロントエンド開発者はesbuild、Snowpack、そしてViteなど、より高速でシンプルなビルドソリューションを手に入れることができました。
ツールにおいては、NPM 7がひとつのリポジトリで複数のパッケージを扱えるようになるworkspacesをリリースしました。
これはライバルであるYarnが先に提供していた大きな利点のひとつです。スタイルについては、よりシンプルなコンセプトを中心としたエコシステムを構築する、Tailwind CSSのような方向性のソリューションが他にも現れています。
2021年には何が期待できるでしょうか?
React Server Componentsがどのようなものになるかは興味深いところです。
Sebastian McKenzie(BabelやYarnを作った人)がRomeにフルタイムで入っている今、そのJavaScriptツールを統一しようとする試みはどこまで進むでしょうか。
コンパイル、テスト、Lint、その他全て、全てが入ったたったひとつの依存は完成するでしょうか。我々は、フルスタックフレームワークであるRedwoodにも注目しています。
これはGraphQLと相性が良く、そしてデータハンドリングに"cells"と呼ばれるユニークな仕組みを使っています。ユーザのフィードバックに基づいた、本調査とは別観点からの結果を見たいのであれば、State of JSも参照してください。
みてくれてありがとう。
また来年会いましょう!感想
可及的速やかにReactが絶滅しますように。
このランキングが始まって以来4年間トップをひた走っていたVue.jsを抑えて、なんとDenoがトップに立ちました。
ただ、30kの半数近く13.7kは、バージョン1が出た5月の一ヶ月だけで稼いでいます。ご祝儀にしても極端すぎるような。
試しに5月を除いてみると7位くらいです。
とはいえ非常に注目されていることには間違いないので、今後の技術の採用基準の視野に入れてもいいかもしれません。ただし、あくまで☆の増加数であって、ダウンロード数でも実際に運用されているサイト数でもないので、注目されている=最適な技術である、は必ずしも成り立たないことに注意が必要です。
アーリーアダプター()以外の普通の開発者は、普通に枯れた技術を使うのが一番です。たとえば昨年Angularを抜き去り、すわ新時代の到来かと思われたSvelteも、今年はあっさりAngularに抜き返されました。
Svelteが即座に消え去るとは思いませんが、今後RiotやAureliaのようにフェードアウトしていってもおかしくありません。
もちろん何かの拍子に再躍進する可能性もあるでしょう。
そのような将来が不安定な技術はそういうのが好きな連中に任せておいて、業務に取り入れるのは十分に成熟してからも遅くありません。
だいたいWebサイトを使うユーザは、そのサイトが何の技術でできているかなんて一切興味ありませんからね。
Next.js + TailwindだろうがjQuery + べた書きstyle要素だろうが、見た目が同じならそれは同じものです。もちろん、そういうのが好きな人はどんどん手を出しましょう。
ちなみにこのJavaScript Rising StarsのサイトはNext.jsでできています。
コマンド幾つか打つだけでローカルサーバが立ち上がるのでとっても楽。
ただ昨年はホットリロード対応してたはずなんだけど今年は手動リロードしないとだめだった。なんでだろう?(調べてない)Houkago Atelier Toiro ha iizo
- 投稿日:2021-01-18T00:11:45+09:00
え、意外とみんな知らない!?要素外でクリックしたときにイベントを発火させる方法
皆さんこんにちは!
今サイト制作を行っている際に、ふとドロップダウンメニュー(プルダウンとも呼ぶ)を作っていた時、「要素外でクリックしたときにメニューを閉じたいな~」と思い、1から構築しようとしたけどめちゃだるい!!!
僕がサイト制作を行うときに一番心掛けていることは、「どれだけ楽をして作るか」を日々考えながらやっています。
そんな僕にとって、この機能を1から作るなんて死んでもやりたくもない。。。
Googleで検索したところ、、、
なんとありました!!
パッケージ名は「vue-click-outside」
初めて聞いた
さて、今回はドロップダウンメニューを作りつつ「vue-click-outside」を使っていきたいと思います。
使い方だけ見たいという方は、こちらのサイトからご覧ください。
使い方の例も載っているので、分かりやすいかと。
それでは、順を追って一緒に使い方を見ていきましょう!
パッケージのインストール
npm i vue-click-outside --save-devドロップダウンメニューの作成(リストの作成)
ここからはドロップダウンメニューを作っていきます。
表示したいメニュー項目を
menuList
で定義し、isOpend
で要素をクリックしたかを判断します。また、menuList
のプロパティはお好みで設定してください。App.vue<script> export default{ data() { return { isOpend: false, menuList: [ { icon: 'user', id: 'account?isActive=0', labelText: 'アカウント情報' }, { icon: 'envelope', id: 'email?isActive=1', labelText: 'メールアドレス変更' }, { icon: 'key', id: 'password?isActive=2', labelText: 'パスワード変更' }, { icon: 'calendar-alt', id: 'event?isActive=3', labelText: 'イベント' }, { icon: 'user-minus', id: 'withdrawal?isActive=4', labelText: '退会' } ] } }, } </script>v-forでメニューリストを表示
次は、先ほど作成したオブジェクトを使用し、v-forを使って表示していきます。
また、ドロップダウンメニューを表示したときモーダルウィンドウとして表示するため、Buefyを使っていきます。アイコンもBuefyで表示します。
Buefyの使い方は、こちら以下の記事で詳しく書いているので、興味のある方はぜひご覧ください!
初心者必見!サイト制作は楽してなんぼ。CSSフレームワークBuefyの紹介!!
効率的にサイト作り!Buefyでアイコンを表示しよう!!App.vue<template> <div class="phone-side-menu"> <div class="drop-down-menu"> <div class="drop-down-menu-wrapper"> <b-modal v-model="isOpend"> <div class="drop-down-menu-list"> <ul> <li v-for="(element, index) in menuList" :key="index"> <input :id="element.id" name="sideMenuItems" type="radio" /> <label :for="element.id" class="phone-menu-label"> <b-icon pack="fas" :icon="element.icon" size="medium"></b-icon> {{ element.labelText }} </label> </li> </ul> </div> </b-modal> </div> </div> </div> </template> <script> export default{ data() { return { isOpend: false, menuList: [ { icon: 'user', id: 'account?isActive=0', labelText: 'アカウント情報' }, { icon: 'envelope', id: 'email?isActive=1', labelText: 'メールアドレス変更' }, { icon: 'key', id: 'password?isActive=2', labelText: 'パスワード変更' }, { icon: 'calendar-alt', id: 'event?isActive=3', labelText: 'イベント' }, { icon: 'user-minus', id: 'withdrawal?isActive=4', labelText: '退会' } ] } }, } </script>モーダルウィンドウの表示
次は、クリックされたときにモーダルウィンドウを表示する関数、ボタンを作ります。
App.vue<template> <div class="phone-side-menu"> <div class="drop-down-menu"> <div class="drop-down-menu-wrapper"> <!-- ここから追加 --> <b-button type="is-text" @click="toggleMenuList"> ドロップダウンメニュー </b-button> <b-modal v-model="isOpend"> <div class="drop-down-menu-list"> <ul> <li v-for="(element, index) in menuList" :key="index"> <input :id="element.id" name="sideMenuItems" type="radio" /> <label :for="element.id" class="phone-menu-label"> <b-icon pack="fas" :icon="element.icon" size="medium"></b-icon> {{ element.labelText }} </label> </li> </ul> </div> </b-modal> </div> </div> </div> </template> <script> export default{ data() { return { isOpend: false, menuList: [ { icon: 'user', id: 'account?isActive=0', labelText: 'アカウント情報' }, { icon: 'envelope', id: 'email?isActive=1', labelText: 'メールアドレス変更' }, { icon: 'key', id: 'password?isActive=2', labelText: 'パスワード変更' }, { icon: 'calendar-alt', id: 'event?isActive=3', labelText: 'イベント' }, { icon: 'user-minus', id: 'withdrawal?isActive=4', labelText: '退会' } ] } }, methods: { toggleMenuList() { this.isOpend = !this.isOpend } } </script>ボタンをクリックしたら、
toggleMenuList
という関数を実行してモーダルウィンドウの表示・非表示を行います。要素外をクリックしたときの関数を作成
最後に、モーダルウィンドウを表示中に要素外をクリックしたら、モーダルウィンドウを閉じるための関数を作成していきます。
関数名は
hideMenuList
、先ほどインストールしたパッケージをインストールしscript内でdirectives
を定義、v-click-outside
でイベントの発火というような仕組みになっております。
App.vue<template> <!-- イベント発火 --> <div v-click-outside="hideMenuList" class="phone-side-menu"> <div class="drop-down-menu"> <div class="drop-down-menu-wrapper"> <!-- ここから追加 --> <b-button type="is-text" @click="toggleMenuList"> ドロップダウンメニュー </b-button> <b-modal v-model="isOpend"> <div class="drop-down-menu-list"> <ul> <li v-for="(element, index) in menuList" :key="index"> <input :id="element.id" name="sideMenuItems" type="radio" /> <label :for="element.id" class="phone-menu-label"> <b-icon pack="fas" :icon="element.icon" size="medium"></b-icon> {{ element.labelText }} </label> </li> </ul> </div> </b-modal> </div> </div> </div> </template> <script> export default{ // ディレクティブを定義 directives: { ClickOutside }, data() { return { isOpend: false, menuList: [ { icon: 'user', id: 'account?isActive=0', labelText: 'アカウント情報' }, { icon: 'envelope', id: 'email?isActive=1', labelText: 'メールアドレス変更' }, { icon: 'key', id: 'password?isActive=2', labelText: 'パスワード変更' }, { icon: 'calendar-alt', id: 'event?isActive=3', labelText: 'イベント' }, { icon: 'user-minus', id: 'withdrawal?isActive=4', labelText: '退会' } ] } }, methods: { toggleMenuList() { this.isOpend = !this.isOpend }, // 関数を定義 hideMenuList() { this.isOpend = false } } </script>いかがだったでしょうか?
このようにして、ドロップダウンメニューのモーダルウィンドウを表示、要素外をクリックしたらモーダルウィンドウを閉じるという一連の動作を完成させることができます!
最近、モチベが低下しつつある。。。
それでも僕は頑張ります!
皆さんも一緒に頑張りましょう!!!
以上、「え、意外とみんな知らない!?要素外でクリックしたときにイベントを発火させる方法」でした!
良かったら、LGTM、コメントお願いします。
また、何か間違っていることがあればご指摘頂けると幸いです。
他にも初心者さん向けに記事を投稿しているので、時間があれば他の記事も見て下さい!!
Thank you for reading