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

ストリーム型の GIS データをLeaflet地図上に表示

こんにちは。 ストリーム型の GIS データ(下記)をLeaflet地図上に表示してみました。入手したサンプルソース(fetch利用で動作)へ手を加えて、ローカルファイル読み込みへ書き換えました。 flatgeobuf ←今回(こちらからサンプルソース、データファイルを入手) geojson stream しかしこれ(ローカルファイル読み込みへ書き換えたもの)の動作を試すと、ストリーム型の読み込み動作を示しませんでした(下記)。flatgeobuf や geojson stream 側には原因はないだろうと思いますが、原因不明です。 ソース&デモ動作はこちら: Leaflet map: read flatgeobuf files by dropping-on (bl.ocks.org)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

InDesign スクリプト 要素名に(構造の選択された属性の値を)

構造の選択された属性の値を要素名にするスクリプトは、これで良いのかな・・・? /* 構造の選択された属性の値を要素名にする 更新 2021/6/4 */ // アプリ指定 #target "indesign"; // スクリプト名 var scriptName = "要素名に(構造の選択された属性の値を)"; //スクリプト動作指定(一つのアンドゥ履歴にする及びアンドゥ名の指定) app.doScript(function () { // 選択されているオブジェクト selectObjects = app.activeDocument.selection; // 変更された要素名数 var changeElementNameNumber = 0; // 選択されているオブジェクトの数だけ繰り返す for(var i = 0; i < selectObjects.length; i++){ // 選択がXml属性の場合 if(selectObjects[i].constructor.name == "XMLAttribute"){ // 選択の親のXml要素のタグ名と属性の値が違う場合 if(selectObjects[i].parent.markupTag.name != selectObjects[i].value){ // 属性の値のXmlタグ名が存在しない場合 if(app.activeDocument.xmlTags.itemByName(selectObjects[i].value).isValid == false){ // エラー対策 try{ // 属性の値の名前のXmlタグを追加 app.activeDocument.xmlTags.add(selectObjects[i].value); // エラーの場合 }catch(e){ // 次の繰り返しへ continue; } } // Xml要素のタグに属性の値の名前のXmlタグを入れる selectObjects[i].parent.markupTag = app.activeDocument.xmlTags.itemByName(selectObjects[i].value); // 変更された要素名数を追加 changeElementNameNumber++; } } } // 結果表示 alert("変更数 " + changeElementNameNumber, scriptName); //スクリプト動作指定(一つのアンドゥ履歴にする及びアンドゥ名の指定)の続き }, ScriptLanguage.JAVASCRIPT, [scriptName], UndoModes.ENTIRE_SCRIPT, scriptName);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】export function とはなんぞや

はじめに 先に言っておくと「export function」なんてありません! この記事はただ自分の痴態を晒しているだけで、自分への戒めとして、あとは気軽にQiitaに投稿するきっかけになればいいなくらいの思いでこの記事を書き始めました。 きっかけ! jQueryは普段触っていますが、モダンと言われるような(今では当たり前?)ES2015以降のJavaScriptにしっかりと触れていなかったので触れたいと思い、とりあえずやってみれば出来るだろうと軽い気持ちでNext.jsのチュートリアルに手を出して模写してみました。 しかし、モダンな環境に慣れていない私にはfunctionは分かってもみなさんご存知のようにexport functionなんて早速書かれても、functionの頭になんでexportなんてついちゃったの!?!? ってな感じで困ってしまい、最初からわかんないのまま進むのは良くないなと思い、「export function」について調べ出しました。(この時までは本当に「export function」なんて構文があると思っていたんです...。) ググってみた! こんな内容なんてググればすぐに出てくるだろうと思い「js export function」で早速調べてみたのですが、見出しを見てみる限りJavaScriptのexportについての記事ばかりです。 勝手に検索結果に出てくると思っていた「export function」についてのサイトが出てこないので泣きそうになりました。しかし天下のGoogleを信じてサイト上位のサイトを見てみることに。 ・・・ ふむふむ...ふむ?...ん〜なるほど、よくわからんぞ!! ただ、分からないなりにも分かったこともありましたよ。 export functionとは!(まとめ) 「export function」なんて構文はない! 自分の思い込みで勝手に作った構文でした... 「export」と「function」は別々の構文で知らなかったことは「export」についてだった! 「export」で指定した処理を使うときは「import」で利用するよ! 「export」はモジュールやクラスとして扱うために使用されているみたい! モダンな環境に慣れていない人(自分)には「import」と「export」がわかるとNext.jsのソースが読み方がわかるようになった! 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DataView

DataView とは何か ArrayBuffer内のデータを読み書きするためのビュー。 (ArrayBuffer自身には読み書きする機能はないので、それを補完する立ち位置) これと類似する型付き配列(typed array)との違いは、「エンディアンをよしなにしてくれないので、バッファ内に書き込まれてるままのバイト順で取り扱う(エンディアンについては開発者側が責任を握る)」ということ。 用途 前述の通り、型付き配列(typed array)と違ってエンディアンを意識しながら触る用途向きなので、使う・見かけることが稀というか、DataViewの利用シーンも型付き配列(typed array)と比べて「(技術的に)濃い」めになりがち。 DataViewの本質的用途は以下。 ArrayBufferから、指定のエンディアンで(&指定の数値型として)値を読み出す ArrayBufferから、指定のエンディアンで(&指定の数値型として)値を書き込む なお、これらの応用として 型付き配列(typed array)と組み合わせることで、「自環境がどちらのエンディアンか」を判定する などの用途も考えられる。 参照 JavaScript 型付き配列 - JavaScript | MDN TypedArray - JavaScript | MDN Typed Arrays: ブラウザでバイナリデータを扱う - HTML5 Rocks
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

discord.jsでのUserクラスとGuildMemberクラスの違い

