- 投稿日:2021-03-02T23:29:10+09:00
Mapbox で 地図検索 機能を付けてみた
1. はじめに
この記事は「Mapboxで地図を表示してみた」の続きになります。
- Qiita: Mapboxで地図を表示してみた
前回は、Mapbox を使用して 地図をWebブラウザ上に表示しました。
今回は、検索機能を追加し、指定した場所の地図を表示するようにします。
2. 準備
2-1. アカウント作成・アクセストークン 取得
前回記事の [2. 準備] を参考に、
Mapboxのアカウントを作成し、アクセストークン を取得します。
- Mapboxで地図を表示してみた > [2. 準備]
2-2. スタイル作成
日本語で 地図を表示・検索できるようにするため、日本語に対応したマップスタイルを取得します。
Mapbox では、Mapbox Studio を使ってマップスタイルを設計・管理します。
- Mapbox | Docs: Mapbox Studio:
① Mapbox Studioを開く
以下リンク先の Mapbox Studio を開きます。
- Mapbox Studio:
② スタイル選択
今回は、Gallery で すでに用意されたスタイルを使用します。
Mapbox Studio 画面右側の [Find inspiration in the style gallery] をクリックします。
使用可能なスタイルが 表示されます。
下にスクロールし、[Mapbox Streets Japan]スタイル を選択します。
③ スタイル追加
[Add Mapbox Streets Japan to your account] をクリックし、スタイルを 自分のアカウントに追加します。
④ Style URL取得
追加したスタイルのURLを取得します。
このURLは 後のスクリプトに使用します。3. コーディング
今回は 地図を表示した上で、検索機能を追加し、指定した場所の地図を表示するようにします。
3-1. サンプル
・ HTMLファイル
東京駅周辺(緯度: 35.6809591、経度: 139.7673068)のマップを表示し、
さらに検索した場所周辺のマップを表示するような HTMLファイル(map.html)を作成します。map.html<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>地図表示</title> <script src="https://api.mapbox.com/mapbox-gl-js/v2.1.1/mapbox-gl.js"></script> <link href="https://api.mapbox.com/mapbox-gl-js/v2.1.1/mapbox-gl.css" rel="stylesheet" /> <style> html { height: 100% } body { height: 100% } #map { height: 100%; width: 100%} </style> </head> <body> <script src="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v4.5.1/mapbox-gl-geocoder.min.js"></script> <link rel="stylesheet" href="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v4.5.1/mapbox-gl-geocoder.css" type="text/css"> <script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.auto.min.js"></script> <div id='map'></div> <script> mapboxgl.accessToken = '<アクセストークン>'; var map = new mapboxgl.Map({ container: 'map', style: '<Style URL>', center: [139.7670516, 35.6811673], zoom: 15 }); map.addControl( new MapboxGeocoder({ accessToken: mapboxgl.accessToken, mapboxgl: mapboxgl }) ); </script> </body> </html>・ ブラウザ表示
map.html をブラウザ(ここではChromeを使用)で開いた画面を下に示します。
Mapbox | Docs :Add a geocoder
3-2. 解説
サンプル(map.html)の中身を解説します。
・JavaScript・CSSファイル 読込
head 要素に、
以下のように Mapbox を使用するための jsファイル・cssファイルを読み込みます。<script src="https://api.mapbox.com/mapbox-gl-js/v2.1.1/mapbox-gl.js"></script> <link href="https://api.mapbox.com/mapbox-gl-js/v2.1.1/mapbox-gl.css" rel="stylesheet" />・style属性 指定
style 要素にて、HTMLのスタイルを指定します。
ここでは、ウィンドウの高さheightを100%に設定しています。
また、マップ(id : map)のスタイルを、高さheightを100%、幅 widthを100% に設定します。<style> html { height: 100% } body { height: 100% } #map { height: 100%; width: 100%} </style>・mapbox-gl-geocoder 使用のためのJavaScript・CSSファイル 読込
mapbox-gl-geocoder を使用するための jsファイル・cssファイルを読み込みます。
<script src="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v4.5.1/mapbox-gl-geocoder.min.js"></script> <link rel="stylesheet" href="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v4.5.1/mapbox-gl-geocoder.css" type="text/css">また、IE11でmapbox-gl-geocoderを使用する場合、
Promise polyfillも読み込みます。<script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.auto.min.js"></script>・マップ表示
div要素にて、マップ(id : map)をブラウザ画面上に配置します。
<div id="map"></div>・スクリプト記述
script 要素にて、マップ(id:map)を表示し、検索するためのスクリプトを記述します。
<script> mapboxgl.accessToken = '<アクセストークン>'; var map = new mapboxgl.Map({ container: 'map', style: '<Style URL>', center: [139.7670516, 35.6811673], zoom: 15 }); map.addControl( new MapboxGeocoder({ accessToken: mapboxgl.accessToken, mapboxgl: mapboxgl }) ); </script>以下に、スクリプトの中身を解説します。
- アクセストークン設定
第2項 で取得した アクセストークンを、
mapboxgl.accessToken にセット設定します。mapboxgl.accessToken = '<アクセストークン>';
- マップ設定
Map オブジェクトを作成し、ページにマップを表示します。
var map = new mapboxgl.Map({ container: 'map', style: '<Style URL>', center: [139.7670516, 35.6811673], zoom: 15 });オプションで、マップの細かな設定をすることができます。
ここでは container、style、center、zoom の4項目のオプションを設定しています。
オプション 説明 container Mapbox GL JSがマップをレンダリングするHTML要素、または要素の文字列ID。 style マップのスタイル。 center 最初にマップ表示する地理的中心点。 zoom マップの初期ズームレベル。 container 項目には、’map’ を設定します。
style 項目には、第2項で取得した Style URL を指定します。
center 項目には、東京駅周辺(緯度: 35.6809591、経度: 139.7673068)のマップを表示するために、
[139.7670516, 35.6811673] を設定します。zoom 項目には、マップの初期ズームレベルとして 15 を設定します。
- 検索機能 追加
ジオコーダー(・・・場所から 緯度経度の値を取得する) を追加し、
指定した場所を検索できるようにします。map.addControl( new MapboxGeocoder({ accessToken: mapboxgl.accessToken, mapboxgl: mapboxgl }) );addControl メソッドを使用して、コントロールを追加します。
ここでは、Mapbox Geocoding APIの mapbox-gl-geocoder コントロール ( MapboxGeocoder )を追加します。MapboxGeocoder でジオコーダーを設定します。
オプションでは、accessToken、mapboxgl の2項目を設定します。
オプション 説明 accessToken アクセストークン mapboxgl mapboxglオブジェクト accessTokenには アクセストークン mapboxgl.accessToken を、
mapboxglには mapboxglを設定します。
- GitHub:mapbox/mapbox-gl-geocoder
4. おわりに
今回は、Mapboxを使って 指定した場所の地図を検索 してみました。
mapbox-gl-geocoderを使用することで、
他の地図サービスと同じような検索機能を、とても簡単に追加することができました。参考情報
- Mapbox GL _ JS API REFERENCE
- 投稿日:2021-03-02T23:28:54+09:00
【JavaScript 関数】RGBメソッドの使い方
- 投稿日:2021-03-02T23:25:03+09:00
【JavaScript 関数】clearIntervalメソッドの使い方
clearInterval()
setInterval()でセットしたタイマーを解除する
これが元になって調査
methods: { startTimer: function () { // 残り5秒 this.restSec = 5; // タイマースタート。1秒(1000ミリ秒)ごとに、1秒減らす this.timerObj = setInterval(() => { this.restSec--; }, 1000) // console.log(this.timerObj); } }, watch: { restSec: function() { // 0秒以下になったらアラート&タイマー停止 if (this.restSec <= 0) { alert("制限時間です。"); clearInterval(this.timerObj); } }, }参考
- 投稿日:2021-03-02T23:20:43+09:00
【jquery,js】表示されたcsvを並べ替える[sort,join,trim,split,each] [js10_20210302]
処理の概要
テキストエリアに表示されたcsvデータを読みの順で並べ替えを行います。
処理のフロー:
(1)初期化メソッドでテキストエリアにcsvテキストを表示します。
(2)実行ボタンを押下するとテキストを取得します。
(3)指定の文字列を取得し、任意のカラムで比較を行います。
(4)テキストエリア2にソートしたテキストを表示する画面イメージ
画像1
画像2
ソースコード
index.html<body onload="init()"> <!-- テキストエリア表示内容はタブ入力されるので、インデント注意 --> <textarea id="beforeUserInput"></textarea> <br> <input type="button" id="execSortButton" value="並べ替え"> <br> <textarea id="afterUserInput"></textarea> </body>リファクタリングする前
main.jsfunction init(){ var lobjText = "" var defaultStr = [ ["蜜柑","みかん",80], ["西瓜","すいか",300], ["葡萄","ぶどう",280] ] $.each(defaultStr,function(){ lobjText = lobjText + this + "\n"; }); $("#beforeUserInput").val(lobjText) }; $(function() { $("#execSortButton").click(function(){ var sortItem = $("#beforeUserInput").val(); sortItem = sortItem.trim(); var sortArray = sortItem.split("\n"); // 列数を指定 var sortColumnNum = 2; sortArray.sort(function(sortName1,sortName2){ //ソートする対象となる列数を格納する var strNumber = sortColumnNum - 1 var compareNameA = sortName1.split(",")[strNumber]; var compareNameB = sortName2.split(",")[strNumber]; if (compareNameA < compareNameB) return -1; if (compareNameA > compareNameB) return 1; return 0; }); outputText = sortArray.join("\n") $("#afterUserInput").val(outputText); }); });ポイント
html:
(1)特になし
js:
(1)sortItemの前後に無駄な改行や、タブ文字、半角スペースが存在しないように除去する。
この方法は、「trim()」を利用する
(2)ソート処理は、sort()関数の引数に関数を指定することで複雑な条件でソートが行える。
(3)join()は配列に対して、セパレーターをドッキングさせて文字列とする。
引数がない場合は、カンマでドッキング。
[a,b,c].join() => "a,b,c"
(4)split()は引数を入力すると、セパレータを排除して、配列に変換する。
"a\nb\nc".split(\n) => [a,b,c] 便利!参考資料
JavaScript(仕事の現場でサッと使える!デザイン教科書) p210
- 投稿日:2021-03-02T22:38:01+09:00
CKEditor をローカルPCでHTML(WYSIWYG)エディタもどきとして使用する
はじめに
windows環境でフリーのHTMLエディタを探したところ、適当なのが見つからずにCKEditorエディタなるものが検索にヒットしたので調べてみたらwebアプリだったので、それを出来るだけwindows環境でローカルアプリのように使用出来るようにしてみた。
まずはカスタマイズを一切せずに使用してみる
以下のコードをCKEditor.htmと名前を付けて保存し、ブラウザで開いてみる
CKEditor.htm<script src="https://cdn.ckeditor.com/4.16.0/full-all/ckeditor.js"></script> <textarea name="editor1" id="editor1"></textarea> <script> const editor = CKEDITOR.replace('editor1'); </script>
メニューの左上のソースというボタンで、htmlフォーマットとソースコードを切り替えて、ソースコードをクリップボードにコピーすれば、最小限のHTMLエディタとして機能させることが出来ます。いろいろカスタマイズして使い勝手を良くしてみる
やりたいこと
- 起動時に最大化して起動する
- ファイルを開くコマンドを追加する
- 保存ボタンで、ファイルを保存出来るようにする
保存ボタンを有効にするには、<textarea>
タグを<form>
タグで囲みます
保存時にダイアログを表示するには、ブラウザの設定が必要です
例:Google Chromeの場合- ウィンドウを閉じるときに確認メッセージを表示させる
- 自分用のcssファイルを用意して、適用できるようにする
- インデント(字下げ)を1文字単位に変更する(デフォルトでは40px単位)
CKEditor.htm<script src="https://cdn.ckeditor.com/4.16.0/full-all/ckeditor.js"></script> <form> <textarea name="editor1" id="editor1"></textarea> </form> <script> //ファイルを開くボタン押下時のファイル読込み処理 function onOpenFileChange(event) { const file = event.target.files[0]; if(file){ filename = file.name; const reader = new FileReader(); reader.readAsText(file); reader.onload = () => { editor.setData( reader.result ); }; } } //ファイルを開くボタン押下処理 function OpenFile(editor) { const input = document.createElement('input'); input.type = 'file'; input.accept = 'text/html'; input.setAttribute('onchange', 'onOpenFileChange(event)'); input.click(); } //保存ボタン押下処理 function SaveFile(editor) { const a = document.createElement('a'); a.href = 'data:text/plain;charset=utf-8,' + encodeURIComponent(editor.getData()); a.download = filename; a.click(); } CKEDITOR.on('instanceReady', function (event) { //保存ボタン置換え var overridecmd = new CKEDITOR.command(editor, {exec: SaveFile}); event.editor.commands['save'].exec = overridecmd.exec; //最大化する event.editor.execCommand('maximize'); }); let filename = ''; //インデント(字下げ)の単位を1文字に変更する CKEDITOR.config.indentUnit = 'em'; CKEDITOR.config.indentOffset = 1; //スキンを適用し、見た目を変更する CKEDITOR.config.skin = 'moono'; //ユーザ定義のCSSを適用する CKEDITOR.config.contentsCss = [ './style.css']; const editor = CKEDITOR.replace('editor1'); //エディタのプラグインが呼び出される際のcallback editor.on('pluginsLoaded', function(event) { //ファイルを開くボタンを追加する this.ui.addButton('openFile', { label: 'ファイルを開く', command: 'openFile', toolbar: 'document,3', //ボタンを追加するtoolbarと位置を指定 icon: 'https://icongr.am/fontawesome/folder-open-o.svg' }); this.addCommand('openFile', {exec: OpenFile}); }); //ウィンドウを閉じる時に確認メッセージを表示させる window.addEventListener('beforeunload', function(event) { event.preventDefault(); event.returnValue = ''; }); </script>style.cssbody {background-color:#FEF2DB;} p {margin:2px 0; text-align:justify; text-justify:inter-ideograph; font-size: 1.0em; font-family:sans-serif;} hr {margin:20px 0;} h1 {margin:8px 0; text-align:justify; text-justify:inter-ideograph; font-size: 1.2em; font-family:sans-serif;} a:link {color:blue; text-decoration:underline; font-family:sans-serif;} a:visited {color:purple; text-decoration:underline; font-family:sans-serif;}CKEditor.htm
style.css
を同じフォルダにコピーして、CKEditor.htm をブラウザで開きます環境
OS : WINDOWS 10
ブラウザ : Google Chrome
CKEditorバージョン : CKEditor4参考
CKEditor4 リファレンス
CKEditor4 Githubリポジトリ
ブログ記事等のエディタを実装する際はCKEditor!さらに便利でカッコよく使い易く!サンプルソースあり
javascript - CKEditorの[保存]ボタンでクリックイベントをキャプチャする方法
Webデザインで使えるWebアイコンのCDNサービス「icongram」
- 投稿日:2021-03-02T22:32:30+09:00
pm2でnext js を永続化させる方法について。プロダクション環境での注意点も含めてご紹介
next jsを永続化し落ちないようにするために試行錯誤したのでご紹介。
next jsをAWS ec2 のような環境に直接デプロイ(npm run start)すると安定せず突然サイトが落ちることがあります。
pm2を使用すると自動的にサーバーを再起動&監視してくれプロダクション環境でも安定して使用可能です。Step1
pm2 を環境にインストール
npm install -g pm2上記のコマンドをPC(もしくはクラウド環境)に打ち込むとインストールできます。
Step2
next jsをビルドする。
npm run buildビルドすることにより、高速にページが開けるようになります。
Step3
pm2でnext jsを永続化
pm2 start npm --name "next" -- startちなみに以下のパスから起動すると、ビルド前の非効率なサーバーが永続化されるので要注意。
ページスピードインサイトで構築中のサイトを”startで起動したもの”と"以下のパスで起動したも"の比較すると、ページの送信サイズが10倍ほど違うという結果になりました。(当然重すぎてまともな時間で動きませんでした。)
./node_modules/next/dist/bin/nextStep4
pm2でサーバーが永続化されていることを確認
pm2 list以上で完了!
- 投稿日:2021-03-02T22:14:07+09:00
TypeScriptをなんとなく理解する③ ~型を組み合わせる: Generics~
この記事は何?
TypeScriptとは何か、何がいいのかを伝えようと頑張った記事の第3弾です!
今回は、型の使いまわしであるGenericsについて見ていきます。
過去の記事、続編も合わせて御覧ください!
・ JavaScriptを知っている方がTypeScriptをなんとなく理解するための記事① ~はじめに~
・ TypeScriptをなんとなく理解する② ~型を組み合わせる: Union~
・ TypeScriptをなんとなく理解する③ ~型を組み合わせる: Generics~← この記事
・ TypeScriptをなんとなく理解する④ ~構造型~ (3/3更新予定)型の組み合わせとは?
TypeScriptでは、型と型を組み合わせて新しく型を作り上げることが出来ます。
この型の組み合わせの方法は2つあります。
1つは、Union、もう1つは、Genericsです。この記事では、Genericsの概要について説明していきます。
※ Unionについては、前回の記事で紹介しています。
Genericsとは?
TypeScriptにおけるGenerics(ジェネリクス)を学ぶ前に、そもそもGenericsの英単語の意味を確認してみます。
genericsとは
一般的な、ジェネリックの、属の、ノーブランド商品なんだか、定まっていないというか、汎用的な意味合いを感じます。
実際に、TypeScriptにおける、Genericsも同じような意味です。
TypeScriptにおけるGenericsは、利用されるまで型が確定しない、クラス, 関数, インターフェイスなどなど...を実現するためのものです。事前にガッチガチに型定義するのもいいですが、汎用的に使える型もほしいです。
そこで、Genericsが活躍します。もう少しイメージを掴むために、実際にコードを見ていきます。
まず、今までの知識を使って、関数を作ってみます。
const returnStrArray = (value1: string, value2: string):string[] => { return [value1, value2]; }; const returnNumberArray = (value1: number, value2: number):number[] => { return [value1, value2]; }; const penguinsName = returnStrArray('コウテイペンギン','アデリーペンギン'); // ['コウテイペンギン', 'アデリーペンギン'] const penguinsWeight = returnNumberArray(1000, 50); // [1000, 50]上のコードは、引数
value1
,value2
を含む配列を返す関数です。
...同じようなことを2回もやるのって冗長じゃないですか...?そこで、汎用的なことを出来るGenericsを利用します?
const returnArray = <T>(value1: T, value2: T):T[] => { return [value1, value2]; } const penguinsNames = returnArray<string>('コウテイペンギン','アデリーペンギン'); // ['コウテイペンギン', 'アデリーペンギン'] const penguinsWeights = returnArray<number>(1000, 50); // [1000, 50]
T
は何にでも変わる型です。const penguinsNames = returnArray<string>('コウテイペンギン','アデリーペンギン');
<string>
のように指定すると、T
がstring
であることを示します。なので、以下の型を定義した関数と同義になります。const returnArray = (value1: string, value2: string):string[] => { return [value1, value2]; }アロー関数でGenericsを利用するときは、
<T>
を()
の前につけてあげる必要があるため<T>()
のような形になっています。このように、Genericsを利用することで汎用的で使いまわし可能な型定義をすることが出来ます。
TypeScriptにおけるGenericsは、利用されるまで型が確定しない、クラス, 関数, インターフェイスなどなど...を実現するためのものです。
この伏線を回収することが出来たと思います。
T
は<string>
のように指定するまで型が確定しないことから、今回の例では、利用するまで型が確定しない関数を実現出来たと思います。
const penguinsNames = returnArray<string>('コウテイペンギン','アデリーペンギン'); // 利用したから型が確定他にも、
interface funcs<T> { add: (obj: T) => void; get: () => T; }みたいに、独自の型を宣言出来たりもします。
おわりに
できるだけわかりやすく伝えようとしましたが、理解出来そうでしょうか?
この記事だけで理解していただけましたら、是非LGTM✨いただければと思います。理解できない方がいた場合は...もっとわかりやすく伝えられるよう精進します...!
参考文献
- 投稿日:2021-03-02T22:14:07+09:00
TypeScriptをなんとなく理解するための記事③ ~型を組み合わせる: Generics~
この記事は何?
TypeScriptとは何か、何がいいのかを伝えようと頑張った記事の第3弾です!
今回は、型の使いまわしであるGenericsについて見ていきます。
過去の記事、続編も合わせて御覧ください!
・ JavaScriptを知っている方がTypeScriptをなんとなく理解するための記事① ~はじめに~
・ TypeScriptをなんとなく理解するための記事② ~型を組み合わせる: Union~
・ TypeScriptをなんとなく理解するための記事③ ~型を組み合わせる: Generics~← この記事
・ TypeScriptをなんとなく理解するための記事④ ~構造型~ (3/3更新予定)型の組み合わせとは?
TypeScriptでは、型と型を組み合わせて新しく型を作り上げることが出来ます。
この型の組み合わせの方法は2つあります。
1つは、Union、もう1つは、Genericsです。この記事では、Genericsの概要について説明していきます。
※ Unionについては、前回の記事で紹介しています。
Genericsとは?
TypeScriptにおけるGenerics(ジェネリクス)を学ぶ前に、そもそもGenericsの英単語の意味を確認してみます。
genericsとは
一般的な、ジェネリックの、属の、ノーブランド商品なんだか、定まっていないというか、汎用的な意味合いを感じます。
実際に、TypeScriptにおける、Genericsも同じような意味です。
TypeScriptにおけるGenericsは、利用されるまで型が確定しない、クラス, 関数, インターフェイスなどなど...を実現するためのものです。事前にガッチガチに型定義するのもいいですが、汎用的に使える型もほしいです。
そこで、Genericsが活躍します。もう少しイメージを掴むために、実際にコードを見ていきます。
まず、今までの知識を使って、関数を作ってみます。
// 引数value1, value2は文字列型で、返り値は各要素が文字列の配列 const returnStrArray = (value1: string, value2: string):string[] => { return [value1, value2]; }; // 引数value1, value2は数値型で、返り値は各要素が数値の配列 const returnNumberArray = (value1: number, value2: number):number[] => { return [value1, value2]; }; const penguinsName = returnStrArray('コウテイペンギン','アデリーペンギン'); // ['コウテイペンギン', 'アデリーペンギン'] const penguinsWeight = returnNumberArray(1000, 50); // [1000, 50]上のコードは、引数
value1
,value2
を含む配列を返す関数です。
...同じようなことを2回もやるのって冗長じゃないですか...?そこで、汎用的なことを出来るGenericsを利用します?
const returnArray = <T>(value1: T, value2: T):T[] => { return [value1, value2]; } const penguinsNames = returnArray<string>('コウテイペンギン','アデリーペンギン'); // ['コウテイペンギン', 'アデリーペンギン'] const penguinsWeights = returnArray<number>(1000, 50); // [1000, 50]
T
は何にでも変わる型です。// T → string const penguinsNames = returnArray<string>('コウテイペンギン','アデリーペンギン');
<string>
のように指定すると、T
がstring
であることを示します。なので、以下の型を定義した関数と同義になります。const returnArray = (value1: string, value2: string):string[] => { return [value1, value2]; }アロー関数でGenericsを利用するときは、
<T>
を()
の前につけてあげる必要があるため<T>()
のような形になっています。このように、Genericsを利用することで汎用的で使いまわし可能な型定義をすることが出来ます。
TypeScriptにおけるGenericsは、利用されるまで型が確定しない、クラス, 関数, インターフェイスなどなど...を実現するためのものです。
この伏線を回収することが出来たと思います。
T
は<string>
のように指定するまで型が確定しないことから、今回の例では、利用するまで型が確定しない関数を実現出来たと思います。
const penguinsNames = returnArray<string>('コウテイペンギン','アデリーペンギン'); // 利用したから型が確定他にも、
interface funcs<T> { add: (obj: T) => void; get: () => T; }みたいに、独自の型を宣言出来たりもします。
おわりに
できるだけわかりやすく伝えようとしましたが、理解出来そうでしょうか?
この記事だけで理解していただけましたら、是非LGTM✨いただければと思います。理解できない方がいた場合は...もっとわかりやすく伝えられるよう精進します...!
参考文献
- 投稿日:2021-03-02T22:14:07+09:00
TypeScriptをなんとなく理解するための記事③ ~汎用的な型を作成する: Generics~
この記事は何?
TypeScriptとは何か、何がいいのかを伝えようと頑張った記事の第3弾です!
今回は、型の使いまわしであるGenericsについて見ていきます。
過去の記事、続編も合わせて御覧ください!
- JavaScriptを知っている方がTypeScriptをなんとなく理解するための記事① ~はじめに~
- TypeScriptをなんとなく理解するための記事② ~型を組み合わせる: Union~
- TypeScriptをなんとなく理解するための記事③ ~汎用的な型を作成する: Generics~
← この記事
- TypeScriptをなんとなく理解するための記事④ ~構造型~
TypeScriptがなんとなく理解できたら、是非インストールして、触ってみてください!
→ 【TypeScriptを導入しよう!】エンジニアもすなるTypeScriptといふものを、我もしてみむとてするなり(3/3 作成予定)型の組み合わせとは?
TypeScriptでは、型と型を組み合わせて新しく型を作り上げることが出来ます。
この型の組み合わせの方法は2つあります。
1つは、Union、もう1つは、Genericsです。この記事では、Genericsの概要について説明していきます。
※ Unionについては、前回の記事で紹介しています。
Genericsとは?
TypeScriptにおけるGenerics(ジェネリクス)を学ぶ前に、そもそもGenericsの英単語の意味を確認してみます。
genericsとは
一般的な、ジェネリックの、属の、ノーブランド商品なんだか、定まっていないというか、汎用的な意味合いを感じます。
実際に、TypeScriptにおける、Genericsも同じような意味です。
TypeScriptにおけるGenericsは、利用されるまで型が確定しない、クラス, 関数, インターフェイスなどなど...を実現するためのものです。事前にガッチガチに型定義するのもいいですが、汎用的に使える型もほしいです。
そこで、Genericsが活躍します。もう少しイメージを掴むために、実際にコードを見ていきます。
まず、今までの知識を使って、関数を作ってみます。
// 引数value1, value2は文字列型で、返り値は各要素が文字列の配列 const returnStrArray = (value1: string, value2: string):string[] => { return [value1, value2]; }; // 引数value1, value2は数値型で、返り値は各要素が数値の配列 const returnNumberArray = (value1: number, value2: number):number[] => { return [value1, value2]; }; const penguinsName = returnStrArray('コウテイペンギン','アデリーペンギン'); // ['コウテイペンギン', 'アデリーペンギン'] const penguinsWeight = returnNumberArray(1000, 50); // [1000, 50]上のコードは、引数
value1
,value2
を含む配列を返す関数です。
...同じようなことを2回もやるのって冗長じゃないですか...?そこで、汎用的なことを出来るGenericsを利用します?
const returnArray = <T>(value1: T, value2: T):T[] => { return [value1, value2]; } const penguinsNames = returnArray<string>('コウテイペンギン','アデリーペンギン'); // ['コウテイペンギン', 'アデリーペンギン'] const penguinsWeights = returnArray<number>(1000, 50); // [1000, 50]
T
は何にでも変わる型です。// T → string const penguinsNames = returnArray<string>('コウテイペンギン','アデリーペンギン');
<string>
のように指定すると、T
がstring
であることを示します。なので、以下の型を定義した関数と同義になります。const returnArray = (value1: string, value2: string):string[] => { return [value1, value2]; }アロー関数でGenericsを利用するときは、
<T>
を()
の前につけてあげる必要があるため<T>()
のような形になっています。このように、Genericsを利用することで汎用的で使いまわし可能な型定義をすることが出来ます。
TypeScriptにおけるGenericsは、利用されるまで型が確定しない、クラス, 関数, インターフェイスなどなど...を実現するためのものです。
この伏線を回収することが出来たと思います。
T
は<string>
のように指定するまで型が確定しないことから、今回の例では、利用するまで型が確定しない関数を実現出来たと思います。
const penguinsNames = returnArray<string>('コウテイペンギン','アデリーペンギン'); // 利用したから型が確定他にも、
interface funcs<T> { add: (obj: T) => void; get: () => T; }みたいに、独自の型を宣言出来たりもします。
おわりに
できるだけわかりやすく伝えようとしましたが、理解出来そうでしょうか?
この記事だけで理解していただけましたら、是非LGTM✨いただければと思います。理解できない方がいた場合は...もっとわかりやすく伝えられるよう精進します...!
Next: TypeScriptをなんとなく理解するための記事④ ~構造型~
参考文献
- 投稿日:2021-03-02T20:59:14+09:00
「見える!動きが見える!」JavaScriptで残像カメラを作ってみた
俺にはおまえの動きが見えるぞ
Webカメラの画像を、JavaScriptで処理して残像をつけてみました。ダンスや機か運動の様子を撮影して、動きを見られるようできたらと考えつくりました。フリーズさせるための「止まれ」ボタンもつけてみました。でも、できがいまいちでした。(-_-)
https://kaihatuiinkai.jp/afterimage/「動きがブレて見える!」。動きがはっきり見えない。この教材はボツかな?
それでは、作成ステップを説明します。データの流れとプログラム作動を確認してもらうため、ソースコードを全て貼り付けしました。長くなってしまうことをご了承願います。
WebカメラをJavaScriptで画像表示させるための方法はこちらを参照願います。
https://qiita.com/mvm43236/items/398e92761440eb006271残像表示(画像重ね合わせ)
Webカメラの画像を<canvas>に表示します。前の画像と重ね合わせをして、右の<canvas>に表示します。
作動例→ https://kaihatuiinkai.jp/afterimage/camera5.htmlcamera5.html<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>残像表示(画像重ね合わせ)</title> <style> canvas, video{ border: 1px solid gray; } </style> </head> <body> <h2>残像表示(画像重ね合わせ)</h2> video canvas1 canvas2(画像重ね合わせ)<br> <video id="video" width="300" height="200"></video> <canvas id="canvas1" width="300" height="200"></canvas> <canvas id="canvas2" width="300" height="200"></canvas> <script> var imageDelay = 10; // 表示遅延 1秒 window.onload = () => { const video = document.querySelector("#video"); const canvas1 = document.querySelector("#canvas1"); const canvas2 = document.querySelector("#canvas2"); // カメラ設定 // const constraints = { audio: false, video: { width: 300, height: 200, facingMode: "user" // フロントカメラを利用する // facingMode: { exact: "environment" } // リアカメラを利用する場合 } }; // カメラを<video>と同期 // navigator.mediaDevices.getUserMedia(constraints) .then( (stream) => { video.srcObject = stream; video.onloadedmetadata = (e) => { video.play(); }; }) .catch( (err) => { console.log(err.name + ": " + err.message); }); // canvas2 の準備 var picture2 = canvas2.getContext("2d"); // canvas2に画像を貼り付ける picture2.drawImage(video, 0, 0, canvas1.width, canvas1.height); // RGBの範囲 function set255(value){ if (value > 255) return 255; if (value < 0) return 0; return value; } // インターバル処理 var intervalID = setInterval(function(){ // canvas1 の準備 var picture1 = canvas1.getContext("2d"); // canvas1に画像を貼り付ける picture1.drawImage(video, 0, 0, canvas1.width, canvas1.height); // イメージデータの取得 image1 = picture1.getImageData(0,0,canvas1.width, canvas1.height); // canvas2 の準備 var picture2 = canvas2.getContext("2d"); // イメージデータの取得 image2 = picture2.getImageData(0,0,canvas1.width, canvas1.height); var afterimage =picture2.createImageData(canvas2.width, canvas2.height); // 画像合成 var cnt = 0; var amount = 128 / 255; for (var y = 0; y < canvas1.height; y++) { for (var x = 0;x < canvas1.width; x++) { afterimage.data[(cnt*4)] = set255(Math.round((1 - amount) * image1.data[(cnt*4)] + (amount * image2.data[(cnt*4)]))); afterimage.data[(cnt*4)+1] = set255(Math.round((1 - amount) * image1.data[(cnt*4)+1] + (amount * image2.data[(cnt*4)+1]))); afterimage.data[(cnt*4)+2] = set255(Math.round((1 - amount) * image1.data[(cnt*4)+2] + (amount * image2.data[(cnt*4)+2]))); afterimage.data[(cnt*4)+3] = 255; cnt++; } } // エラー回避処理 try { // canvas2 に変数の画像を貼り付ける document.getElementById("canvas2").getContext('2d').putImageData(afterimage, 0, 0); }catch(e){ // エラー時には何もしない }; }, 300); }; </script> </body> </html>残像表示(差分抽出表示)
Webカメラの画像を<canvas>に表示します。前の画像と比較して、下の<canvas>に差分を表示します。
作動例→ https://kaihatuiinkai.jp/afterimage/camera6.html動いた部分が表示されます。
camera6.html<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>残像表示(差分抽出表示)</title> <style> canvas, video{ border: 1px solid gray; } </style> </head> <body> <h2>残像表示(差分抽出表示)</h2> video canvas1 canvas2<br> <video id="video" width="300" height="200"></video> <canvas id="canvas1" width="300" height="200"></canvas> <canvas id="canvas2" width="300" height="200"></canvas> <br>canvas2-canvas1 差分抽出画像<br> <canvas id="canvas3" width="300" height="200"></canvas> <script> var imageDelay = 10; // 表示遅延 1秒 window.onload = () => { const video = document.querySelector("#video"); const canvas1 = document.querySelector("#canvas1"); const canvas2 = document.querySelector("#canvas2"); const canvas3 = document.querySelector("#canvas3"); // カメラ設定 // const constraints = { audio: false, video: { width: 300, height: 200, facingMode: "user" // フロントカメラを利用する // facingMode: { exact: "environment" } // リアカメラを利用する場合 } }; // カメラを<video>と同期 // navigator.mediaDevices.getUserMedia(constraints) .then( (stream) => { video.srcObject = stream; video.onloadedmetadata = (e) => { video.play(); }; }) .catch( (err) => { console.log(err.name + ": " + err.message); }); // canvas2 の準備 var picture2 = canvas2.getContext("2d"); // canvas2に画像を貼り付ける picture2.drawImage(video, 0, 0, canvas1.width, canvas1.height); // RGBの範囲 function set255(value){ if (value > 255) return 255; if (value < 0) return 0; return value; } // インターバル処理 var intervalID = setInterval(function(){ // canvas1 の準備 var picture1 = canvas1.getContext("2d"); // canvas1に画像を貼り付ける picture1.drawImage(video, 0, 0, canvas1.width, canvas1.height); // イメージデータの取得 image1 = picture1.getImageData(0,0,canvas1.width, canvas1.height); // canvas2 の準備 var picture2 = canvas2.getContext("2d"); // イメージデータの取得 image2 = picture2.getImageData(0,0,canvas1.width, canvas1.height); var afterimage =picture2.createImageData(canvas2.width, canvas2.height); // 画像合成 var cnt = 0; var difference = 15; var cValue = 255; for (var y = 0; y < canvas1.height; y++) { for (var x = 0;x < canvas1.width; x++) { if((image1.data[(cnt*4)]-image2.data[(cnt*4)] > difference)||((image1.data[(cnt*4)+1]-image2.data[(cnt*4)+1] > difference))||(image1.data[(cnt*4)+2]-image2.data[(cnt*4)+2] > difference)){ afterimage.data[(cnt*4)] = image1.data[(cnt*4)]; afterimage.data[(cnt*4)+1] = image1.data[(cnt*4)+1]; afterimage.data[(cnt*4)+2] = image1.data[(cnt*4)+2]; }else{ afterimage.data[(cnt*4)] = cValue; afterimage.data[(cnt*4)+1] = cValue; afterimage.data[(cnt*4)+2] = cValue; }; afterimage.data[(cnt*4)+3] = 255; cnt++; } } // エラー回避処理 try { // canvas2 に変数の画像を貼り付ける document.getElementById("canvas2").getContext('2d').putImageData(image1, 0, 0); // canvas3 の準備 var picture3 = canvas3.getContext("2d"); // canvas3 に変数の画像を貼り付ける document.getElementById("canvas3").getContext('2d').putImageData(afterimage, 0, 0); }catch(e){ // エラー時には何もしない }; }, 300); }; </script> </body> </html>残像表示(差分抽出、重ね合わせ)
Webカメラの画像を<canvas>に表示します。前の画像と比較して、下の<canvas>に差分を重ね合わせて表示します。
作動例→ https://kaihatuiinkai.jp/afterimage/camera7.html動いた部分が影のようについてきます。
・カメラの前で顔を動かすと、シュールな顔になれます。
・仮○ライ○○の変身シーンの動きをすると、かっこよく見えます。
・カメラに向かって、拳を何度も降り出すと、ペガ○○流○拳、北○百○拳が再現できます。
・・・あれ?教材にならない。camera7.html<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>残像表示(差分抽出、重ね合わせ)</title> <style> canvas, video{ border: 1px solid gray; } </style> </head> <body> <h2>残像表示(差分抽出、重ね合わせ)</h2> video canvas1 現在の画像 canvas2 この画像をもとにして重ね合わせ<br> <video id="video" width="300" height="200"></video> <canvas id="canvas1" width="300" height="200"></canvas> <canvas id="canvas2" width="300" height="200"></canvas> <br>canvas2-canvas1 差分抽出、重ね合わせ画像<br> <canvas id="canvas3" width="300" height="200"></canvas> <script> var imageMax = 30; // 遅延最大時間 最大3秒 var imageArray = new Array(imageMax); // 画像保存用 var imageNumber = 0; // 現在の画像番号 var imageDisplay = 0; // 表示番号 var imageDelay = 8; // 表示遅延 1秒 // 読み込まれたら 作動します window.onload = () => { const video = document.querySelector("#video"); const canvas1 = document.querySelector("#canvas1"); const canvas2 = document.querySelector("#canvas2"); const canvas3 = document.querySelector("#canvas3"); // カメラ設定 // const constraints = { audio: false, video: { width: 300, height: 200, facingMode: "user" // フロントカメラを利用する // facingMode: { exact: "environment" } // リアカメラを利用する場合 } }; // カメラを<video>と同期 // navigator.mediaDevices.getUserMedia(constraints) .then( (stream) => { video.srcObject = stream; video.onloadedmetadata = (e) => { video.play(); }; }) .catch( (err) => { console.log(err.name + ": " + err.message); }); // canvas3 の準備 var picture3 = canvas3.getContext("2d"); // canvas2に画像を貼り付ける picture3.drawImage(video, 0, 0, canvas3.width, canvas3.height); // イメージデータの取得 var afterimage =picture3.createImageData(canvas3.width, canvas3.height); // エラー回避のため、事前に画像を入れる for(var i= 0; i <= imageMax; i++){ imageArray[i] = picture3.getImageData(0,0,canvas1.width, canvas1.height); }; // インターバル処理 var intervalID = setInterval(function(){ // 画像記録番号 imageNumber = imageNumber + 1; if(imageNumber >= imageMax){ imageNumber =0; } // 表示番号 imageDisplay = imageNumber - imageDelay; if(imageDisplay <0 ){ imageDisplay = imageDisplay + imageMax; // マイナスの処理 } // canvas1 の準備 var picture1 = canvas1.getContext("2d"); // canvas1に画像を貼り付ける picture1.drawImage(video, 0, 0, canvas1.width, canvas1.height); // イメージデータの取得 imageArray[imageNumber] = picture1.getImageData(0,0,canvas1.width, canvas1.height); // canvas2 の準備 var picture2 = canvas2.getContext("2d"); try { // canvas2 に変数の画像を貼り付ける document.getElementById("canvas2").getContext('2d').putImageData(imageArray[imageDisplay], 0, 0); }catch(e){ // エラー時には何もしない }; // 画像合成 imageDisplay = imageNumber - imageDelay; if(imageDisplay <0 ){ imageDisplay = imageDisplay + imageMax; // マイナスの処理 } afterimage = imageArray[imageDisplay]; for(var i= 1; i <= imageDelay; i++){ funcComposition(imageArray[setImageNumber(imageNumber - imageDelay + i)],imageArray[imageDisplay]); }; // エラー回避処理 try { // canvas3 の準備 var picture3 = canvas3.getContext("2d"); // canvas3 に変数の画像を貼り付ける document.getElementById("canvas3").getContext('2d').putImageData(afterimage, 0, 0); }catch(e){ // エラー時には何もしない }; }, 50); // ImageNumberの範囲 function setImageNumber(value){ if (value > imageMax) return (value - imageMax); if (value < 0) return (value + imageMax); return value; } function funcComposition(image1,image2){ // 画像合成 var cnt = 0; var difference = 4; var cValue = 255; for (var y = 0; y < canvas1.height; y++) { for (var x = 0;x < canvas1.width; x++) { if((image1.data[(cnt*4)]-image2.data[(cnt*4)] > difference)||((image1.data[(cnt*4)+1]-image2.data[(cnt*4)+1] > difference))||(image1.data[(cnt*4)+2]-image2.data[(cnt*4)+2] > difference)){ afterimage.data[(cnt*4)] = image1.data[(cnt*4)]; afterimage.data[(cnt*4)+1] = image1.data[(cnt*4)+1]; afterimage.data[(cnt*4)+2] = image1.data[(cnt*4)+2]; }; afterimage.data[(cnt*4)+3] = 255; cnt++; } } }; }; </script> </body> </html>以前、Windows用に画像重ね合わせソフト「多重露光」を作りました。
http://www1.iwate-ed.jp/tantou/joho/material/tajuurokou/index.html
.NETの機能を使って、Windowsネイティブのソフトで処理していたことが、今は、ブラウザで表示できることに進歩を感じます。ハードとソフトの進化ですね。
1990年代、CPUがV30のPC9801で「気象衛星画像」のビットマップを1ドットごとに処理をして表示する理科の教材を作りました。でも、400×400を終えるのに1分30秒かかり、授業で使うことをあきらめました。→事前に処理して保存しておきました。それを今では、0.05秒で処理してしまう。コンピュータの能力の進化はすばらしいですね。
- 投稿日:2021-03-02T19:45:58+09:00
TypeScriptをなんとなく理解する② ~型を組み合わせる: Union~
この記事は何?
TypeScriptとは何か、何がいいのかを伝えようと頑張った記事の第2弾です!
今回は、型を組み合わせて新たな型を生成する、
Unionについて見ていきます。過去の記事、続編も合わせて御覧ください!
・ JavaScriptを知っている方がTypeScriptをなんとなく理解するための記事① ~はじめに~
・ TypeScriptをなんとなく理解する② ~型を組み合わせる: Union~← この記事
・ TypeScriptをなんとなく理解する③ ~型を組み合わせる: Generics~
・ TypeScriptをなんとなく理解する④ ~構造型~ (3/3更新予定)型の組み合わせとは?
TypeScriptでは、型と型を組み合わせて新しく型を作り上げることが出来ます。
この型の組み合わせの方法は2つあります。
1つは、Union、もう1つは、Genericsです。この記事では、Unionの概要について説明していきます。
Unionによる型の作成
unionの基本
まずは、Unioinを使わずに型を作ってみます。
型はtype
を利用して、以下のように作成します。type Fish = "sanma";新しく
Fish
型を作成しました。
このFish
型は、文字列(string)のsanma
のみを許容する型です。
そのため、この型を使って以下のように、定数lunch
を定義できます。type Fish = "sanma"; const lunch: Fish = "sanma";
lunch
はFish
型なので、"sanma"
のみ許容します。
つまり、ランチにはサンマしか食べることが出来ません。
実際にlunch
にサンマ以外を定義しようとすると...const lunch: Fish = "iwana"; // タイプ '"iwana"'はタイプ '"sanma"'に割り当てることができません。ts(2322)
Fish
型のlunch
には、"iwana"
を定義することが出来ませんでした。
ランチには、サンマもイワナも食べるので、どちらも許容したいです。
そこで、Union
を利用します。Union
は|
を利用して次のように記述します。type Fish = "sanma" | "iwana"; const lunch: Fish = "iwana";このように
|
を利用してFish
型に"sanma"
,"iwana"
に絞る事ができます。ちなみに、
"sanma"
,"iwana"
以外は許容されません。type Fish = "sanma" | "iwana"; const lunch: Fish = "iwana"; const dinner: Fish = "sanma"; const breakfirst: Fish = "saba"; // タイプ '"saba"'はタイプ 'Fish'に割り当てることができません。もちろん数値も扱うことができます。
type AndroidVersion = 9 | 10 | 11; const myAndroidOsVersion: AndroidVersion = 11;ちょっと発展した型の作成
以上では、明確な文字や数値に限定して、型を作成しました。
ここまで厳格にしなくても、「文字列string
はOK!」「文字列の入った配列string[]
はOK!」ということも出来ます。具体的には、以下のような記述方法です。
type Food = string | string[]; const branch: Food = 'cake'; const nightMeal: Food = ['ramen', 'onigiri', 'potechi']; const theLastSupper: Food = ['ramen', 'onigiri', 1498]; // タイプ「number」はタイプ「string」に割り当てることができません。ts(2322)レオナルド・ダ・ヴィンチ作の最後の晩餐(
theLastSupper
)は、1498年に完成しましたが、これは数値であり、文字列でないのでエラーが起きます。ちなみに、このような
Union
で型を定義した際に便利なのが型のチェックです。以下のように、型のチェックをすることができます。
タイプ 述語 文字列 typeof s === "string"
数値 typeof n === "number"
ブール値 typeof b === "boolean"
未定義 typeof undefined === "undefined"
関数 typeof f === "function"
配列 Array.isArray(a)
これを使って、型によって条件分岐をすることが出来ます。
const healthCheck = (food: Food): string => { if (typeof food === "string") { return `it's OK`; } else { return `it's Bad`; } }; console.log(healthCheck(["ramen", "onigiri", "potechi"])); // it's Badおわりに
今回はUnionを使って、型を結合してみました!
型を組み合わせて、自分だけのユニークな型を作ってみましょう!※ 「そういえば、前回、Interfaceが出てきたけどtypeと何が違うの...?」と思った方もいるかもしれません。今後、この違いに関しても書く予定ではあります。が、既に記事があるので一旦こちらにご案内します...! → TypeScript の Interface と Type Alias の違い
参考文献
- 投稿日:2021-03-02T19:45:58+09:00
TypeScriptをなんとなく理解するための記事② ~型を組み合わせる: Union~
この記事は何?
TypeScriptとは何か、何がいいのかを伝えようと頑張った記事の第2弾です!
今回は、型を組み合わせて新たな型を生成する、
Unionについて説明していきます。過去の記事、続編も合わせて御覧ください!
- JavaScriptを知っている方がTypeScriptをなんとなく理解するための記事① ~はじめに~
- TypeScriptをなんとなく理解するための記事② ~型を組み合わせる: Union~
← この記事
- TypeScriptをなんとなく理解するための記事③ ~汎用的な型を作成する: Generics~
- TypeScriptをなんとなく理解するための記事④ ~構造型~
TypeScriptがなんとなく理解できたら、是非インストールして、触ってみてください!
→ 【TypeScriptを導入しよう!】エンジニアもすなるTypeScriptといふものを、我もしてみむとてするなり(3/3 作成予定)型の組み合わせとは?
TypeScriptでは、型と型を組み合わせて新しく型を作り上げることが出来ます。
この型の組み合わせの方法は2つあります。
1つは、Union、もう1つは、Genericsです。この記事では、Unionの概要について説明していきます。
※Genericsについては次回の記事で説明しています。
Unionによる型の作成
unionの基本
まずは、Unioinを使わずに型を作ってみます。
型はtype
を利用して、以下のように作成します。type Fish = "sanma";新しく
Fish
型を作成しました。
このFish
型は、文字列(string)のsanma
のみを許容する型です。
そのため、この型を使って以下のように、定数lunch
を定義できます。type Fish = "sanma"; const lunch: Fish = "sanma";
lunch
はFish
型なので、"sanma"
のみ許容します。
つまり、ランチにはサンマしか食べることが出来ません。
実際にlunch
にサンマ以外を定義しようとすると...const lunch: Fish = "iwana"; // タイプ '"iwana"'はタイプ '"sanma"'に割り当てることができません。ts(2322)
Fish
型のlunch
には、"iwana"
を定義することが出来ませんでした。
ランチには、サンマもイワナも食べるので、どちらも許容したいです。
そこで、Union
を利用します。Union
は|
を利用して次のように記述します。type Fish = "sanma" | "iwana"; const lunch: Fish = "iwana";このように
|
を利用してFish
型に"sanma"
,"iwana"
に絞る事ができます。ちなみに、
"sanma"
,"iwana"
以外は許容されません。type Fish = "sanma" | "iwana"; const lunch: Fish = "iwana"; const dinner: Fish = "sanma"; const breakfirst: Fish = "saba"; // タイプ '"saba"'はタイプ 'Fish'に割り当てることができません。もちろん数値も扱うことができます。
type AndroidVersion = 9 | 10 | 11; const myAndroidOsVersion: AndroidVersion = 11;ちょっと発展した型の作成
以上では、明確な文字や数値に限定して、型を作成しました。
ここまで厳格にしなくても、「文字列string
はOK!」「文字列の入った配列string[]
はOK!」ということも出来ます。具体的には、以下のような記述方法です。
type Food = string | string[]; const branch: Food = 'cake'; const nightMeal: Food = ['ramen', 'onigiri', 'potechi']; const theLastSupper: Food = ['ramen', 'onigiri', 1498]; // タイプ「number」はタイプ「string」に割り当てることができません。ts(2322)レオナルド・ダ・ヴィンチ作の最後の晩餐(
theLastSupper
)は、1498年に完成しましたが、これは数値であり、文字列でないのでエラーが起きます。ちなみに、このような
Union
で型を定義した際に便利なのが型のチェックです。以下のように、型のチェックをすることができます。
タイプ 述語 文字列 typeof s === "string"
数値 typeof n === "number"
ブール値 typeof b === "boolean"
未定義 typeof undefined === "undefined"
関数 typeof f === "function"
配列 Array.isArray(a)
これを使って、型によって条件分岐をすることが出来ます。
const healthCheck = (food: Food): string => { if (typeof food === "string") { return `it's OK`; } else { return `it's Bad`; } }; console.log(healthCheck(["ramen", "onigiri", "potechi"])); // it's Badおわりに
今回はUnionを使って、型を結合してみました!
型を組み合わせて、自分だけのユニークな型を作ってみましょう!※ 「そういえば、前回、Interfaceが出てきたけどtypeと何が違うの...?」と思った方もいるかもしれません。今後、この違いに関しても書く予定ではあります。が、既に記事があるので一旦こちらにご案内します...! → TypeScript の Interface と Type Alias の違い
Next: TypeScriptをなんとなく理解するための記事③ ~汎用的な型を作成する: Generics~
参考文献
- 投稿日:2021-03-02T17:58:45+09:00
JavaScript
スキャナと同じ操作をjavascriptで行う方法
KeyboardEvent
キーボード入力と同じイベント発行できるメソッドdispathEvent
あたかもイベント発行したかのように振る舞うメソッドrefs
https://ameblo.jp/personwritep/entry-12456996738.html
https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget-dispatchEventWebAPI
- 投稿日:2021-03-02T17:44:03+09:00
JS配列簡単生成
[…Array(20)].map(() =>{ return {text:’’,point:’’} })のように書くと配列を20個生成できる
- 投稿日:2021-03-02T17:22:30+09:00
【初学者向け】WAI-ARIAを使ってモーダルウィンドウを作る
今まで私はjQueryでモーダルウィンドウを実装していたのですが、最近jQueryを使わないことが増えたのでJavaScriptで実装した際の備忘録です。
使用するWAI-ARIAの属性
aria-controls
指定した要素が値に指定した要素を制御することを示すWAI-ARIAの属性です。
(※HTMLリファレンスから引用)今回の場合は、モーダルウィンドウを開閉するボタンに使用します。
対応するモーダルウィンドウのidと同じ文字列を、開閉するボタンのaria-controls
の値に設定することで、「このボタンとこのモーダルウィンドウが対応している」という設定をします。aria-expanded
要素の開閉の状態を示すためのWAI-ARIAの属性です。
(※HTMLリファレンスから引用)こちらも今回の場合はモーダルウィンドウを開閉するボタンに使用します。
対応する要素が閉じている場合はfalse
、開いている場合はtrue
を設定します。
例えば、モーダルウィンドウを開くボタンを押下するときは、対応するモーダルウィンドウは閉じているはずです。
そのため、モーダルを開くボタンにはaria-expanded="false"
、モーダルを閉じるボタンにはaria-expanded="true"
を設定します。aria-hidden
ユーザーエージェントに認識させたくない要素に指定するためのWAI-ARIA属性です。
(※HTMLリファレンスから引用)
display:none;
やvisibility:hidden;
で隠れている要素に指定する属性です。
今回の場合、モーダルウィンドウは最初非表示になっているので、モーダルウィンドウの大枠の要素に
aria-hidden="true"
を設定します。HTMLの準備
modal.html<!-- モーダルウィンドウ --> <div class="p-modal" id="modal_01" aria-hidden="true"> <div class="p-modal__wrap"> <!-- ↓モーダルを閉じるボタン↓ --> <button class="js-modalClose" aria-controls="modal_01" aria-expanded="true"></button> <div class="p-modal__contents"> モーダルの中身 </div> </div> </div>モーダルウィンドウ側のHTMLには、まず大枠の要素にidを付与します。
開いたモーダルウィンドウ内に、そのモーダルウィンドウを閉じるボタンを設置する場合は、
js-modalClose
といった閉じる用のクラスを付与し、aria-controls
には閉じる対象のモーダルウィンドウに付与されているidと同じものを記述します。index.html<!-- モーダルを開くためのボタン --> <button class="js-modalOpen" aria-controls="modal_01" aria-expanded="false"></button>モーダルウィンドウを開くボタンには、
js-modalOpen
といったクラスを付与し、
aria-controls
には開く対象となるモーダルウィンドウに付与されているidと同じものを記述します。CSSで整形
デザイン面は除き、モーダルウィンドウの表示非表示に関わる部分だけ記載します。
style.scss.p-modal { &[aria-hidden="true"] { visibility: hidden; opacity: 0; transition: .4s; } &[aria-hidden="false"] { visibility: visible; opacity: 1; transition: .4s; } }
aria-hidden
属性がtrue
(要するに非表示)の場合はvisibility: hidden;
で隠しています。
display: none;
でもいいのですが、transition
が効かなかったりするので今回はvisibility
を使用しました。
反対にaria-hidden
属性がfalse
(要するに表示)の場合はvisibility: visible;
で表示させ、
opacity
とtransition
でフェードインするような表示の仕方をさせています。実際にJavaScriptで表示非表示を制御してみる
さて、ここからが本題です。
細かい説明は、各所にコメントアウトで記載しています。modal.jsfunction modalWindow() { // ここで、HTML上にあるモーダル開閉用のクラスを全て取得 const modalOpenBtn = document.querySelectorAll('.js-modalOpen'); const modalCloseBtn = document.querySelectorAll('.js-modalClose'); // 取得したモーダルを開くボタン一個一個に対しforEachで処理 modalOpenBtn.forEach((elm) => { // ボタンがクリックされたら elm.addEventListener('click', () => { // ボタンに付与されているaria-controlsの値を取得し、それと同じidが付与されているモーダルウィンドウを取得する let targetId = elm.getAttribute('aria-controls'); let target = document.getElementById(targetId); // モーダルが非表示だった場合 if (target.getAttribute('aria-hidden') === 'true') { // ボタンのaria-expanded属性を変更し、対応するモーダルが開いたと設定する elm.setAttribute('aria-expanded', 'true'); // モーダルのaria-hidden属性を変更し、モーダルが開いたと設定する target.setAttribute('aria-hidden', 'false'); // 現在のスクロール位置を取得した後、bodyを固定させる const scrollY = window.scrollY; document.body.style.position = 'fixed'; document.body.style.top = -scrollY + 'px'; document.body.style.left = '0'; document.body.style.right = '0'; } }); }); // 取得したモーダルを閉じるボタン一個一個に対しforEachで処理 modalCloseBtn.forEach((elm) => { // ボタンがクリックされたら elm.addEventListener('click', () => { // ボタンに付与されているaria-controlsの値を取得し、それと同じidが付与されているモーダルウィンドウを取得する let targetId = elm.getAttribute('aria-controls'); let target = document.getElementById(targetId); // モーダルが表示されている場合 if (target.getAttribute('aria-hidden') === 'false') { // 開くボタンクリック時に設定されたbodyのtopを取得し、bodyの固定を解除 const scrollY = document.body.style.top; document.body.style.position = ''; document.body.style.top = ''; document.body.style.left = ''; document.body.style.right = ''; // スクロール位置をモーダルを開いた時と同じ位置に戻す window.scrollTo(0, parseInt(scrollY || '0') * -1); // ボタンのaria-expanded属性を変更し、対応するモーダルが閉じたと設定する elm.setAttribute('aria-expanded', 'false'); // モーダルのaria-hidden属性を変更し、モーダルが閉じたと設定する target.setAttribute('aria-hidden', 'true'); } }); }); }; modalWindow();固定する背景に色を付けたいとか、固定背景クリックでモーダルを閉じるようにしたい場合もあると思いますが、それに関してはまたの機会に…
さいごに
こういった構成にすることで、モーダルが何個に増えても簡単に対応でき、後の作業が楽になったかなと思います。
慣れたらもっとスマートなコードが書けるようになると思うので、その際はまた加筆修正行いたいと思います。読んでいただきありがとうございました!
- 投稿日:2021-03-02T15:31:25+09:00
divをクリックしてタイムピッカーを起動させたい
type="time"
<input type="time">input要素のtype属性にtimeを指定することで、入力される値は時間の形式になる。
このインプットをクリックするとタイムピッカーが起動する。
いろんなとこでタイムピッカーを使いたい
Windowsはさておき、Androidの方は直感的に操作できてけっこう便利。
という訳で<div>
とか、なにかしら要素をクリックしたときにタイムピッカーが起動して時刻が入力できるようにしたい。
もちろんJavaScriptで値も取得したい。作りたいもの
今回は
<div>
で作ったボタンをクリックするとタイムピッカーが起動して、入力した時刻がボタンに表示される。っていうのをつくる。
機能としては<input>
と変わらないけどデザインの自由度は<input>
よりも大きいはず。作成方法
jQueryとかは別として、どうやらタイムピッカーを
<input type="time">
以外で起動させる方法はなさそう。
最初は、何か要素をクリックしたらJavaScriptで強制的に<input>
をクリックさせてタイムピッカーを起動させようとしたもののWindowsの方で失敗。Androidの場合は<input>
をタップするとタイムピッカーが起動するけど、Windowsだと時計のアイコンをクリックしないといけないので、そこで躓いた。
いろいろ調べてみると、<input>
の中のアイコンは操作できるらしいので、<input>
をボタン内部いっぱいにして透明にして隠しておく方針に変更。
アイコンの操作は以下二つのサイトを参考にした。https://blog.tmyt.jp/entry/2020/06/24/012442
https://itokoba.com/archives/301ボタンを作成
ボタンとして
<div>
を設置。<input>
は中に入れて見えないように透明に。CSSはボタンぽくサクッと整える。html<div id="button"> <input type="time"> </div>css#button { width: 400px; height: 100px; border-radius: 20px; background-color: #2dbed5; box-shadow: 0 5px 10px #09262a; } /*クリック時の動き*/ #button:active { box-shadow: none; background-color: #2397aa; top: 5px; } input { height: 100%; width: 100%; border: none; opacity: 0; } /*どこをクリックしてもタイムピッカーが起動するようにする*/ input[type="time"]::-webkit-calendar-picker-indicator { margin: 0; padding: 0; height: 100%; width: 100%; }時刻を表示する
ボタンに時刻を表示するために
<span>
を追加。
JavaScriptで値を受け取って表示させれば完成。
このとき、ボタンや<input>
にもCSSを加えて、要素の重なりを指定しておかないと<input>
より<span>
が上になってしまい、<input>
がクリックできなくなるので注意。
以下のサイトを参考にした。html<div id="button"> <input type="time"> <span>00:00</span> <!-- 追加 --> </div>css/*cssに追加*/ #button { position: relative; z-index: 1; } input { position: absolute; z-index: 2; } span { font-size: 80px; user-select: none; position: absolute; z-index: 1; top: 50%; left: 50%; transform: translateY(-50%) translateX(-50%); -webkit-transform: translateY(-50%) translateX(-50%); }jslet input = document.querySelector('input'); let time = document.querySelector('span'); input.addEventListener("input", () => time.innerHTML = input.value, false);
- 投稿日:2021-03-02T15:08:30+09:00
jQueryで上からslideToggleでメッセージを出す
はじめに
slideToggleを使ってニュルっとメッセージを出す方法の記録です。
jQueryのコード
以下のコードでメッセージの出す、戻すを行う。
表示するメッセージを受け取った場合は空白を全て取り除く。
そしてslideToggleで表示し、5秒後にslideToggleで戻す。let $slideMsg = $('#js-slide-msg'); let msg = $slideMsg.text(); if(msg.replace(/\s+/g, "")){ $jsShowMsg.slideToggle().show(); setTimeout(function(){ $jsShowMsg.slideToggle().show(); }, 5000); }表示について
$msgに表示するメッセージが入っていれば表示させる。
<div id="js-slide-msg"> <?php if(!empty($msg) echo $msg; ?> </div>
- 投稿日:2021-03-02T14:33:09+09:00
intra-mart を利用したjsp画面でClandar付入力項目のonChange動作
個人用の技術メモ
そもそもこれを書く理由は、im:calendarと関連する入力項目にだけonChangeをかけると、calendarによる入力時は動いてくれないので、それの対応策。
まず、入力項目のonChange動作は通常通り実装(一応手入力もできるので)
//IE11前のIEバージョン if(window.attachEvent){ document.getElementById("id").attachEvent( 'onchange', function(){//処理内容function}) } else { //IE11,mozilla系(firefox,chromeなど)ブラウザ document.getElementById("id").addEventListener( 'change', function(){//処理内容function}) }で、im:calendarの属性onSelectを利用して、同じ内容を実装する
//IE11前のIEバージョン <im:calendar onSelect = "//処理内容function"/>めんどくさく感じますが、ほかのいい方法思いつかないので、これで。intra-martさん提供してる情報あまり詳細がないので困るわ。
因みに、calendarで日付を選択する瞬間、フォーカスアウト動作になるので、通常のフォーカスアウト動作を実装した場合、思うように動かないでしょう。
- 投稿日:2021-03-02T12:26:30+09:00
【JavaScript】submitボタンをonClick()でdisabledするとonSubmit()が実行されない件【二重送信防止】
問題
どうも。みるみっとです。
フロントエンドエンジニアをやっています。業務中にお問い合わせフォームを作っていたところ、
「できれば二重防止送信機能を作って欲しい」との要望が。単純に「送信ボタンをクリックした後にdisabledするかぁー」と思っていたのですが、
どうやらsubmitボタンのclickイベントでdisabledにするとonSubmitが実行されないということが分かりました。※ちなみにonSubmit内でdisabledにしてもダメでした。
別にonSubmitまでdisabledしなくても良いのに...(´ω`)
動作環境
OS: macOs 10.15.6
ブラウザ: Google Chrome ver88.0解決方法
disabledではなく、CSSの「pointer-events:none」を使うと上手くいきました。
pointer-events:noneにすることで、ポインターイベントが無効されます。
つまり、その要素のクリック・タッチが効かなくなります。
ユーザーにとっては実質disabledです。form.html<form name="myForm" action="" method="get" onSubmit="return submitForm()"> <p class="title">メッセージ</p> <textarea id="message"></textarea> <input type="submit" id="submit_pointer" value="送信" onClick="buttonDisabled()"> </form>form.js//submitボタンのクリックイベント function buttonDisabled(){ //pointer-eventsをnoneに $('#submit_pointer').css("pointer-events", "none"); //disabledしてるっぽい色に変更 $('#submit_pointer').css("background", "#636363"); } //フォームのsubmitイベント function submitForm(){ return true; }実装例
下記に、
①二重送信防止していない送信ボタン
②送信後にdisabledする送信ボタン
③送信後にpointer-events:noneする送信ボタン
の3パターンを実装した礼を載せておきます。disabledボタンはフォームが送信されず、
pointer-eventsボタンだとフォームが送信されることが分かると思います。
See the Pen
submitボタンをdisabledするとsubmitできない件 by mykz (@mykzreas)
on CodePen.
※フォームの送信に成功すると(onSubmitが動作すると)アラートが表示されるようにしています。
※ページ遷移を防ぐためonSubmitはreturn falseしています。終わりに
disabledするとonSubmitが動作しないのはいまいちピンと来ないですが、pointer-events:noneの方が動作的のも軽そうですね。
視覚的にdisabledっぽいボタンの色に変更するのもUX的にも良さそうです。
参考
- 投稿日:2021-03-02T11:56:21+09:00
【Django】JavaScriptで動的に作成されたformにcsrf_tokenを追加する
概要
htmlファイル内で簡単な処理を行った後、formを経由してviews側に情報を返す方法に3日ほど詰まったので、備忘録代わりに書き残しておきます。
参考サイト様
- How to properly append django csrf_token to form in inline javascript?
- 【Django】 csrf_tokenの仕組みとCSRF無効化・画面カスタマイズする方法
手法
formタグを利用した取得が基本となり、ここは公式のチュートリアルにも記載があります。(参照: はじめての Django アプリ作成、その 4)
ただ、単純にJavaScriptでform要素を追加してsubmitしようとすると、「csrf_tokenないよ」みたいなエラーを吐きます。そこで、csrf_tokenを手動で追加する必要があります。以下サンプルです。
htmlファイル<!--関数起動用のボタン--> <input type="button" value="集計" onclick="aggregate()">function aggregate() { //form作成、メソッドとリンク先の追加 let form = document.createElement('form'); form.method = "POST"; form.action = "{% url 'myapp:feedback' %}"; form.id = "aggregated-form"; //送信したい情報を格納したinput要素作成、type,valueの追加など let input_container = document.createElement('input'); input_container.type = 'hidden'; //入力フォームの非表示 input_container.name = 'aggregated-information'; //何かしらの処理を施した情報 let information_to_post = ..... //情報の追加 input_container.value = information_to_post; //csrf_tokenの追加手動 let csrf_element = document.createElement('input'); csrf_element.type = 'hidden'; csrf_element.name = 'csrfmiddlewaretoken'; csrf_element.value = '{{ csrf_token }}'; //csrf_tokenと送信したい情報の追加 form.appendChild(csrf_element); form.appendChild(input_container); //文書への追加 document.body.appendChild(form); //送信 form.submit(); }あとはurls.pyでfeedbackとviews.pyの紐付けを行い、返ってきた情報をrequest.POSTで取得すれば利用できます。
まとめ
csrf_tokenの正体がinput要素であると知ることができました。JSONとして受け取るなどの工夫を行えばもっと使い勝手が良さそうです。集合知に感謝。
- 投稿日:2021-03-02T11:08:02+09:00
railsアプリのデプロイエラーの原因が、gemのuglifierのバージョンに依るものだった話
railsアプリのデプロイがこけたので、その原因を追求してみた。
デプロイ時のエラーメッセージを読むと、以下の様なメッセージが頻出していた。
Caused by: V8::Error: SyntaxError: Unexpected token: name (jsの変数名) at js_error (<eval>:3623:12167) at croak (<eval>:3623:22038) at token_error (<eval>:3623:22175) at unexpected (<eval>:3623:22263) at semicolon (<eval>:3623:22781) at simple_statement (<eval>:3623:25959) at <eval>:3623:23747 at <eval>:3623:22954 at block_ (<eval>:3623:28083) at ctor.body (<eval>:3623:27686) at function_ (<eval>:3623:27782) at <eval>:3623:24469 at <eval>:3623:22954 at block_ (<eval>:3623:28083) at <eval>:3623:23857 at <eval>:3623:22954 at <eval>:3624:3759 at parse (<eval>:3624:3999) at parse (<eval>:3958:22) at uglifier (<eval>:4003:13)下記の記事で、jsで
let
を使用している場合var
に変更すると良い、というアドバイスがあったので、変更してみた結果、デプロイ成功。
どうやら、gemのuglifierのバージョンが足りず、ES6をサポートしていなかったため、letという新しい書き方を許容していなかったため、エラーが起きたらしい。
https://stackoverflow.com/questions/39221152/rails-5-heroku-deploy-error-execjsprogramerror-syntaxerror-unexpected-token尚、ES6の書き方を許容するのはUglifier 3.2.0以降からだそう。
- 投稿日:2021-03-02T10:50:28+09:00
【JavaScript 関数】substrメソッドの使い方
substr()
String.prototype.substr()
文字列の一部を、指定した位置から後方向指定した文字数だけ返す
//公式 str.substr(start[, length]) //サイト記事 var str = 文字列 str.substr( 開始位置, 文字数 )これが元になって調査
watch: { // 入力された文字列を監視する inputText: function() { var pos = this.inputText.indexOf(this.forbiddenText); // indexOf:指定された値が最初に現れたインデックスを返す if (pos >= 0) { alert(this.forbiddenText + "は、入力できません。"); // 入力文字列から禁止文字を削除する this.inputText = this.inputText.substr(0, pos); //str.substr( 開始位置, 文字数(indexOfででたインデックス) ) } } }参考
- 投稿日:2021-03-02T09:35:04+09:00
[js]繰り返し処理いろいろ
メモ帳に書いてたやつをQiitaに 其の1
- メモってるのをわりとそのまま
- 「○」とか「□」とかの記号部分は自分にとって分かりやすいメモの取り方だったのでそれはそのままにしてるのであしからず
- 好き勝手に付けていい=変更可能なやつのこと ※
変数
のことや- code関連は一言一句ちがったらいかんやつもあるので、個人的にこういう書き方が分かりやすかった
for文:
for ( let ◯=0; ◯< □; ◯++){ /*処理*/ }
↑超一般的なあれfor ( let ◯ of □){ /*処理*/ }
↑配列に使うfor ( let ◯ in △){ /*処理*/ }
↑オブジェクトに使うwhile文:while、do while:◎実行速度早い ⚠︎無限roopに陥りがち要注意
forEachとか:forEach 配列の先頭から順番に要素が渡されて実行される
各構文の簡単なまとめ
for文:一般的に繰り返し回数の指定に使われる@超一般的なあれ
→繰り返し回数が決まっている場合はfor文
→途中でループを抜けることが想定されるのであればfor文
for-of文:配列やMapや文字列使える、比較的新しい構文 ECMAScript2015
→イテラブルなオブジェクトに対するループ
for-in文:オブジェクトに使える、比較的新しい(以下同文)
while文:配列や値に条件を指定して処理を行いたい時
→繰り返しに配列や値に条件を指定したい場合はwhile文
→処理速度は基本的に最速≒パフォーマンス重視ならこれ
└→【!!】for文とwhile文は誤差程度の違いという計測結果・私見のものもある
forEach:配列の要素のみ
→関数コストが高いためたの繰り返し文と比較すると遅い
ループ処理を抜ける方法:<使用可:for文,while文>
continue; //処理を中断して条件式に戻る/スキップする
break; //処理中の文から抜けて次の文へ移行. 繰り返し終了 @for-inなど
for文書き方 速度について
JavaScriptのループはどれが一番高速なのか - Qiita
(forEachが比較対象に入ってない、while文も比較対象に入ってない)最速結論これ
for (let i=0; i<●; i=(i+1)|0){ /*処理 */ }
i++
対i = i+1
すべてのブラウザで、i = i+1 / i = i-1が最速
最速コードを++/--にしたコードは、約3倍遅い
|0
で型を認識
すべてのブラウザで、|0でtypedを施したコードが最速
使わない場合2〜3倍の差
for文
↓初期値; ↓条件式; ↓増減式
for ( let 〇=0; 〇< □; 〇++){ //□は配列名
/* 処理 */
}
↓初期値; ↓条件式; ↓増減式 (最速結論)
for (let 〇=0; 〇< □; 〇=(i+1)|0){ //□は配列名
/* 処理 */
}
for(let i=△; i—){/*処理*/}
という書き方もある
while文
const i=0; //←while外に条件式に使う実行回数の初期値を書かなアカン while ( i <10){ //←この例は 1〜10の繰り返し処理 /* 実行文 */ }
- 条件式がtrueである限り反復処理する
- └繰り返し回数が決まっていないループ処理に最適
- 最初から条件式がfalseなら何も実行せず終わる
- ⚠︎条件式に直接trueを記述すると無限ループに!ダメ絶対ダメ
- 実行速度が速いといわれてるが誤差範囲という見方もあり
//while : 1〜10の繰り返し処理 var i=0; while ( i <10) { console.log(i); i++; //or以下のような書き方 //conlole.log(i++); } //while : coutinueを使って処理をスキップ(条件式に戻る) var i=0; ┏━━━━━━━━━━━━━━━━┓ ↓ ┃ while ( i <10 ) { ┃ ⚠︎if文で偶数の時だけcontinue i++; ┃ 以降の処理を中断し条件式に戻る if(i %2===0) continue; ━┛ conlole.log(i++); //←結果的に奇数だけがconsole.logに表示 } //while : breakを使って繰り返し処理終了 var i=0; while ( i <10 ) { i++; if(i %2===0) break; ┓ conlole.log(i++); ┃//←結果的に最初の「1」だけ表示 } ┏━━━━━━━━━━━━━━━━━━━━━┛ ⚠︎if文で偶数の時だけbreak ↓ ⚠︎while文を途中で終了(繰り返し処理終了)、 次のコードに //次のコード//while - 配列の繰り返し処理 var arr=[‘a’,b’’,’c’], i=0, len=arr.length; //←配列, iとは, lenとは; while(i<len){ console.log(arr[i++]); } //while - イテレータの繰り返し処理 var mapIterator =new Map([[0,’a’],[1,’b’],[2,’c’]]).entries(); var iteratorResult; while (iteratorResult =mapIterator.next(), !iteratorResult.done){ console.log(eteratorResult.value); }for-of文
配列やMapで使う処理
for (let ● of □){ //□は配列名かMap /*処理*/ }
- 「for…of」は主に配列やMapで使える.「values」(=値そのもの) をとってくる
- 指定された配列の中身(各要素)を取り出しながら繰り返し処理
- ⚠︎順番通りではないので注意が必要
- 配列,文字列,マップ(Map),DOMなど
- イテラブルなオブジェクトに対するループ(オブジェクトには利用できない)
- 比較的新しい構文 ECMAScript2015
//例1 let 〇〇 =[10,20,30]; for(let e of 〇〇){ console.log(e); //結果 10,20,30 ←valuesを取得 } //例2 var arr=[0,1,2,3,4]; for(var val of arr){ console.log(val); // } console.log(Array.from(arr)); //配列として出力for-in文
オブジェクトで使う処理
for (let ● in □){ //□は配列名 /*処理*/ }
- 「for…in」は主にオブジェクトで使う.「プロパティ」 をとってくる
- 指定された配列のプロパティを順に取り出しながら繰り返し処理
- オブジェクト=名前と値で管理する
- オブジェクトに含まれるプロパティの数だけ繰り返す=条件式や変化式が不要
- 比較的新しい構文 ECMAScript2015
forEach文
□.forEach( v => /*1行なら*/ ); //アロー関数, 1行, {(大カッコ)}不要 □.forEach( v => { /*処理*/ }); //アロー関数, 1行で □.forEach( (v,i,a) => {//アロー関数で /*処理*/ }); □.forEach( function(val,idx,arr){ //functionで /*処理*/ });
- 配列の繰り返し処理にだけ使える構文
- 配列の要素としてオブジェクトを格納しているような配列操作も可
- breakやcontinueは使えない
- 類似メソッド:
- map:返り値がある=新たな配列として作成可
- forEach:返り値がない=新たな配列としては作れない
- 外側に予め空配列を作ってpushするなら可(map使ったらよか)
do while文
do{/*実行文*/}while(条件式);
- 最初から条件を満たさない場合falseでも初回の実行文が処理される
- ⚠︎条件式に直接trueを記述すると無限ループに!ダメ絶対ダメ
//例 const x=1000; do{ /*実行文*/ console.log(x); // } while (x < 1000);
- 投稿日:2021-03-02T00:30:11+09:00
addEventListenerが追加したDOM要素等の影響でうまく設定ができない時の対応
今回の経緯
今回fullcalendarというカレンダーライブラリーで日付部分にボタンを追加、そのボタンにクリックイベントを仕込む、という実装が必要であったが、ライブラリーが吐き出したDomに対して直接イベントを設定する必要があり少し変わった実装方法をしたのでそのメモ
const ele = document.querySelector('.fc-scroller-harness .fc-scroller .fc-col-header tbody'); ele.addEventListener('click', (e) => { const x = e.target; if (Array.from(document.querySelectorAll('.label-btn')).includes(x)) { // .label-btn をクリックした時にイベントクリックイベントが発火する } });これ結構便利です!!たまにこのようにして使いたいパターンが出てくるので忘れないようにこちらメモとして残しておこう。
おしまい
- 投稿日:2021-03-02T00:17:58+09:00
ScriptAutoRunner を使ってインプレスの恥ずかしい記事リンクを消しました
素晴らしい記事がありました。
Amazonの商品ページから、「人気のインディーズマンガ」を消したい(chrome) - Qiita
https://qiita.com/iteyan/items/78bcad46ae43616eeb0fコメント欄に書きましたが
Chrome の Adblock Plus という拡張つかって、とりあえず画像だけを消していました。が、画像しか選択できなくて不便でしたが、この仕組をつかえば記事タイトルもまるごと消せますね。
ということで、以前から気になっていた
窓の杜とか、PCWatchの肌色成分が激しい記事リンクを消してみました。
記事タイトルの一部文字で消すことができます。const pcWatch = () => { const excludeTextArray = [ 'バウヒュッテ', 'シリコンで人肌の', 'ノーフューチャー', 'ウィザードリィスキーマ', 'ナイトピットウォーク', '人間の肌のような', '攻殻機動隊の実写版', 'グラビア', 'あの太ももが', '恥じらい顔', '恋人たち', 'この綾波、大人だ', '写真のキモチ', ]; excludeTextArray.forEach((excludeText) => { targetTitles = document.querySelectorAll('span'); targetTitles.forEach((targetTitle) => { const title = targetTitle.getAttribute('title'); if (title) { // console.log('ScriptAutoRunner', title) if (title.includes(excludeText)) { console.log('ScriptAutoRunner', 'Hit', title) targetTitle.parentNode.remove(); } } }); }); }; setTimeout(pcWatch, 5000); setTimeout(pcWatch, 10000); setTimeout(pcWatch, 15000); setTimeout(pcWatch, 20000); setTimeout(pcWatch, 25000); setTimeout(pcWatch, 30000); setTimeout(pcWatch, 35000); setTimeout(pcWatch, 40000); setTimeout(pcWatch, 45000); setTimeout(pcWatch, 50000); setTimeout(pcWatch, 55000); setTimeout(pcWatch, 60000);とりあえず動くの対応で、パフォーマンスは後回しです。
これ、インプレス系のページだけで動くようにしたいですが、やり方わかんなかったです。
誰か組み方を洗練させていってみてください。
- 投稿日:2021-03-02T00:17:58+09:00
ScriptAutoRunner を使ってインプレスの肌色成分多め記事リンクを消しました
素晴らしい記事がありました。
Amazonの商品ページから、「人気のインディーズマンガ」を消したい(chrome) - Qiita
https://qiita.com/iteyan/items/78bcad46ae43616eeb0fコメント欄に書きましたが、いくつかのサイトで肌色成分多め画像は、Chrome の Adblock Plus という拡張つかって、とりあえず目立つものを個別に画像だけを消していました。Adblock Plus は全体的に広告が消えますし、個別の画像を指定することもできるからです。
別に広告業界に反対しているわけでもなんでもないのですが、肌色系の画像がちらつくのが嫌だから導入しています。
ですがこの Adblock Plus では個別には画像しか選択できなくて不便でした。
ScriptAutoRunner の仕組をつかえば記事タイトルもまるごと消せるのでありがたい。
ということで「Amazonのインディーズマンガ」は非表示にさせてもらうとともに、
以前から気になっていた、窓の杜とか、PCWatchとか、インプレス社サイトの肌色成分が多めの記事リンクを消してみました。画像は今までも消していたのですが、記事タイトル文字が残ってしまっていたので、そちらも ScriptAutoRunner によって文字指定して消すことができました。
const pcWatch = () => { const excludeTextArray = [ 'バウヒュッテ', 'シリコンで人肌の', 'ノーフューチャー', 'ウィザードリィスキーマ', 'ナイトピットウォーク', '人間の肌のような', '攻殻機動隊の実写版', 'グラビア', 'あの太ももが', '恥じらい顔', '恋人たち', 'この綾波、大人だ', '写真のキモチ', ]; excludeTextArray.forEach((excludeText) => { targetTitles = document.querySelectorAll('span,img'); targetTitles.forEach((targetTitle) => { const title = targetTitle.getAttribute('title'); if (title) { // console.log('ScriptAutoRunner', title) if (title.includes(excludeText)) { console.log('ScriptAutoRunner', 'Hit', title) targetTitle.parentNode.remove(); } } }); }); }; setTimeout(pcWatch, 5000); setTimeout(pcWatch, 10000); setTimeout(pcWatch, 15000); setTimeout(pcWatch, 20000); setTimeout(pcWatch, 25000); setTimeout(pcWatch, 30000); setTimeout(pcWatch, 35000); setTimeout(pcWatch, 40000); setTimeout(pcWatch, 45000); setTimeout(pcWatch, 50000); setTimeout(pcWatch, 55000); setTimeout(pcWatch, 60000);指定文字列が実際にあわせているので、なんだか卑猥感丸出しの指定ですが、これ、ターゲット記事リンクで俺が以前みたことがあるから出ているとかではないですよね。全く恥ずかしい。
こんな恥ずかしい文字列をプログラムコードを書いたのは、初めてかもしれません。やれやれです。プログラマとして肌色成分多めなお仕事はしたくないものですよ。
さて、とりあえず動くの対応で、パフォーマンスは後回ししています。リンク記事画像読み込みがけっこう遅延して実行されるみたいなので、単にsetTimeoutでぐるぐる回してみました。新しいのが読み込まれたらそれをイベントで拾って随時消すとか、そういうのやりたければ誰か書いてください。
また、これだとブラウザで見る全てのページに負担がかかってしまうので、URLがインプレス系のページだけで動くようにしたいですが、今の所まだ実装してないです。
ところで動作確認してたら、PCWatchとかでは肌色成分多めなリンク記事がバンバンでるんだけど、家電Watchでは全くでないから、いろいろ文句言われないように調整しているんですね。インプレスさん。なかなかロジックを苦労されているみたいで、w苦笑です。
そういう知りたくもない分岐処理ロジックとかを見抜いたりとか、ため息ついてしまいます。一生懸命、どこかのだれかのエンジニアが調整して「よーし、おじさん、PCWatchやAKIBA PC HOTLINE では、ターゲットが雄ばかりだから、メスフェロモン増加しちゃうぞー」的記事リンクを作成するようなコードとか設定を書いていたんですよね。やれやれ。そうすると記事自体に、エロフラグとか付帯しているってことですよね。ユーザーには見えないけれども、AIに判定させるためにとか。そういう仕事したくないなあ。
終わりに
ところでこれ応用したら、Qiitaページだけで、特定ユーザーの記事やコメントを消すってのも簡単にできそうです。応用範囲が広くて楽しみ。
追記:改良
こんな感じかな。特定サイト向けだから負荷高くてもいいだろうというのにしておきました。
const targetUrls = [ '.impress.co.jp', ]; if (targetUrls.some(url => location.href.includes(url))) { console.log('impress site !'); const notObsceneImpress = () => { const excludeTextArray = [ 'バウヒュッテ', 'シリコンで人肌の', 'ノーフューチャー', 'ウィザードリィスキーマ', 'ナイトピットウォーク', '人間の肌のような', '攻殻機動隊の実写版', 'グラビア', 'あの太ももが', '恥じらい顔', '恋人たち', 'この綾波、大人だ', '写真のキモチ', ]; excludeTextArray.forEach((excludeText) => { targetTitles = document.querySelectorAll('span,img'); targetTitles.forEach((targetTitle) => { const title = targetTitle.getAttribute('title'); if (title) { // console.log('ScriptAutoRunner', title) if (title.includes(excludeText)) { console.log('ScriptAutoRunner', 'Hit', title) targetTitle.parentNode.remove(); // targetTitle.style.display = 'none'; } } }); }); }; for (let i = 1; i < 60; i += 1) { setTimeout(notObsceneImpress, i * 1000) } }