雑記用 そもそもdiscord.jsとは DiscordのBotをJavaScriptで動かすためのいい感じのライブラリ。これの説明は他の記事にぶん投げます クラス(Class)とは ものの説明書のことです。(ここより引用:https://techplay.jp/column/482) つまり、「ユーザー」をあらわすクラスと、「サーバーの人」をあらわすクラスがあるわけです。 違い Userクラス (詳細:https://discord.js.org/#/docs/main/stable/class/User) ユーザー(サーバー内外問わず)をあらわす。アカウント作成日時やユーザータグ(#1234など)が確認できる。 GuildMemberクラス (詳細:https://discord.js.org/#/docs/main/stable/class/GuildMember) サーバー内のユーザーをあらわす。サーバーの参加日時やClientUser(今プログラムでログインしているBotのこと)にそのユーザーがBAN可能かなどを確認できる。 変換方法 User → GuildMember guild.member(/*Userクラスの値*/) でできる。 たとえばコマンドの場合で、送信者をGuildMemberにする場合は message.guild.member(message.author); でできる。 GuildMember → User client.users.cache.get(/*GuildMemberクラスの値*/.id) でできます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

型付き配列(typed array)

型付き配列(typed array) とは何か ArrayBuffer内のデータを読み書きするためのビュー。 (ArrayBuffer自身には読み書きする機能はなく、このtyped arrayと別述のDataViewとがその役割を担うことになっている) これと類似したものとしてDataViewというのがあるが、こちらは「開発者側がバイナリのエンディアンを意識しながら触る必要がある」とかでもない限り使うことは稀で、だいたい型付き配列(typed array)の方を使う。 用途 (自身には読み書き機能を持たない)ArrayBufferに対して、データの読み書きを行う ArrayBuffer内のデータに対して、.forEach(), .map(), .lengthなどArrayライクな操作を行う ちなみに、.push()とか.pop()とか(一部Arrayにあるのに)ないやつもある。まぁあったところで、存在意義が謎なので「ぁ確かに」という感じ。 なお後述の通り、typed arrayにはいくつかの種類があるが、 それら違い(用途の使い分け)はそのバイナリ(ArrayBufferの中身)をどう解釈するか(i.e. そのバイナリのデータ単位が何バイトなのか、符号(±)はあり/なしか)であり、 「どう解釈すべきか」の答えは以下の順で探せばだいたい見つかると思われる 1. そのバイナリの生成側の仕様(書) 2. (もし1.がないなら)生成するコードを書いた人の脳内 3. 上記関係なく(ハック的な意味も含み)、あなたがそのバイナリをどう読み(解釈し)たいか typed arrayの種類 typed arrayには後述の表にまとめたとおり、いくつかの種類がある。 これらはその名前からどんなものか判別可能になっている <[Int|Uint|Float]><バイト数>Array - 数値の種類 - Int: 符号あり(±)整数 - 64ビットのときだけ"BigInt" - Uint: 符号なし(+のみ)整数 - 64ビットのときだけ"BigUint" - Float: 浮動小数点 - バイト数(8 〜 64バイト) - "Array": そのまま。「(〜な)配列」ってこと。 - 8ビットにだけ、別途"ClampedArray"というのがいる。後述の表の備考欄を参照のこと。 型 ± / +のみ Int / Float 数値のビット長(バイト長) 値の範囲 備考 Int8Array ± Int 8ビット(1バイト) -128 ~ 127 Uint8Array +のみ Int 8ビット(1バイト) 0 ~ 255 Uint8ClampedArray +のみ Int 8ビット(1バイト) 0 ~ 255 設定しようとした値が 0 〜 255 の範囲外だった場合、 0 または 255 が代わりに設定される(最小値0, 最大値255でキャップされる; Uint8Arrayの場合は、上位桁に繰り上がる) Int16Array ± Int 16ビット(2バイト) -32768 ~ 32767 Uint16Array +のみ Int 16ビット(2バイト) 0 ~ 65535 Int32Array ± Int 32ビット(4バイト) -2147483648 ~ 2147483647 Uint32Array +のみ Int 32ビット(4バイト) 0 ~ 4294967295 Float32Array ± Float 32ビット(4バイト) 1.2×10-38 ~ 3.4×1038 BigInt64Array ± Int 64ビット(8バイト) 5.0×10-324 ~ 1.8×10308 BigUint64Array +のみ Int 64ビット(8バイト) -263 ~ 263-1 Float64Array ± Float 64ビット(8バイト) 0 ~ 264-1 参照 JavaScript 型付き配列 - JavaScript | MDN TypedArray - JavaScript | MDN Typed Arrays: ブラウザでバイナリデータを扱う - HTML5 Rocks
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

即時関数、アロー関数

即時関数 即時関数とは、関数を定義すると同時に実行される構文です。関数を定義してから呼び出すと言う手間を省くことができます。 // 無名関数 const countNum = function(num){ console.log(num) } countNum(1) // 即時関数 (function countNum(num) { console.log(num) })(1) ()の中にfunctionからはじまる関数定義そのものを配置することで、その関数を即実行するということができるようになります。その結果、関数を呼び出すという手間が省けます。 アロー関数 アロー関数とは、functionの記述を省略し、その代わりに()=>という記述によって関数を定義する構文です。より短い記述で関数を定義できるというメリットがあります。 // 無名関数 const 変数名 = function(){  処理 } // アロー関数 const 変数名 = () => {  処理 } 以下のコードを記述して1と表示されていれば成功です。 const countNum = (num) => { console.log(num) } countNum(1) 関数定義の種類  特徴 関数宣言 標準的な関数の定義 無名関数 関数を多く使用するコードであるときに使用する。関数名の重複を避けることができる。 即時関数 流用する可能性のない関数を定義するときに使用する。別途関数を定義する手間がない。 アロー関数 無名関数または即時関数において、より省略したいときに使用する。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

jQueryでの要素の操作

記事の内容 この記事は「jQuery 本格入門」(著:沖林正紀 出版:技術評論社)のPart2 「プログラミング編」から学習した内容をメモ程度にまとめたものである。 内容の誤記、誤字等はご容赦いただきたい。また、JavaScriptの基本的な文法や、jQueryのマークアップ記法などを知らないと、当記事を理解するが難しいと思われる。 とは言っても、私もJavaScriptを本格的に学び始めて、4日目なので、やってみれば意外とわかったりするかもしれない。関数型プログラミングについての知識がある場合、JavaScriptのコールバック関数などの理解の助けになると思う。 jQueryオブジェクト jQueryと$ jQueryが組み込まれたブラウザ上のコンソールでは jQuery === $→true jQuery instanceof Object→true $ instanceof Object→true となる。 また typeof $→function と表示される。 このことから、 jQueryと$は同値であり、関数オブジェクトであることがわかる。 次に 'jQuery' in window→true '$' in window→true となることから、 jQueryと\$はともにwindowオブジェクトのプロパティであることがわかる。 \$をセレクタなどのために用いるjQueryであるが、先に組み込まれたプログラムによって、windowオブジェクトのプロパティである\$が定義されていることも考えられる。 そこでjQueryのnoConflictメソッドを使う。 window.$ = ...//$を定義する var jq = jQuery.noConflict(); //jQuery自身を返す。 jq(function(){... //jqがjQueryを表す。 引数をtrueにすることで、上書きされた\$の定義を復活させることができる。 異なるバージョンのjQuery共存させるときなどに使える。 一定の範囲で\$をjQueryとして使いたい場合は (function($){...})(jQuery); ※ここに記述された処理はすぐに実行される。 jQuery(function($){...}); ※HTML文書が読み込まれたら処理を実行。 このようにすると{...}の範囲で\$がjQueryを表すことになる。 $(...)の動作 jQueryオブジェクトは引数を伴って実行可能な関数である。 $(function) $('#xxx')などがそうだろう。 主に、要素の生成、指定、処理の実行に使われている。 第一引数がセレクタ(文字列)、DOMオブジェクト、jQueryオブジェクトの場合 →要素の指定が行われる。第二引数が指定されているとそれに従って、要素を限定していく。 第一引数がHTML文書の場合 →要素の生成が行われる。第二引数には、追加先の親オブジェクトなどが指定される。 第一引数が関数の場合 →Webブラウザの画面にページ全体が読み込まれたときに、その関数実行される。 これらは関数だが戻り値についてはどうだろうか。 コンソール上で以下を実行してみると $('*').constructor === jQuery→true $('*') instanceof jQuery→true であり、関数の戻り値として帰ってきているのは、 jQueryオブジェクトのインスタンスである。インスタンスのみにあるメソッドやプロパティは多く、それらによって、インスタンス独特に操作を可能にしている。 このインスタンスは配列のように、添え字で要素を指定したり、要素の追加、削除、並べ替えのようなメソッドが実行できる。これは決してjQueryインスタンスが配列オブジェクトなわけではなく、jQueryオブジェクトにそのような機能が備わっているのである、 $.parseHTML $.parseHTMLはHTMLタグを含む文字列の解析をする関数で、戻り値にHTMLタグや文字列を要素とする配列を返す。 これによって、\$(...)を用いて要素の生成を行うことができる。 var s = '<a href="#">こちら</a>にアクセス'; $($.parseHTML(s)).appendTo('body'); //<body>内にsの内容が追加される。 要素の集合と繰り返し処理について \$(...)で指定した要素に対してメソッドを実行すると、指定した要素すべてに対して、メソッドが実行される。これは指定した要素が一つの集合として管理され、それぞれの要素に対して、繰り返しで同じメソッドが行われているから。 指定した要素、集合からの取得。 toArray()→要素の集合を配列で取得 get([位置])→集合の特定の位置にある要素を参照。因数がない場合要素の集合を配列で取得 index([セレクタ/要素])→指定された要素が存在する位置を取得 集合に対して繰り返し処理をする each(関数) $.each(集合, 関数)→\$(...)で指定した/集合それぞれの要素それぞれに対して関数の処理を適用する 戻り値:関数の処理に用いた集合 引数:要素の位置とそれぞれの要素 関数内のthisキーワードも要素を表す 関数の戻り値が↓ false→繰り返しの途中でeachメソッドは終了 false以外→処理を継続 ※null,undefinedでも処理は継続する。 map(関数) map(集合, 関数)→\$で指定した要素/集合それぞれの要素を関数で処理した結果を新たな要素とする。 戻り値:関数処理によって新たに生成された配列 関数の引数:それぞれの要素とその位置 関数内のthisキーワードも要素を表す。 それぞれに\$(...)で指定した要素に対して行うメソッドと \$自体が持っているメソッドがあるが、 後者は、指定された要素に限らず、配列やオブジェクトに使用可能であり、前者は後者を簡潔に記述するために用いられる。 関数の引数で設定される関数をコールバック関数という。 配列やオブジェクトの操作 ・配列の中から必要な要素を探すメソッド $.inArray(値,配列[, 開始位置]) →引数の値が指定した配列内に存在するか探す。見つからなければ-1を返す。見つかった場合その位置を数値で返す。 $.grep(配列, 関数[, 反転]) →配列要素のうち、関数の処理結果がtrueとなるものを探す。第3引数がtrueの場合戻り値がtrue出ないものを探す。見つかった要素の集合を返す。 ・配列の要素を操作するメソッド $.merge(配列1, 配列2) →2つの配列の要素を統合する。重複した値もそのまま要素として残る。結果は第1引数の配列に反映され、その値を戻り値として返す。 $.makeArray(オブジェクト) →オブジェクトの集合と同じ要素を持つ配列を新たに生成する。DOMオブジェクトの集合と同じ配列を生成することで、配列を操作するメソッドが実行できるようになる。 $.unique(配列) →重複するDOMオブジェクトを削除し、同じ値は1つだけしか含まないようにする。結果は引数の配列に格納される。 ・オブジェクトに関するメソッド $.extend([ディープコピーするか,]オブジェクト1 [, オブジェクト2...]) →オブジェクトのプロパティを追加する。オブジェクト2以降のプロパティをオブジェクト1に追加する。 $.contains(要素1, 要素2) →要素2は要素1に含まれているか、DOMオブジェクト同士の包含関係を調べる。 オブジェクト型を判別 $.type(オブジェクト(※以下obj))→オブジェクトの方を表す文字列を取得 $.isNumeric(obj)→オブジェクトが数値として解釈できるか判別 $.isArray(obj) →引数が配列か判別 $.isEmptyObject(obj)→引数は空のオブジェクト(プロパティのないオブジェクト)か判別 $.isPlainObject(obj)→{...}で生成されたオブジェクトか判別 $.isFunction(obj)→関数か判別 $.isWindow(obj)→windowオブジェクトか判別 $.typeによってオブジェクトの型を判定するより、isXxxxxメソッドで判別したほうが可読性が高い。 ブラウザのサポート状況で処理を変える。 $.support →Webブラウザが必要な機能を備えているか判別するフラグを保持しているオブジェクト。参照するときは、.xxx(サポートを確認する機能名)でつないで記述する。 $.trim(文字列) →文字列前後の空白を削除する。バージョン2.xでは、trimメソッドを持たない環境で実行するとエラーとなることがある。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

無名関数

無名関数 無名関数は関数名なしで関数を定義することができます。より簡潔なコードが記述できると言うメリットがあります。 //関数宣言 function hello(){ console.log('hello') } //関数式(無名関数) const hello = function(){ console.log('hello') } 以下のコードを記述して、12と出力されていれば成功です。 const calc = function(num1,num2){ return num1*num2 } const num1 = 3 const num2 = 4 console.log(calc(num1,num2))
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SharedArrayBuffer

SharedArrayBuffer とは何か (Web Worker - メインプロセス間などの)スレッド間で参照を共有することが出来る版のArrayBuffer。 (通常のArrayBufferは他のスレッドに参照を渡したら、自スレッドからはアクセスできなくなる。一方、ShareArrayBufferは渡した後でもアクセスできる。) 使用上の注意点 セキュリティ Spectreの脆弱性の発見に伴い、SharedArrayBufferの「スレッド間で値を参照し合える」という特性が当該攻撃についてセキュリティリスクを孕むことが判明したため、現在ではcross-origin isolatedな状態でない限りブラウザのセキュリティ機構によってSharedArrayBufferの使用をブロックされるようになった。 (※ cross-origin isolated: ざっくり言うと、自サイトが<iframe>, <script>, <img>, XMLHttpRequest(XHR), etc. で他サイトから取得されれたり、したりするときの利用ポリシー(何を、どうするのがOK/NGか)を設計・設定・管理されている状態にであるということ。) 参照 SharedArrayBuffer updates in Android Chrome 88 and Desktop Chrome 91 - Chrome Developers 「Google Chrome 91」でSharedArrayBufferの制限が強化 ~COEP/COOPヘッダーの明示的な指定が必要に - 窓の杜 Security consideration: Multi-threading helps cache-based side channel attacks · Issue #1 · tc39/ecmascript_sharedmem · GitHub Cross-Origin Resource Policy (CORP) - HTTP | MDN マルチスレッド処理における排他処理 SharedArrayBufferも、マルチスレッド処理における類似の他例に漏れず、値の取り扱いには排他処理への考慮が必須となる。 (もし要らないとしたら、もしかしてそれArrayBufferで事足りる要件だったりしませんか?) JavaScriptにおける排他処理、つまり不可分操作は、Atomicsを使って実装することが出来る。 不可分操作とは マルチスレッドで1つの変数を共有している場合、その変数に両方のプロセスのアクセスタイミングが重なるとデータ整合性上のトラブルが発生し、バグの原因となる。 これを防ぐために、不可分操作を施し、他のスレッドの割り込みを禁止して、値の書き込みと読み出しの整合性(書き込み処理と読み出し処理の間に別スレッドによる別の書き込み処理が割り込んでいないこと)が確実に担保された区間を作ることが出来る、というもの。 参照 SharedArrayBuffer - JavaScript | MDN #atomic_操作で共有メモリを更新、同期する Atomics - JavaScript | MDN 参照 SharedArrayBuffer - JavaScript | MDN ecmascript_sharedmem/TUTORIAL.md at master · tc39/ecmascript_sharedmem · GitHub ArrayBuffer - JavaScript | MDN Typed Arrays: ブラウザでバイナリデータを扱う - HTML5 Rocks
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

関数式

関数式 関数式の場合は、function(引数) {}という無名の関数を変数に定義または、代入して関数を定義する、と言う記述になります。 変数 = function(引数) { //関数内の処理 } 関数宣言と関数式の違いについて 関数宣言と関数式では読み込まれるタイミングが異なります。 以下のコードを実行すると、Uncaught ReferenceError...といった形でエラーが表示されます。 hello() const hello = function() { console.log('hello') } 1行目の時点で関数helloは定義されていないため、エラーとなります。一方、以下のように関数宣言を用いた場合はエラー無く、helloと出力が確認できます。 hello() function hello(){ console.log('hello') } JavaScriptにおいては関数宣言は先に読み込まれるために、このような事象が発生します。一方で関数式であれば先に読み込まれることはありません。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

react-router-domでrouterが動かない場合はrouter以下をdivで囲む

動かない <Router> <ul> <li> <Link to="/">Home</Link> </li> <li> <Link to="/about">About</Link> </li> <li> <Link to="/dashboard">Dashboard</Link> </li> </ul> <hr /> {/* A <Switch> looks through all its children <Route> elements and renders the first one whose path matches the current URL. Use a <Switch> any time you have multiple routes, but you want only one of them to render at a time */} <Switch> <Route exact path="/"> <Home /> </Route> <Route path="/about"> <About /> </Route> <Route path="/dashboard"> <Dashboard /> </Route> </Switch> </Router> 動く <Router> <div> <ul> <li> <Link to="/">Home</Link> </li> <li> <Link to="/about">About</Link> </li> <li> <Link to="/dashboard">Dashboard</Link> </li> </ul> <hr /> {/* A <Switch> looks through all its children <Route> elements and renders the first one whose path matches the current URL. Use a <Switch> any time you have multiple routes, but you want only one of them to render at a time */} <Switch> <Route exact path="/"> <Home /> </Route> <Route path="/about"> <About /> </Route> <Route path="/dashboard"> <Dashboard /> </Route> </Switch> </div> </Router> routerの中が単一の要素でないと動かないらしい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【自分用メモ】ArrayBuffer

ArrayBuffer とは何か ざっくりいうと、「生のバイナリデータのバッファ(入れ物)」。 そしてこのArrayBuffer、なんと自身のデータを読み書きするメソッドがない。 ではどうやってデータをArrayBuffer読み書きするのかというと、「型付き配列(Typed Array)」や「DataView」といった、バッファからデータを読み書きする専用のクラスを介して行う。 (要は、JavaScriptではバイナリを保持する役割(「バッファ」)と読み書きする手段(「ビュー」)で実装を分け(役割分担し)ており、使う側が適宜それらを理解して効率的&高速に使っていこうね、ってこと。) ArrayBufferが(こいつ単体で)出来ること byteLengthプロパティから、データのバイト長を知る。 slice()メソッドで、部分コピーな新しいArrayBufferをつくる。 メソッドの引数に渡したりとかの、変数(値)としての取り回しができる。(そりゃな) 以上。 つまり、ArrayBuffer自体は本当に ただの「バイナリデータの塊」 でしかないということ。 なので、「(リトルエンディアン/ビッグエンディアンみたいなバイナリ特有のめんどくさいこと考慮しつつ/しないでよしなに)データの読み書きがしたいんですけどー」みたいなときはその用途専用のビューを使って行きましょうね、という感じ。 参照 ArrayBuffer - JavaScript | MDN Typed Arrays: ブラウザでバイナリデータを扱う - HTML5 Rocks
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ArrayBuffer

ArrayBuffer とは何か ざっくりいうと、「生のバイナリデータのバッファ(入れ物)」。 そしてこのArrayBuffer、なんと自身のデータを読み書きするメソッドがない。 ではどうやってデータをArrayBuffer読み書きするのかというと、「型付き配列(typed array)」や「DataView」といった、バッファからデータを読み書きする専用のクラスを介して行う。 (要は、JavaScriptではバイナリを保持する役割(「バッファ」)と読み書きする手段(「ビュー」)で実装を分け(役割分担し)ており、使う側が適宜それらを理解して効率的&高速に使っていこうね、ってこと。) ArrayBufferが(こいつ単体で)出来ること byteLengthプロパティから、データのバイト長を知る。 slice()メソッドで、部分コピーな新しいArrayBufferをつくる。 メソッドの引数に渡したりとかの、変数(値)としての取り回しができる。(そりゃな) 以上。 つまり、ArrayBuffer自体は本当に ただの「バイナリデータの塊」 でしかないということ。 なので、「(リトルエンディアン/ビッグエンディアンみたいなバイナリ特有のめんどくさいこと考慮しつつ/しないでよしなに)データの読み書きがしたいんですけどー」みたいなときはその用途専用のビューを使って行きましょうね、という感じ。 参照 ArrayBuffer - JavaScript | MDN Typed Arrays: ブラウザでバイナリデータを扱う - HTML5 Rocks
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LINE Messaging API SDK(Node.js)クックブック

はじめに 最近LINEを使って色々と作業したので、備忘録的にLINE Messaging API SDK(Node.js)の 使い方を記載します。 (TypeScriptで書いていますので、JavaScriptしか分からないという方はすみません。) 基本的には以下の公式ページを参考にすれば書けると思いますが、見てもよく分からんという方の参考になればいいなと思います。 LINE MessagingAPIリファレンス 準備 この章ではLINE MessagingAPIを使用するための準備をします。 準備として、Bot機能を使用し、おうむ返しBotを作ります。 環境構築 まずはじめに環境構築から始めます。 最初にexpressを使用して、最少構成のサーバを立てます。 mkdir <適当な名前> cd <適当な名前> npm init -y npm install express npm install -D typescript ts-node @types/express ngrok npx tsc --init touch index.ts index.ts import express from "express"; const app = express(); const port = process.env.PORT || 8080; // port番号を指定 app.post("/webhook", (req, res) => { res.send("準備中"); }); app.listen(port); console.log("listen on port " + port); ついでにpackage.jsonのscriptsに"start":"ts-node index.ts"を追加しましょう。 package.json { "scripts": { "start": "ts-node index.ts" }, "dependencies": { "express": "^4.17.1" }, "devDependencies": { "@types/express": "^4.17.12", "ngrok": "^4.0.1", "ts-node": "^10.0.0", "typescript": "^4.3.2" } } 最後に確認します。 npm start //別ターミナルを開き以下、コマンド実行 npx ngrok http 8080 // httpsのURLを取得して、以下のコマンドを実行 curl --request POST '<取得したURL>/webhook' 準備中と返信が返って来ればOKです。 LINE Messaging API SDK(Node.js)の準備 とりあえずinstallしましょう。 npm install @line/bot-sdk 次に以下のようにindex.tsを変更します。 import express from "express"; const app = express(); const port = process.env.PORT || 8080; // ~~~次からこれより上を省略します。 import * as line from "@line/bot-sdk"; const config = { channelAccessToken:<ボットのアクセストークン>, // コードにベタガキしない方がいいです。 channelSecret: <ボットのシークレットId>, // コードにベタガキしない方がいいです。 }; app.post( "/webhook", line.middleware(config), (req, res) => { res.status(200).send("ok"); } ); // ~~~次からこれより下を省略します。 app.listen(port); console.log("listen on port " + port); これで、以下のコマンドを実行してサーバを立てましょう。 npm start //ターミナルを新しく開いて npx ngrok http 8080 // 前回の処理が残っていて、8080ポートが使用できない場合は、前回の処理を止めてControl - cで終了して、再実行してください。 // 知識のある方はnodemonなどをinstallしてhotreload機能を使ってください。 LINE Developpersにログインしてngrokで取得したURL/webhookを入力して、検証を押して 成功が返ってくるか確かめてください。成功が返って来れば準備は完了です! おうむ返しBotの作成 Botに何か話しかけると同じ言葉を返すおうむ返しBotを作成します。 すでに先ほど立てたサーバとLINEとをwebhookで連携できることが確認したので、 あとは、受け取ったデータをどのように返すのかの処理を記述します。 LINEから送ってくる処理すべきリクエストの単位をeventと呼びます。(公式な呼び方かどうかは分かりません。) 1つのhttpリクエストに複数のeventがやってくる場合があるので、まずはeventのハンドリングを行います。 そしてそのeventがユーザが発したメッセージの場合だけ、おうむ返しを実行します。 実際のコードは以下です。 index.ts // ~~省略してます import * as line from "@line/bot-sdk"; import { TextMessage, WebhookEvent } from "@line/bot-sdk"; const config = { channelAccessToken:<ボットのアクセストークン>, channelSecret: <ボットのシークレットId>, }; /* 色々な処理を行うclientを準備する。 このclientに処理を命令することによって、Botがアクションを起こす。 */ const client = new line.Client(config); app.post("/webhook", line.middleware(config), (req, res, next) => { /* eventはbodyの中に格納されており Promise.allを使用して、複数のevent処理を行う。 */ Promise.all(req.body.events.map(handleEvent)) .then((result) => res.json(result)) .catch((err) => next(err)); }); const handleEvent = async (event: WebhookEvent) => { /* 色々なタイプのイベント毎に処理を振り分ける関数 */ if (event.type === "message" && event.message.type == "text") { /* メッセージが送信されてきた時の処理 */ const replyToken = event.replyToken const message = event.message.text; const response: TextMessage = { type: "text", text: message, }; await client.replyMessage(replyToken, response); return; } }; // ~~省略してます メッセージeventにはreplyTokenというものが設定されており、このreplyTokenを使用することによって 発言者に対して、返信することができます。 (event全てにreplyTokenがあるわけではなく、eventのタイプによってはreplyTokenがないものもあります。) 上記のコードで、おうむ返しのBotが出来上がったと思います。実際に試して確認してみてください。 ※ 上手くいかない人はnpm startとnpx ngrok http 8080をやり直して、LINE Developpersのwebhook設定をやり直してみてください。 クックブック ここからは、まとまった内容というよりは、ユースケースに合わせた内容を記載していきます。 困っている方の助けになればいいなと思います。 新規に公式Botアカウントがフォローされた時の処理 Botが新しいユーザにフォローされた時に、特別な処理を行いたい時があると思います。 Botが新しいユーザにフォローされたタイミングでもeventが発行され、webhookを通じて サーバが知ることができます。 index.ts const handleEvent = async (event: WebhookEvent) => { if (event.type === 'follow') { /* 友達登録された時の処理 */ const userId = event.source.userId; const profile = await client.getProfile(userId!); console.log(profile.displayName); return; } // ~~~他の処理 }; eventを処理するhandleEvent関数内で、event.typeがfollowのものを 検知して、フォローされた際のアクションを行うことができます。 userIdはevent.source.userIdから得ることができ、userIdからユーザの詳細情報の profileを得ることもできます。 クイックリプライを送る クイックリプライの詳細についてはリンク先を参照してください。 ざっくりいうとユーザが簡単に返信できるようにするための機能です。 ここでは、テキストメッセージにクイックリプライを設定する方法を示します。 以下は、おうむ返しを行うBotに「yes」,「no」とクイックリプライを設定しています。 クイックリプライは色々なアクションに対応しているのですが、今回はpostbackアクションというものを設定しています。 const handleEvent = async (event: WebhookEvent) => { if (event.type === "message" && event.message.type == "text") { /* メッセージが送信されてきた時の処理 */ const message = event.message.text; const quickReplys: QuickReplyItem[] = [ { type: "action", action: { type: "postback", label: "yes", data: '{"action":"yes"}', }, }, { type: "action", action: { type: "postback", label: "no", data: '{"action":"no"}', }, }, ]; const response: TextMessage = { type: "text", text: message, quickReply:{ items :quickReplys } }; await client.replyMessage(event.replyToken, response); return; } }; postbackアクションはかなり有用なアクションで、ボタンを押すとdataに記載されている情報とともに サーバに送信されます。 ※ postbackアクションのdataには好きな文字列を設定することができます。私的におすすめなのはJSON形式で書くとbackendで使いやすくなります。 postbackの受け取りについては次の節で説明します。 postbackアクションを受け取る postbackアクションを設定しているボタンなどがクリックされるとpostbackeventが発行されます。 その受け取り方について示します。 const handleEvent = async (event: WebhookEvent) => { /* postBackアクションが送られてきた時の処理 */ if (event.type === "postback") { const data = event.postback.data; const parseData = JSON.parse(data); console.log(parseData); return; } // ~~~他の処理は省略 } 他の処理と同様にevent.typeでeventをハンドリングします。 postbackアクションのtypeは、"postback"になります。 data自体は文字列型なのですが、JSON形式の文字列型に設定することで、parseして扱いやすくなります。 pushMessgeを送信する Botから特定のユーザに向けてメッセージを送信する。 この処理は送りたいuserのIDが分かっている場合に使用します。 pushMessage.ts import * as line from "@line/bot-sdk"; import { TextMessage } from "@line/bot-sdk"; const config = { channelAccessToken:<ボットのアクセストークン>, channelSecret: <ボットのシークレットId>, }; // ~~~ 次回以降省略します。 const client = new line.Client(config); const pushMessage = async (userId: string) => { // userIdを引数にとって、pushMessageを送信する関数 const message: TextMessage = { type: "text", text: "pushメッセージです。", }; await client.pushMessage(userId, message); }; pushMessage("U95b0ce9db0b4cb73ec243a56e8b78nou"); サーバなどは関係ないため、 npx ts-node pushMessage.ts で特定ユーザにメッセージを送ることができます。 broadCastMessageを送信する BotからBotと友達になっている全ユーザに向けてメッセージを送信します。 broadCastMessage.ts const broadCastMessage = async () => { const message: TextMessage = { type: "text", text: "broadCastメッセージです。全てのユーザに届きます。", }; await client.broadcast( message); }; broadCastMessage(); リッチメニューを設定する Botにリッチメニューを登録することで、ユーザがどのように使えば良いかや Botが提供する機能について、分かりやすくなります。 ここではリッチメニューの設定方法を示します。 まず、最初にリッチメニューの背景画像を用意します。 リッチメニューの背景画像は以下の要件をみたいしている必要があります。 画像フォーマット:JPEGまたはPNG 画像の幅サイズ(ピクセル):800以上、2500以下 画像の高さサイズ(ピクセル):250以上 画像のアスペクト比(幅/高さ):1.45以上 最大ファイルサイズ:1MB ※上記のように記載されていますが、私は画像サイズが横2500px,縦1686pxの画像でのみ設定したことがありますが、 1pxでもズレるとエラーになるため、エラーで進まない場合は、横2500px縦1686pxで画像を作成してみてください。 次に、リッチメニューの画像のどこを押せば何を行うなどの情報を作成するのですが、 その作成には、LINE Bot Designerで作成するのが簡単だと思います。 ※Designerの話はここでは詳しくやりません。また、別の機会に書きます。割と直感的に使えると思います。 LINE Bot Designerからリッチメニューの設定を行うと Designerの下の方からJSON形式の情報を取得できます。(やってみたらわかるはず。適当ですみません。) 画像とJSON形式の情報を作成したら以下のスクリプトに入力してください。 richMenu.ts import fs from 'fs'; import { RichMenu } from '@line/bot-sdk'; // LINE Designerを使用して作成 const richMenu: RichMenu = <JSONをここに貼り付け> const registRichMenu = async () => { const richMenuId = await client.createRichMenu(richMenu); await client.setRichMenuImage( richMenuId, fs.createReadStream('<リッチメニューの背景画像へのパスを入力>') ); await client.setDefaultRichMenu(richMenuId); }; registRichMenu(); npx ts-node richMenu.ts 上記を実行することによってリッチメニューが登録できます。 client.setDefaultRichMenuはデフォルトのリッチメニューを設定します。 ユーザ毎にリッチメニューを分けたい場合は、client.linkRichMenuToUser(<userId>,<richMenuId>) で設定することができます。 アクセストークンの検証(LINEログインとの連携) LINEログイン(LIFF,LINE ログイン SDK)を使用したアプリケーションとLINEBotを連携させたいという要望もあったりするかなと思います。 同じプロバイダーでLINEログインのアプリとBotを作成すると、同じユーザには同じuserIDが割り振られるので、簡単に連携することが可能です。 LINEでログインを行うアプリケーションでは、ログインが行われるとLINEからアクセストークンが配布されます。 そのアクセストークンをBotが動いているサーバに送ることによって、Botが連携してメッセージを送信することができるようになります。 詳しくはこちらをご覧ください。 この時のアクセストークンの検証を方法を記載します。 少し複雑になってしまいました。後で、ちゃんと修正します。 validation.ts import fetch from 'node-fetch'; import express from 'express'; const LOGIN_APP_ID = <LINEログインアプリケーションのID> const LINE_AUTH_API = 'https://api.line.me/oauth2/v2.1/'; const LINE_API = 'https://api.line.me/v2/'; interface ValidationSuccess { client_id: string; expires_in: number; scope: string; } interface ValidationError { error: string; error_description: string; } function isValidationError(arg: any): arg is ValidationError { return arg.error !== undefined; } export const accsessTokenValidation = async ( req: express.Request, res: express.Response, next: express.NextFunction ) => { try { const token = req.headers.authorization?.replace('Bearer ', ''); if (typeof token !== 'string') { throw new Error('バリデーションエラー'); } const res = await fetch(`${LINE_AUTH_API}verify?access_token=${token}`); const data = (await res.json()) as ValidationSuccess | ValidationError; if (isValidationError(data)) { // バリデーションエラー console.log(data.error); throw new Error('バリデーションエラー'); } if ( data.client_id === LOGIN_APP_ID) { // 認証成功 try { const res = await fetch(`${LINE_API}/profile`, { headers: { Authorization: `Bearer ${token}` }, }); const profile = (await res.json()) as { userId: string }; // userId取得後にheaderに記録、後の処理で使用する req.headers.userId = profile.userId; next(); } catch (e) { next(new Error('lineのユーザ情報の取得に失敗しました。')); } } else { throw new Error('認証エラー'); } } catch (e) { next(e); } }; accessTokenの検証が終了すると、accsessTokenからprofileを取得して、userIdを知ることで さまざまな処理ができるようになります。 index.ts app.post('/api',accsessTokenValidation,(req,res,next) => { const userId = req.headers.userId const message: TextMessage = { type: "text", text: "LINEログインと連携されてメッセージが送られました。", }; await client.pushMessage(userId, message); res.send("ok"); }) middlewareとして導入することで、好きなところでaccsessTokenの検証ができるようになります。 その後の処理でuserIdを使用してBotからメッセージを送っています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Gatsby.jsでマークダウン内の外部URLの画像もgatsby-imageで最適化したい!

はじめに こんにちは。いきものがかりのほっちが卒業することを発表しました。ファンとしては残念ですが、これからの活動を応援しております! さて、Gatsby.jsで画像の最適化を行うgatsby-imageを使用しています。 その際に、マークダウン中に挿入している画像が、外部URLの場合にも、gatsby-imageを適用する方法を見つけましたので共有します! これで行けます 以下を使用すると、外部URLの画像をダウンロードしてbuild時にstaticファイルとして配置してくれます https://www.gatsbyjs.com/plugins/gatsby-remark-images-anywhere/ まずはパッケージをインストールします。 $ npm install gatsby-remark-images-anywhere で設定に以下を追加します。 gatsby-config.js module.exports = { plugins: [ { resolve: `gatsby-transformer-remark`, options: { plugins: [ { resolve: `gatsby-remark-images-anywhere`, }, ], }, }, ] } あとはbuild等々すればOK! 実施前 ![sample](https://example.com/sample.jpg) ↓ <img src="https://example.com/sample.jpg" alt="sample" title="sample" /> 追加後 ![sample](https://example.com/sample.jpg) ↓ <img class="gria-image" src="/static/xxxxxx/aaaaa/181128.jpg" srcset=" /static/xxxxxx/bbbbb/181128.jpg 200w, /static/xxxxxx/ccccc/181128.jpg 400w, /static/xxxxxx/ddddd/181128.jpg 784w" title="sample" alt="sample" style=" position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; object-position: center center;" /> おわりに これでマークダウンで挿入した外部URLの画像もgatsby-imageによって最適化されて表示できます サイトのパフォーマンス改善にぜひ!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

関数定義する方法

関数定義 JavaScriptではfunctionを用いることで関数を定義することができます。注意点として引数がない場合にも()を記述する必要があります。 function 関数名(引数) { //処理 } 以下のコードをコンソールで記述し、実行。 function sayHello() { console.log("こんにちは") } sayHello() 以下のように表示されます。 VM2342:2 こんにちは undefined
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

フロントエンドだけで見積書・納品書・請求書を一括生成するWebアプリ

法人であれ個人事業主であれ、他社との取引においては帳票生成が欠かせません。その度にわざわざExcelを開いて方眼紙を作って、、、なんてことはエンジニアたるものやりたくないですよね。そこで、単一のjsonファイルから見積書・納品書・請求書を一括生成できるツールを作ってみることにしました。 デモサイト https://studio-mizutama.github.io/Quotation/ 心掛けたこと ビルド不要で動く サクッと作ってサクッと動かすことを目標にしたため、フレームワークやAltJSは使わずに素のJavaScriptだけで開発しました。 再利用可能 自社の情報をconfig/config.jsにまとめることで、他の人も再利用しやすいアプリになるように作りました。 config/config.js const company = { "name": "<Your company name>", "zipCode": "", "address": "", "tel": "", "logoPath": "./images/logo.svg", "sealPath": "./images/seal.svg", "bank": "**銀行", "branch": "**支店", "typeOfAccount": "普通", "accountNumber": "111-1111111", "accountHolder": "" }; export default company; テキストベースでデータを管理 テキストベース(今回はjsonファイル)でデータを管理することで、git等を使ってバージョン管理がしやすくなります。脱Excelの一番大きなメリットです。 jsonファイルフォーマット { "details": [ { "description": "項目1", "quantity": 1, "unit": "式", "price": 30000 }, { "description": "項目2", "quantity": 5, "unit": "式", "price": 10000 } ], "client": { "name": "株式会社クライアント", "title": "Webサイト構築" }, "dateAndNumbers": { "no": "2021-05-30-01", "quotationDate": "2021-05-30", "validUntil": "2021-06-10", "deliveryDue": "2021-06-30", "paymentMethod": "月末締め翌月末払い", "deliveryDate": "2021-06-30", "invoiceDate": "2021-06-30", "paymentDue": "2021-07-31" } } 技術的知見 ドラッグ&ドロップでファイル読み込み ダイアログから選択だけでなく、ドラッグ&ドロップでもファイルを読み込めるように実装しました。 index.html <h1 id="h1">jsonファイルを選択してください。</h1> <input type="file" id="input-file" accept="application/json"> main.js /** * @param {number} 1: 見積書 2:納品書 3:請求書 */ let type = 1; /** * @param {boolean} */ let display = false; const input = document.getElementById("input-file"); input.addEventListener("change", function() { result = input.files; main(result,type); }); document.addEventListener("DOMContentLoaded",() => { const dropArea = document.getElementById("drop-area"); dropArea.addEventListener("dragover",(e) => { e.preventDefault(); dropArea.classList.add("drag"); }); dropArea.addEventListener("dragleave",() => { dropArea.classList.remove("drag"); }); dropArea.addEventListener("drop",(e) => { e.preventDefault(); dropArea.classList.remove("drag"); result = e.dataTransfer.files; main(result,type); }); }); /** * メイン処理を実行する関数 * @param {FileList} result * @param {number} type */ const main = function(result,type){ display = true; const reader = new FileReader(); reader.readAsText(result[0]); reader.addEventListener("load", function() { const json = JSON.parse(reader.result); //略 input.style.display = "none"; }); } 印刷と同じ表示でプレビュー 印刷プレビューとほぼ同じ見た目で画面表示できるようにCSSを書きました。 index.html <div class="sheet" id="drop-area"> <div class="margin-container"></div> </div> style.css @page { size: A4; margin: 0; } @media print { body { width: 210mm; } html, body { height: 100vh; margin: 0 !important; padding: 0 !important; overflow: hidden; } } .sheet { width: 210mm; height: 296mm; page-break-after: auto; position: relative; } .margin-container { margin: 5mm; width: 200mm; position: absolute; top: 0; left: 0; } @media screen { body { background: #eee; } .sheet { background: white; box-shadow: 0 .5mm 2mm rgba(0,0,0,.3); margin: auto; } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

indesign スクリプト テキスト変数に置換(選択の検索結果と同じ名前の)

選択の検索結果と同じ名前のテキスト変数に置換するスクリプトはこれで良いのかな・・・? /* 選択の検索結果と同じ名前のテキスト変数に置換する 更新 2021/6/4 */ // アプリ指定 #target "indesign"; // スクリプト名 var scriptName = "テキスト変数に置換(選択の検索結果と同じ名前の)"; //スクリプト動作指定(一つのアンドゥ履歴にする、及び、アンドゥ名の指定) app.doScript(function () { // ダイアログ var dialogueFlg = confirm("「検索と置換」の正規表現の設定で検索して検索結果とテキスト変数名が一致する場合はテキスト変数に置換します", "", scriptName); // Noの場合 if (dialogueFlg == false) { // スクリプトを終了 exit(); } // 正規表現の検索プロパティのフラグ var findGrepPreferencesPropertyFlg = false; // 正規表現の検索のプロパティ名の数だけ変数に入れて繰り返す for(var findGrepPreferencesProperty in app.findGrepPreferences.properties){ // 正規表現の検索のプロパティ名が「parent」、「bulletChar」、「numberingRestartPolicies」では無い場合 if(findGrepPreferencesProperty != "parent" && findGrepPreferencesProperty != "bulletChar" && findGrepPreferencesProperty != "numberingRestartPolicies"){ // 正規表現の検索のプロパティの内容が「NothingEnum.NOTHING」、「」、「null」ではない場合 if(app.findGrepPreferences[findGrepPreferencesProperty] != NothingEnum.NOTHING && app.findGrepPreferences[findGrepPreferencesProperty] != "" && app.findGrepPreferences[findGrepPreferencesProperty] != null){ // 正規表現の検索プロパティのフラグにtrueを入れる findGrepPreferencesPropertyFlg = true; // 繰り返しを抜ける break; } } } // 正規表現の検索プロパティのフラグがfalseの場合 if(findGrepPreferencesPropertyFlg == false){ // 警告 alert("「検索と置換」の正規表現の検索に値がありません"); // スクリプトを終了 exit(); } // 置換数 var replaceNumber = 0; // 選択の数だけ繰り返す for (var i = 0; i < app.activeDocument.selection.length; i++){ // オブジェクトのメソッドにfindGrepがある場合 if (typeof(app.activeDocument.selection[i].findGrep) == "function"){ // 正規表現での検索結果の配列に検索結果を入れる var findGrepResultArray = app.activeDocument.selection[i].findGrep(); // 検索された場合 if(findGrepResultArray.length > 0){ // 検索結果の配列の数だけ繰り返す for(var ii = 0; ii < findGrepResultArray.length; ii++){ // 検索結果の名前のテキスト変数が存在する場合 if(app.activeDocument.textVariables.itemByName(findGrepResultArray[ii].contents).isValid == true){ // 検索結果の初めに検索結果の名前のテキスト変数を挿入 findGrepResultArray[ii].insertionPoints.firstItem().textVariableInstances.add().associatedTextVariable = app.activeDocument.textVariables.itemByName(findGrepResultArray[ii].contents); // 検索結果を削除 findGrepResultArray[ii].remove(); // 置換数を追加 replaceNumber++ } } } } } // 結果表示 alert("置換数 " + replaceNumber, scriptName); //スクリプト動作指定(一つのアンドゥ履歴にする及びアンドゥ名の指定)の続き }, ScriptLanguage.JAVASCRIPT, [scriptName], UndoModes.ENTIRE_SCRIPT, scriptName);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

100で割ってから100で掛けると元の数字に戻らないJSの数値の例

最近まで「JSの数値演算は誤差が...」的な話は自分には縁遠かったですが。。。 TL;DR; //左=元の数、右= 元の100で割ってから100掛けた結果(100以下の数の場合) [ 7, 7.000000000000001 ] [ 14, 14.000000000000002 ] [ 28, 28.000000000000004 ] [ 29, 28.999999999999996 ] [ 55, 55.00000000000001 ] [ 56, 56.00000000000001 ] [ 57, 56.99999999999999 ] [ 58, 57.99999999999999 ] 確認方法 // 100で割って100で掛けると少数になる数を抽出 [...Array(100).keys()] .map(x => x++) .map(x => [x, (x / 100) * 100]) .filter(x => !Number.isInteger(x[1])) .forEach(x => console.log(x)); 対策 元の数より大きい場合、小さいい場合、.000000000000001大きい場合、0.000000000000004小さい場合、等々まちまち。少数点への演算は、Math.floor() , Math.ceil()の内部でのみ行うくらいしか思いつかず。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React Final Form #02 Render Props を使って、かんたんにバリデーションを実装する

この記事を読むと幸せになれそうな人 Controlled Component (React.useState や Redux を使う方法)で大きめのフォームを構築する時、クライアントサイドでのバリデーションを実装したりすると、ボイラープレートコードが多すぎて辟易してしまうと思います。 そんな人には、この React Final Form がオススメかもしれません。 続き物です #01『かんたんにフォームを構築する』 #02『Render Props を使って、かんたんにバリデーションを実装する』 ←今ここ 0. emailアドレス欄を追加 まずは、前回に続いて、emailアドレスの入力欄を追加します。(バリデーションの題材としてはこっちの方が手っ取り早いので) pages/react-final-forms/index.tsx const waitMs = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); const initialValues = { name: "aa", + email: "" } as const; type ValuesType = typeof initialValues; const ReactFinalFormPage: NextPage = () => { type HandleSubmit = FormProps<ValuesType, ValuesType>["onSubmit"]; const handleSubmit: HandleSubmit = async (s) => { await waitMs(1000); window.alert(`JSON: ${JSON.stringify(s)}`); // 例) JSON: {"name":"山田太郎"} }; return ( <Form onSubmit={handleSubmit} initialValues={initialValues} render={({ handleSubmit }) => ( <form onSubmit={handleSubmit}> <Field name="name" component="input" /> + <Field name="email" component="input" /> <button>送信</button> </form> )} /> ); }; export default ReactFinalFormPage; 1. Fieldで、 Render Props を使って書き換え Fieldの機能はそのままで、 component Prop を使わずに、 Render Prop を使った書き方に変えてみましょう。後々バリデーションメッセージを表示したり、Material UI のような UI ライブラリを使う際ためには、おそらくこの書き方に変える必要があるでしょう。 pages/react-final-forms/index.tsx - <Field name="email" component="input" /> + <Field<string> name="email" render={({ input }) => <input {...input} />} /> <Field<string> ...> の部分は、わかりにくいですが、Fieldコンポーネントに、型引数として、stringを渡せているみたいです。こんな変なこともできるんですね。 Render Prop の引数は、Field Render Props 型になります。 関数の引数オブジェクトの input プロパティはオブジェクトになっています。基本的には、この内容を全て <input/> 要素に渡すことになっています。スプレッド構文を使ってまとめて渡しましょう。 2. バリデータの追加 それでは、お待ちかねのバリデーションの実装に移ります。 次の const 宣言をトップレベルに書きましょう。 pages/react-final-forms/index.tsx const validateEmail: FieldValidator<string> = (value): string[] | undefined => { if (!value || value.length === 0) return ["必須要素です"]; const emailRegexp = /^[a-zA-Z0-9_+-]+(.[a-zA-Z0-9_+-]+)*@([a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]*\.)+[a-zA-Z]{2,}$/; if (!emailRegexp.test(value)) return ["Eメールアドレスのフォーマットに適していません"]; return undefined; }; React Final Form では、Fieldに追加するバリデーション関数は FieldValidator<T> 型になります。 返り値としては、エラーがない時には undefined を返し、エラーの時には、エラーを表す何らかの値を返しましょう。 実は、async 関数もOKです。(今回は使いません) (今回は、string[]型の値に決めていますが、返り値の型は元々決まっていないので、自分で決めましょう。) メールアドレスの正規表現は下のページを参考にしています。 これを、Fieldのコンポーネントに実装していきましょう。 pages/react-final-forms/index.tsx <Field<string> name="email" validate={validateEmail} render={({ input, meta }) => ( <div> <input {...input} /> <span> {meta.error && meta.touched && meta.error.map((s: string) => <small>{s}</small>)} </span> </div> )} /> まず、validate Propに先ほどのバリデーション関数を渡します。 すると、Render Props の meta.error にバリデーション関数が返したエラーが入って渡されてきます。 meta.error && meta.touched && を付けていますが、これは、 error が undefined なら何も表示しない。 フィールドが、フォーカスを外されたことがない場合にも、何も表示しない。 meta.touched は、一度フォーカスされたあとフォーカスが外れたら true になるフラグ ことを表しています。 Field Render Props のドキュメントを見ると、他にも様々なフラグ(meta.xxx)があります。それらを使うと、エラーメッセージの表示の切り替えのタイミングを様々に変えたり、初期値から変更されたフィールドだけを強調したりすることもできます。 3. バリデーションが成功していない場合にボタンをdisabledにする ついでに、バリデーションが成功していない場合には、ボタンを disabled にしてしまいましょう。 こうしておかないと、入力値が間違っていても送信できてしまいますからね。 pages/react-final-forms/index.tsx <Form onSubmit={handleSubmit} initialValues={initialValues} - render={({ handleSubmit }) => ( + render={({ handleSubmit, valid }) => ( <form onSubmit={handleSubmit}> <Field name="name" component="input" /> <Field<string> name="email" validate={validateEmail} render={({ input, meta }) => ( <div> <input {...input} /> <span> {meta.error && meta.touched && meta.error.map((s: string) => <small>{s}</small>)} </span> </div> )} /> - <button>送信</button> + <button disabled={!valid}>送信</button> </form> )} /> Form の Render Props (ドキュメント : FormRenderProps ) には、valid Prop があります。中のフィールドのバリデーションが全て成功している時にこの値が true になります。 これによって、バリデーションエラーが出ているときに、ボタンを押せなくすることができました。 4. エラーメッセージをコンポーネント化 エラーメッセージの記述が長ったらしくなっていたので、コンポーネントに切り出して、記述を楽にします。 pages/react-final-forms/index.tsx <Field<string> name="email" validate={validateEmail} render={({ input, meta }) => ( <div> <input {...input} /> - <span> - {meta.error && - meta.touched && - meta.error.map((s: string) => <small>{s}</small>)} - </span> + <ErrorMsg hidden={meta.touched} msgs={meta.error}/> </div> )} /> コンポーネントの定義 type ErrorMsgProps = { hidden?: boolean; msgs: string[] }; const ErrorMsg: React.FC<ErrorMsgProps> = ({ hidden, msgs }) => ( <span>{msgs && hidden && msgs.map((s) => <small>{s}</small>)}</span> ); 5. 名前にもバリデーションをつける 名前にも、文字列が空のときにエラーを返すバリデータを追加します。 コンポーネント化したので、コードも短くてラクラクですね。 pages/react-final-forms/index.tsx const validateRequiredString: FieldValidator<string> = ( value ): string[] | undefined => { if (!value || value.length === 0) return ["必須要素です"]; return undefined; }; pages/react-final-forms/index.tsx <form onSubmit={handleSubmit}> - <Field name="name" component="input" /> + <Field<string> + name="name" + validate={validateRequiredString} + render={({ input, meta }) => ( + <div> + <input {...input} /> + <ErrorMsg hidden={meta.touched} msgs={meta.error} /> + </div> + )} + /> <Field<string> name="email" validate={validateEmail} render={({ input, meta }) => ( <div> <input {...input} /> <ErrorMsg hidden={meta.touched} msgs={meta.error} /> </div> )} /> <button disabled={!valid}>送信</button> </form> )} /> ); }; export default ReactFinalFormPage; まとめ いかがだったでしょうか? Filed の Render Props を使えば、一貫した方法で柔軟にフィールドを制御したり、バリデーションを実装したりできることがわかったと思います。 送信ボタンの活/不活も、同様のインターフェースで制御できましたね。 次回以降はまだ未定です。 コード全容 pages/react-final-forms/index.tsx import { FieldValidator } from "final-form"; import { NextPage } from "next"; import { Field, Form, FormProps } from "react-final-form"; const waitMs = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); const initialValues = { name: "aa", email: "", } as const; type ValuesType = typeof initialValues; const validateRequiredString: FieldValidator<string> = ( value ): string[] | undefined => { if (!value || value.length === 0) return ["必須要素です"]; return undefined; }; const validateEmail: FieldValidator<string> = (value): string[] | undefined => { if (!value || value.length === 0) return ["必須要素です"]; // from https://www.javadrive.jp/regex-basic/sample/index13.html#section1 const emailRegexp = /^[a-zA-Z0-9_+-]+(.[a-zA-Z0-9_+-]+)*@([a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]*\.)+[a-zA-Z]{2,}$/; if (!emailRegexp.test(value)) return ["Eメールアドレスのフォーマットに適していません"]; return undefined; }; type ErrorMsgProps = { hidden?: boolean; msgs: string[] }; const ErrorMsg: React.FC<ErrorMsgProps> = ({ hidden, msgs }) => ( <span>{msgs && hidden && msgs.map((s) => <small>{s}</small>)}</span> ); const ReactFinalFormPage: NextPage = () => { type HandleSubmit = FormProps<ValuesType, ValuesType>["onSubmit"]; const handleSubmit: HandleSubmit = async (s) => { await waitMs(1000); window.alert(`JSON: ${JSON.stringify(s)}`); }; return ( <Form onSubmit={handleSubmit} initialValues={initialValues} render={({ handleSubmit, valid }) => ( <form onSubmit={handleSubmit}> <Field<string> name="name" validate={validateRequiredString} render={({ input, meta }) => ( <div> <input {...input} /> <ErrorMsg hidden={meta.touched} msgs={meta.error} /> </div> )} /> <Field<string> name="email" validate={validateEmail} render={({ input, meta }) => ( <div> <input {...input} /> <ErrorMsg hidden={meta.touched} msgs={meta.error} /> </div> )} /> <button disabled={!valid}>送信</button> </form> )} /> ); }; export default ReactFinalFormPage; See also: Final Form と、そのReactバインディングである React Final Form のドキュメントです この動画でのライブコーディングも、大いに参考になると思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JSXは<script type="text/babel">で読み込む!!

やりたいこと Reactだけを使ってHello, World!を出力したかった。 参考サイト:https://sbfl.net/blog/2019/02/20/react-only-tutorial/ 起こったこと JSXで文法はあってるのにUncaught SyntaxError: Unexpected token '<'エラーが起きて詰んだ。 前提条件 ReactとbabelをCDNで読み込んでいる。 環境 react: v16.14.0 react-dom: v16.14.0 babel: v6.26.0 サーバー:Live Server v5.6.1(VScodeの拡張機能) 問題のCode HTML <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>孤独ったー</title> <!-- ReactをCDNで呼び出す --> <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <!-- 自分で記述するスクリプト --> <script type="text/javascript" src="main.jsx"></script> </head> <body> <!-- React の描画対象を準備する --> <div id="app"></div> </body> </html> JSX function App() { return <div>Hello React!</div>; } const target = document.querySelector('#app'); ReactDOM.render(<App/>, target); 解決策 HTMLの自分で記述するスクリプトのtypeを間違って指定していた。 誤:<script type="text/javascript"> 正:<script type="text/babel"> 最後に そりゃtype間違えば正しく動かないのは理解できたけど。。。 エラー箇所と全く違うところをエラーポイントとして怒らないでほしいな!!!!!!!!!(怒) 参考サイトなど 正真正銘ぼReactだけの不純物なしでReact入門 https://sbfl.net/blog/2019/02/20/react-only-tutorial/ Unexpected tokenとは?Unexpected tokenの対処法を紹介! https://www.fenet.jp/dotnet/column/environment/7497/#i-2
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

当たり前だけどJSXは<script type="text/babel">で読み込む!!

やりたいこと Reactだけを使ってHello, World!を出力したかった。 参考サイト:https://sbfl.net/blog/2019/02/20/react-only-tutorial/ 起こったこと JSXで文法はあってるのにUncaught SyntaxError: Unexpected token '<'エラーが起きて詰んだ。 前提条件 ReactとbabelをCDNで読み込んでいる。 環境 react: v16.14.0 react-dom: v16.14.0 babel: v6.26.0 サーバー:Live Server v5.6.1(VScodeの拡張機能) 問題のCode HTML <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>孤独ったー</title> <!-- ReactをCDNで呼び出す --> <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <!-- 自分で記述するスクリプト --> <script type="text/javascript" src="main.jsx"></script> </head> <body> <!-- React の描画対象を準備する --> <div id="app"></div> </body> </html> JSX function App() { return <div>Hello React!</div>; } const target = document.querySelector('#app'); ReactDOM.render(<App/>, target); 解決策 HTMLの自分で記述するスクリプトのtypeを間違って指定していた。 誤:<script type="text/javascript"> 正:<script type="text/babel"> なぜこのような事が起こるのか 私がまだReactとJSXの仕組みをよくわかってないことが原因なんですけど、つまりは以下のようになってるからなんですね〜 JSXがレンダリングされる仕組み [JSX]→[Babel]→[Javascript]→[HTML] JSXがBabelによってJavascriptに翻訳される 翻訳されたJavascriptがHTMLによって読み込まれる 表示される こういう事だったんですね。 最後に そりゃtype間違えば正しく動かないのは理解できたけど。。。 エラー箇所と全く違うところをエラーポイントとして怒らないでほしいな!!!!!!!!!(怒) 参考サイトなど
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Javascript】Strings(1)学習ノート

初めに Stringについて学習した内容のoutput用記事です。 ※内容に間違いなどがある場合はご指摘をよろしくお願いします。 ※こちらの記事はあくまでも個人で学習した内容のoutputとしての記事になります。 文字列から文字を取得 文字列から単一の文字を取得するには、配列と同じように[]の中に添字を入れます。左から順番になっていて、0番から始まります。 const train = "Yamanotesen"; console.log(train[0]); //Y console.log(train[1]); //a console.log(train[2]); //m stringのメソッド 文字列の長さはlengthメソッドで求めることができます。 console.log(train.length); //11 文字列の特定の文字が何番目にあるのか調べるには、indexOf()メソッドを使います。0から順番に左から数えていきます。 const railwayCompany = "Japan Railways Group"; console.log(railwayCompany.indexOf("R")); //6 これと同じように何番目に文字があるのか調べるlastIndexOf()メソッドがあります。indexOf(0)メソッドと違う点は調べる文字が複数あった場合に、一番最後にある文字の位置を表示するところです。 "Japan Railways Group"にはaが3つあり、その最後のaの位置が分かります。 console.log(railwayCompany.lastIndexOf("a")); //11 探す文字列がある場合にはその文字列の位置を左から順番に数えて表示してくれます。小文字と大文字を区別するため、下記のように大文字のRではなく小文字のrにしてindexOfメソッドを使った場合-1と表示されます。 これは小文字のrで始まるrailswaysは見つからないからです。 console.log(railwayCompany.indexOf("Railways")); //6 console.log(railwayCompany.indexOf("railways")); //-1 文字列のstartする位置を決めるにはsliceメソッドを使います。左から0なので、6を入れれば7番目から文字列が出力されます。 console.log(railwayCompany.slice(6)); //Railways Group sliceメソッドは文字列の終わる位置も指定できます。 console.log(railwayCompany.slice(6,14)); //Railways indexOfメソッドとlastIndexOfメソッドと組み合わせれば、"Japan Railways Group"からJapanとGroupだけを抽出することができます。 console.log(railwayCompany.slice(0, railwayCompany.indexOf(" "))); //Japan console.log(railwayCompany.slice(railwayCompany.lastIndexOf(" ") + 1)); //Group 参考サイト https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/slice 3
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WordPressのContact Form 7を使いAPI経由でフォームと送信する

CMSサービス、フォームメールのバックエンドサービスが出始めていますが、クライアントの都合でなかなかそういったサービスを使えないこともあります。 Wordpressを使いつつ、HeadlessCMSとして使う想定の中でフォーム送信機能もWordpress側で持たせてあげたい、という要件はきっとある(はず)。 ※ Wordpress全般に言えることですが、Contact Form7はちょこちょこ脆弱性が見つかって攻撃されているなどの問題もあります。そういうところも含めてWordPressでの運用を愛せる方が自己責任で行ってください なんでこの記事をかいたのか Contact Form7のAPIを叩く記事はQiitaにもあったが、記述が片手落ちでそのとおりにやってもうまくいかなかった。 よりシンプルに十全な記事を書くことを目的としています。 参考にしたURL 前提 WordpressにContact Form7が入っている 実装サンプルはReact typescript fetchですがなんでも URL エンドポイント Contact Form 7のエンドポイントはこちらになります。 https://<サイトのURL>/wp-json/contact-form-7/v1/contact-forms//feedback FORM_IDには、フォームのショートコードに書かれているIDを入れてあげます [contact-form-7 id="<この数字>" title="コンタクトフォーム"] <form onSubmit={this.handleSubmit}> <input type="text" placeholder="" name="your-name" value={this.state.name} onChange={this.handleChange} /> </form> Reactの場合、formのonSubmitのイベントで処理するとかんたんです。 name属性には、Contact Form7のフォームに指定したnameを入れてあげましょう const API_URL = 'https://<サイトのURL>/wp-json/contact-form-7/v1/contact-forms/<FORM_ID>/feedback' handleSubmit = async (event) => { event.preventDefault(); const formElement = event.target; const body = new FormData(formElement); const res = await fetch(API_URL, { method: 'POST', body: body, }) } あとはサーバーサイドのバリデーション結果や送信完了はレスポンスで返ってきますので、そのあたりをうまく処理してあげたら完了です
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel:部分的に使用しているreactにview変数を受け渡す方法

Laravel:部分的に使用しているreactにview変数を受け渡す方法 構成 基本はbladeを使用し、reactは特定画面の指定div要素内のみで使用 目的 controllerから渡されたviewの変数をreactで使用したい 対応方法 javascriptのglobal変数として渡すことも可能だが、今回はdata属性にパラメータを指定して受け渡す。 受け渡し変数 HogeController.php $fuga = json_encode([ ['key' => 'key1', 'value' => 'hoge1'], ['key' => 'key2', 'value' => 'hoge2'], ['key' => 'key3', 'value' => 'hoge3'], ]); return view('home', compact('fuga')); hoge.blade.php <!-- idおよびblade部分は任意の文字列--> <div id='react' data-blade={{$fuga}}/> app.tsx componentDidMount() { const element = document.getElementById('react'); if (element && element.dataset.blade) { const object = JSON.parse(element.dataset.blade); console.log(object); } } 所感 javascriptのglobal変数に持たせるやり方もありますが、個人的にはこちらのほうが管理しやすいと思います。 vueのv-bindは便利ですね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

moment.jsからdayjsに移行:曜日の表示

日本語の曜日表示の作業メモです 移行前 updateLocaleでlocaleをjaに更新 updateLocaleでlocaleの曜日表示を設定(ここがjaのデフォルト設定と同じなので、実は要らなかった) import moment from 'moment'; // momentの言語設定 moment.updateLocale('ja', { weekdays: ['日曜日', '月曜日', '火曜日', '水曜日', '木曜日', '金曜日', '土曜日'], weekdaysShort: ['日', '月', '火', '水', '木', '金', '土'], }); 移行後 dayjs/locale/jaのインポートが必須 dayjsのプラグインを特にnpm install必要がない ロケールjaで十分なのでupdateLocaleしない import dayjs from 'dayjs'; import 'dayjs/locale/ja'; dayjs.locale('ja'); 動作確認 console.log(dayjs().format('YYYY/MM/DD(ddd) HH:mm')); // 2021/06/04(金) 08:58
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.js入門の方はこちら

タイトルにある通りVue.js入門ということで、今からVue.jsを学び始める方向けに、実際にVue.jsを使うとどんなことができるのかを掻い摘んで解説していきます。 今回は「Vueの基本的な使い方」、「Vue.jsの可能性を広げるディレクティブ」の二本構成で解説に入りたいと思います。 Vue.js導入 ここではVue.jsを扱う上で最低限必要な記述を説明しています。 <!-- 2の処理 --> <div id="app"> {{ hello }} </div> <!-- 1の処理 --> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script> <!-- 3の処理 --> <script> new Vue({ el: "#app", data: { hello: "こんにちは" } }) </script> 基本的な導入の流れです。 VueのCDNを読み込みます。 id="app"のdivタグ内がVue.jsの適用範囲になります。そしてマスタッシュ記号 {{ }} 内のhelloには、Vueインスタンスのdataで登録されたhelloの値を取得します。 Vueのインスタンスを生成します。elはVue.jsの適用範囲とするidを指定します。dataにはhtmlと連携するデータをオブジェクトに格納できます。 この場合ページには「こんにちは」と表示されます。 ディレクティブ ディレクティブとは、接頭辞 v- が付いたVue.jsの特別な属性のことを指します。 ディレクティブを設定することで様々な機能を追加できます。 今回は主要なディレクティブを厳選して実例を交えながら解説していきます。 v-on イベント処理 ここではv-onを利用してボタンを押すとカウントアップされるような機能を実装していきます。 <div id="app"> <p>{{ number }}</p> <button v-on:click="countUp">カウントアップ</button> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script> <script> new Vue({ el: "#app", data: { number: 0, }, methods: { countUp: function() { this.number = this.number + 1 } } }) </script> v-onを利用すると様々なイベントを設定ことができます。 今回はボタンを押してカウントアップさせる機能なので、クリックイベントを使用します。 v-on:click="countUp"をbuttonタグの属性として追加することで、Vueインスタンスに登録されたcountUpメソッドを実行します。 Vueインスタンス側にcountUpメソッドを登録するには、methodのオブジェクト内に定義してあげる必要があります。 countUpメソッドの処理の内容としては、this.numberで現在のnumberの値を取得できるので、ここでは現在の値に1を足し合わせています。 また、v-onは省略できてv-on:click="countUp"を:click="countUp"と書くこともできます。 v-if 条件分岐 ここではv-ifを利用して条件分岐の処理を実装します。 <div id="app"> <p>{{ number }}</p> <button v-on:click="countUp">カウントアップ</button> <p v-if="number">trueだと表示されて、falseだと非表示になります。</p> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script> <script> new Vue({ el: "#app", data: { number: 0, }, methods: { countUp: function() { this.number = this.number + 1 } } }) </script> v-ifの値にはnumberの値をブーリアン形式で取得します。 初期値は0になっているので、この場合はfalseを値として返すため<p v-if="number">trueだと表示されて、falseだと非表示になります。</p>は非表示になります。 numberがカウントアップされるとtrueを値として返すため<p v-if="number">trueだと表示されて、falseだと非表示になります。</p>は表示にされます。 v-for 繰り返し処理 ここではv-forを利用してリストを表示します。 <div id="app"> <ul> <li v-for="pref in prefs">{{ pref.name }}</li> </ul> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script> <script> new Vue({ el: "#app", data: { prefs: [ { name: "北海道" }, { name: "青森" }, { name: "宮城" }, { name: "秋田" }, { name: "岩手" }, { name: "山形" }, { name: "福島" }, ], } }) </script> v-forは繰り返したい要素に設定します。今回はリストを表示したいので、liタグに属性を設定しましょう。 また予めprefsに配列でデータを格納してあげます。 v-for="pref in prefs"の処理ですが、prefsは定義されている値の数だけ繰り返し処理が実行されます。そしてprefには繰り返し一回毎の値が入っています。 マスタッシュ記号 {{ }} 内にはpref.nameとして都道府県の名前を表示してあげます。 v-bind HTML属性との連携 ここではv-bindを利用してhtml属性とVueのデータを紐付ける処理を実行します。 Vue側からhtml属性に値を渡してあげます。 <style> .red { color: red; } </style> <div id="app"> <p v-bind:class="color">クラス名をデータ側から指定できます</p> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script> <script> new Vue({ el: "#app", data: { color: "red" } }) </script> v-bindを利用するとVueインスタンスのデータとhtmlの属性を連動できます。 v-bind:class="color"とすると、Vueインスタンスのcolorの値とpタグのクラス属性の値が連動します。 したがってpタグのclass属性にredが追加され、テキストが赤色になります。 ここまで学習してきた流れでいくとv-bind:class="{{ color }}"と書けばVueインスタンスのcolorの値が取得できそうですが、この記述ではエラーになってしまいます。 なぜかというと、マスタッシュ記号 {{ }} は属性の値として使用できないとVueのルールで定められているからです。 また、v-bindは省略できてv-bind:class="color"を:class="color"と書くこともできます。 v-model 双方向バインド ここではv-modelを利用してhtmlとVueのデータの双方向の連携を実現します。 <style> .red { color: red; } .blue { color: blue; } .green { color: green; } </style> <div id="app"> <p v-bind:class="color">データの値をhtml側から操作できます</p> <input type="text" v-model="color"> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script> <script> new Vue({ el: "#app", data: { color: "" // ブランクにしておきます } }) </script> v-modelはhtmlとVueのデータの双方向の連携が実現できます。このディレクティブはVueの醍醐味と言っても過言ではありません。 主にformのinput要素などで使われます。 inputタグにv-model="color"属性を設定すると、input要素に入力したテキストの値をVueインスタンスのcolorの値として操作することができます。 inputタグにredを入力するとVueインスタンスのcolorの値がredになり、pタグのクラスにredが渡されテキストが赤色に変化します。 以上になります。 今回は主要なディレクティブを厳選して紹介しましたが、他にも様々なディレクティブやVue.js独自に用意されたタグなどがありますので、この際に勉強してみてはいかがでしょうか。 それではドロンとさせていただきます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Tips: Qiitaに書いている文字数が知りたい

この記事は 3000文字Tipsイベントの参加記事です。 テーマは「3000文字Tips - 知ると便利なTipsをみんなへ届けよう」です。今まで記事投稿をしてきた人もしてこなかった人も、知っておくと便利なTipsをこの機会に投稿してみてください。 3000文字!シンプルでいいですね。 いろいろな知見は持っているけれど、記事などの投稿はしたことがなかった方 記事投稿にハードルを感じていた方 記事、真面目に書こうとするとなかなかハードルありますよね。でもそんなこと無いよという趣旨だと思う。弊社の各位にも早速紹介しました。 以下のような記事を期待しています。 記事の文字数が3000文字以下 Tips記事 ...って。 ちょっとまって、3000文字ってどのくらいやねん!わからん!と思ったので考えました。 文字数カウント Step.1 F12を開き開発者ツールConsoleを開きます。 Step.2 以下を貼り付けます。 let input = document.getElementsByClassName("css-1iu5ybx"); let count = input[0].value.replace(/\n/g, "").length; console.log(count); ここまでで612文字のようです。 Step.3 以上でも最低限の気もしますが再利用しづらいのでブックマークレットにしてみます。 URL: chrome://bookmarks/ を開きます。 右上からブックマークを追加します。 javascript: alert(document.getElementsByClassName("css-1iu5ybx")[0].value.replace(/\n/g, "").length); をURLに登録してみましょう。出来上がったら編集しながら、ブックマークボタンを押下。 ここまでで当記事、1064文字になりました。 まとめ 必要な情報を適切な長さでまとめるのが、良い記事を書くコツ(Tips)でもあるようです。 以上参考になればさいわいです。 (1429文字)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

flashメッセージを実装(非同期通信)

現在RubyとJavaScriptを用いて記事投稿サイトを作成しています。 今回はお気に入り登録/解除の際、flashメッセージを表示できるようにしていきたいと思います。 お気に入り機能の実装はこちら ■仕様等 ・お気に入りアイコンをクリックするとページ上部にメッセージが表示される ・画面スクロールに追従する ・連続でアイコンを押下した際は最新のメッセージが割り込む ・jQueryを使用しない ■完成後の動作とコード create.js.erb //①お気に入り登録が行われた場合に処理を実行する if(document.getElementById('article_<%= @article.id %>').innerHTML = '<%= escape_javascript( render 'shared/articles', article: @article ) %>'){ //②flashメッセージが既に存在する場合は、それを削除する if(document.getElementById('flash-message')){ document.getElementById('flash-message').remove(); } //③flashメッセージ(div要素)を生成する const flashMessage = document.createElement('div'); flashMessage.setAttribute('class', 'flash-message') flashMessage.setAttribute('id', 'flash-message') flashMessage.setAttribute("style", `top: ${window.scrollY}px; animation-name: flash-message-fade;` ); flashMessage.innerHTML = "お気に入りに登録しました" //④flashメッセージの挿入先を取得(body) const body = document.querySelector("body"); body.prepend(flashMessage) //⑤flashメッセージの表示位置をページスクロールに合わせる document.addEventListener('scroll', function(){ flashMessage.style.top = `${window.scrollY}px` }) //⑥flashメッセージを4秒後に消去する window.setTimeout(function(){ flashMessage.remove(); }, 4000); } ■お気に入り機能の実装 以前こちらで実装したものをそのまま使用しています。 ■flashメッセージの実装 ①お気に入り登録が行われた場合に処理を実行する create.js.erb if(document.getElementById('article_<%= @article.id %>').innerHTML = '<%= escape_javascript( render 'shared/articles', article: @article ) %>'){ if分の条件として、お気に入り登録の処理を記述しています。 ②flashメッセージが既に存在する場合は、それを消去する create.js.erb if(document.getElementById('flash-message')){ document.getElementById('flash-message').remove(); } flashメッセージ表示中に続けてお気に入りアイコン押下した際、 表示中のメッセージを消去して最新のメッセージを表示します。↓ ③flashメッセージ(div要素)を生成する create.js.erb const flashMessage = document.createElement('div'); flashMessage.setAttribute('class', 'flash-message') flashMessage.setAttribute('id', 'flash-message') flashMessage.setAttribute("style", `top: ${window.scrollY}px; animation-name: flash-message-fade;` ); flashMessage.innerHTML = "お気に入りに登録しました" style属性の値に指定している${window.scrollY}px;は、現在のスクロール位置を返します。 この要素とanimation-nameで指定しているアニメーションのCSSは以下の通りです。↓ index.css @keyframes flash-message-fade { 0% { display: inline; opacity: 0; transform: scaleY(-1); background-color: rgb(235, 247, 129); } 10% { opacity: 0.5; } 20% { opacity: 1; transform: scaleY(1) } 90% { opacity: 1; transform: scaleY(1); } 100% { opacity: 0; transform: scaleY(0); display: none; background-color: white; } } .flash-message { animation-duration: 3s; animation-fill-mode: forwards; opacity: 0; display: none; position: absolute; z-index: 2; width: 100%; display: flex; justify-content: center; align-items: center; height: 70px; padding-top: 1%; } ④flashメッセージの挿入先を取得(body) create.js.erb const body = document.querySelector("body"); body.prepend(flashMessage) bodyを取得し、生成したflashメッセージのdiv要素を挿入します。 ⑤flashメッセージの表示位置をページスクロールに合わせる create.js.erb document.addEventListener('scroll', function(){ flashMessage.style.top = `${window.scrollY}px` }) イベントにscrollを指定する事で、画面がスクロールされた際にイベント発火します。 flashメッセージのstyle属性のtopに、先程のwindow.scrollYを代入します。 これによりメッセージがページスクロールに追従する様になります。↓ ⑥flashメッセージを4秒後に消去する create.js.erb window.setTimeout(function(){ flashMessage.remove(); }, 4000); } window.setTimeout内に記述した処理は、指定した時間後に実行されます。 flashメッセージは表示から4秒後に消去されます。 create.js.erbの記述は以上です。 あとはdestroy.js.erb(お気に入り解除)にも①の条件と③のメッセージ内容を変更したものを記述します。 以上で完成です。 ■参考文献 https://developer.mozilla.org/ja/docs/Web/API/Window/scrollY
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む