- 投稿日:2021-06-23T23:29:40+09:00
Chrome Developer Tools で Web サイトの一部からリンクの一覧を取得する
手順 要素を選択した後にこのコマンドをコンソールで実行するだけです。 $$('a', $0).map(a => a.href) 使ってみた Yahoo News のトピックス一覧 コマンドはそのまま使えます。 TechCrunch Japan の新着順一覧 無駄なリンクを取り除くために selector に h2 を追加します。 $$('h2 a', $0).map(a => a.href)
- 投稿日:2021-06-23T22:36:56+09:00
Windows + Adobe CC illustrator / CEP 5 でイラレのカスタム UI を作ってみる (3) ボタンを作ってリロードする
Introduction Adobe illustrator (イラレ) の仕事では線幅やカラーが厳格に指定されている図形を多用する場合があります。(例えば地図作成や誌面デザイン、手芸の手順書 etc.) illustrator では既定で直線や円などの描画モードがワンボタンで選べるので、同様に所定の図形をワンボタン化できないか調べてまとめました。 この記事は CC 版 illustrator / Windows 環境が対象です。この章ではボタン作成の第一歩として、エクステンションをリロードするためのボタンを作ってみます。 illustrator のエクステンションはリロードが大変 エクステンション開発に当たっては、コードを組んでは試すような試行錯誤が必要になります。ところが illustrator でこれをやろうとするとリロードの問題に突き当たります。エクステンションの中身を更新しても、illustrator を再起動しない限り変更が反映されないのです。 試しにサンプルプログラムでボタン押下時に出力されるアラートの文章を変えてみましょう。 これは jsx フォルダ下に入っている hostscript.jsx に記述されています。以下の "hello from~" の中身を書き換えればアラートが変わるはずなのですが、 変更前 function sayHello(){ alert("hello from ExtendScript"); } 変更後 function sayHello(){ alert("hello from ExtendScript_changed"); } illustrator を起動した状態でこのように変えて hostscript.jsx を保存しても、アラートは変わりません。エクステンションを閉じて開きなおしてもダメです。CefClient もこのツールを裏から覗いているだけなので、クライアント側でリロードしても反映されません。 変えるには illustrator 自体の再起動が必要になります。 スクリプトを少し変えるたびに illustrator を再起動するのは大変です。 今回はスクリプトをリロードして変更をすぐに反映できるような、リロードボタンを作ってみましょう。 ボタンを作る CSS の解説のところで少しやりましたが、要は button タグでただ作るのではなく関連する CSS を指定してあげ、なおかつ処理を呼び出すための ID を一意に与えてあげれば OK です。ここでは btn_reloadpageandscriptとしました。 結局 [Call ExtensionScript] のところをそっくり真似して、index.html を以下のようにします。 変更前 <div> <button id="btn_test" class="topcoat-button--large hostFontSize">Call ExtendScript</button> </div> 変更後 <div> <button id="btn_test" class="topcoat-button--large hostFontSize">Call ExtendScript</button> <!--リロード用のボタンを追加--> <button id="btn_reloadpageandscript" class="topcoat-button--large hostFontSize">Reload</button> </div> この状態で index.html の見た目はこのようになります。 なるほど、これでもいいんですが [Call ExtendScript] ボタンと同じく、[Reload] も横長に引き延ばしてみましょう。 styles.css に btn_reloadpageandscript 専用の記述を加えます。 変更前 #btn_test{ width: 100%; } 変更後 #btn_test{ width: 100%; } #btn_reloadpageandscript{ width: 100%; } 横に伸びました。一旦これで行きましょう。 リロード処理を書く それでは、[Reload] ボタンをクリックしたときにエクステンションがリロードされる処理を記述していきましょう。 まずはユーザーの目に触れる部分である index.html の更新です。これはブラウザで [F5] を押すがごとく、ページを更新してしまえば完了です。 JSX は基本 index.html 表示に干渉できない (らしい) ので、これは main.js の方に書きましょう。例によって、今ある #btn_test の記法をいただいて #btn_reloadpageandscript の処理を記述します。画面更新の window.location.reload() を追加します。 変更前 $("#btn_test").click(function () { csInterface.evalScript('sayHello()'); }); 変更後 $("#btn_test").click(function () { csInterface.evalScript('sayHello()'); }); $("#btn_reloadpageandscript").click(function (){ //リロードのための追加スクリプト\ window.location.reload(); //ここまで }); これで index.html を編集した後に Reload ボタンを押すと、すぐに反映するようになるはずです。たとえば [Call ExtendScript] ボタンの表示を適当に変えて Reload を押すと、 すぐに反映されました。 ところが JSX についてはまだリロードできません。たとえば sayHello() のアラートで出てくる文言を以下のように変えてみても反映されません。ウィンドウをリロードしても JSX スクリプトは併せて読み込まれないようです。 hostscript.jsx alert("hello from ExtendScript_changed"); これを攻略するためには main.js 側で明示的に hostscript.jsx を呼び出します。sayHello() を読んでしまうとアラートが出るだけなので、そうではなくて JSX 全体を読むことにします。 hostscript.jsx は絶対パスで指定して読み込まないといけません。ベタ打ちでもいいのですがシステム側でエクステンションファイルのパスは読み込んでいるので、これに "/jsx/hostscript.jsx" のパスを付け加えて絶対パスを作ります。 (以下参考) https://ten-artai.com/2018/07/1404/ 変更前 (function () { 'use strict'; var csInterface = new CSInterface(); 変更後 (function () { 'use strict'; var csInterface = new CSInterface(); //JSXファイルパスの取得 var jsxPath = csInterface.getSystemPath(SystemPath.EXTENSION) + '/jsx/hostscript.jsx'; var jsxscr = '$.evalFile("' + jsxPath + '");'; //ログ出力 console.log(jsxscr); これで、後で evalScript() に渡すための文字列 evalFile("C:\Users~~<パス>") ができたはずです。ログ出力するようにしておいたので、例のデバッグ用ブラウザ (CefClient) で見てみましょう。 コンソールを見ると、以下のように出力されています。よさそうです。 Reload ボタンを押したとき、jsxscr に格納したパスのスクリプトを読み込むように以下のように変更します。 変更前 $("#btn_reloadpageandscript").click(function (){ //リロードのための追加スクリプト\ window.location.reload(); //ここまで }); 変更後 $("#btn_reloadpageandscript").click(function (){ //リロードのための追加スクリプト\ window.location.reload(); csInterface.evalScript(jsxscr); //ここまで }); 以上で変更は終了です![Reload] ボタンをもう一度押すと、今度はアラートが変更されています。 おまけ: 画面リロード処理の場所の検討 今回、リロード処理はすべて main.js の中に書きました。これは CEP における処理の呼び出しが index.html --> main.js --> hostscript.jsx の順になっており、さらに main.js 側の JQuery 流の記述で「どのボタンで呼び出されるか?」を指定する方式になっているためです。 筆者はしばらくこの方式でリロードボタンを愛用していたのですが、一点問題が出てきました。main.js の途中で、リロードボタンの処理の記述より前の段階でタイプミスなどのバグが出てしまうとリロードが効かなくなり、illustrator を再起動せざるを得なくなるのです。 この後の章で書くように今後は main.js は橋渡し程度しか担わないので、main.js を触ってバグが出てしまう場面は少ないのですが、対策するとすれば画面のロード window.location.reload() は index.html 側に書けば OK です。main.js が壊れていても、index.html 側でリロードを呼び出せるからです。 index.html <div> <!--リロード用のボタンを追加--> <button id="btn_reloadpageandscript" class="topcoat-button--large hostFontSize" onClick = 'window.location.reload();'>Reload</button> </div> ただし例外的に index.html 側に処理を書くことになるので違和感はありますが。 まとめ ここまででリロードボタンの実装は完了です。 次章以降ではこの機能を使って効率よく試行錯誤していきましょう。
- 投稿日:2021-06-23T22:34:36+09:00
Windows + Adobe CC illustrator / CEP 5 でイラレのカスタム UI を作ってみる (2) 中身をざっと見る
Introduction Adobe illustrator (イラレ) の仕事では線幅やカラーが厳格に指定されている図形を多用する場合があります。(例えば地図作成や誌面デザイン、手芸の手順書 etc.) illustrator では既定で直線や円などの描画モードがワンボタンで選べるので、同様に所定の図形をワンボタン化できないか調べてまとめました。 この記事は CC 版 illustrator / Windows 環境が対象です。この章では UI 作成の前に、まずはサンプルスクリプトの中身がどう絡み合っているかを紐解いていきます。 ※読み飛ばしても OK エクステンションの中身をざっと把握する エクステンションのコードがどう動いているのかのイメージを押さえます。(超雑感) (HTML の感覚が20年くらい前で止まってるのでご容赦を…) index.html - ユーザーの触るパネル ユーザー目線ではエクステンションの画面を見て、そこにあるボタンを押下することになります。このインターフェースは中身的には HTML を開いて表示しているわけで、 index.html に記述されています。たとえば、画面真ん中に "Call ExtendScript" ボタンが表示されていますが、これは以下箇所に対応します。 index.html <div> <button id="btn_test" class="topcoat-button--large hostFontSize">Call ExtendScript</button> </div> こういった対応付けは CefClient の開発者モードが便利です。Elements タブでは今表示されているページの内容 (≒index.html) のソースが見え、各行にマウスオーバーすると対応する箇所が色づいて見えます。「この <button ~~ というのにマウスを重ねたらボタンに色がつくから、この辺のコードはボタンを作ってるんだな~」みたいな感じで見ていけます。 css ファイル - ボタンや文字の色や形などを統一指定 ページに表示したいものは基本的に index.html に直接書けば出てきます。HTML ではボタンやフォーム、文字などをベタ打ちで出すことができるのですが、これをやると幾分古ぼけたインターネットの見た目になります。 変更前 <div id="content"> <div> <button id="btn_test" class="topcoat-button--large hostFontSize">Call ExtendScript</button> </div> </div> 変更後 <div id="content"> <div> <button id="btn_test" class="topcoat-button--large hostFontSize">Call ExtendScript</button> </div> <!-- ボタンタグ追加 --> <button type="button">test</button> </div> [test] ボタンができました。でも [Call ExtendScript] ボタンと違って今風じゃないですよね。ほかのインターフェースにマッチしていません。かと言って HTML 内にボタンを作るたびにボタンサイズやら色やらを指定するのはとても煩雑です。 これを解決しているのが css フォルダ内に入っている .css ファイル (Cascading Style Sheets) です。css では文字やボタンなどのサイズ、色、etc. をクラスとして設定しておき、HTML 側では「その要素がどのクラスに準じるか?」だけを教えてあげればスタイルが反映されるのです。もう一度 [Call ExtendScript] の HTML 対応箇所を見てみましょう。 このボタンは topcoat-button--large クラス、hostFontSize クラスが指定されています。(スペースで結んで 2 つのクラスを指定している) index.html <button id="btn_test" class="topcoat-button--large hostFontSize">Call ExtendScript</button> それぞれ CSS ファイルを探してみると、topcoat-button--large については topcoat-destop-dark.min.css に記述されています。(が、読みにくいので省略します) VSCode で見るとグレー系の色が指定されているのが見て取れます。 また hostFontSize については styles.css に書いてありますが、以下のコメントを見る限り illustrator が読み込んだ時に改めて指定するようですね。この動きについては、そりゃあイラレの UI はイラレにフォントを合わせるべきでしょうから納得です。 styles.css /* Those classes will be edited at runtime with values specified by the settings of the CC application */ .hostFontColor{} .hostFontFamily{} .hostFontSize{} また、ボタンの ID btn_test に特化したスタイルも記述されています。 これは横幅いっぱいにボタンを引き延ばすという意味ですね。 styles.css #btn_test{ width: 100%; } 結局、illustrator の見た目からそれほど外れないインターフェースにするのであれば既存の CSS に手を加える必要はありません。 main.js - メインのスクリプト index.html の中を見ていくと、[Call ExtendScript] ボタンを押したときに何が発動するのかは一切書いていません。表示以外の処理は以下箇所で読みこむ Javascript、特に main.js に書かれています。(他はライブラリ系なので無視で OK) index.html <script src="js/libs/CSInterface.js"></script> <script src="js/libs/jquery-2.0.2.min.js"></script> <script src="js/themeManager.js"></script> <script src="js/main.js"></script> main.js を見てみましょう。(一部略) main.js (function () { 'use strict'; var csInterface = new CSInterface(); function init() { themeManager.init(); $("#btn_test").click(function () { csInterface.evalScript('sayHello()'); }); } init(); }()); しばらく javascript に触れていない筆者にとって、この書き方はイマイチ読み方がわからないのでざっと調べます。 まず先頭の (function () 、これは即時関数と呼ばれる書き方のようです。関数を読み込みと同時に実行する方式で、ページの初期化などに使えます。処理自体は関数化しなくても書けるはずですが、関数の形をとることで変数のスコープを限定することができ、予期しない変数名重複などが発生しにくくなります。上の例ですと、csInterface という変数名は main.js の関数内でしか機能しません。 つまり index.html を読み込んで初期化で実行するのにぴったりというわけです。(以下参考) https://qiita.com/katsukii/items/cfe9fd968ba0db603b1e 続いて init() について。関数内に関数を作る構造になっていますが、これもスコープ管理上有利だからでしょうか。最終的に init() が実行されて初期化されることになります。 この中身を見てみると、ボタンを押したときの処理が記述されています。 main.js $("#btn_test").click(function () { csInterface.evalScript('sayHello()'); }); この `$("btn_test") のドル記法みたいなのは JQuery のお作法のようです。 この記述はセレクタと呼ばれ、$("#btn_test") であれば画面内の "btn_test" という ID の要素について、という意味になります。つまり、index.html で btn_test という ID で宣言されているボタンをクリックすると次の処理に入るというわけです。 (以下参考) https://rfs.jp/sb/javascript/jquery_abc/jquery-selector.html また、この関数内で csInterface.evalScript() が呼ばれています。実は Adobe は昔から (UI はなくても) スクリプト単体で動かすことはできました。このスクリプトと CEP のエクステンション UI を橋渡しするのがこの evalScript() です。つまりルール上、index.html --> main.js --> JSX (後述) の順に橋渡しをして初めて本題のスクリプトが動かせるのです。 さて、ここで呼ばれている 'sayHello()' はどこにいるのでしょうか?この答は次項の hostscript.jsx にあります。 hostscript.jsx - 本当に動かしたい処理を書くスクリプト JSX = Adobe の独自仕様の Extended Script File を指します。 紛らわしいのですが、react の JSX とは無関係です。 JSX は短いのでそのまま見てみましょう。 hostscript.jsx function sayHello(){ alert("hello from ExtendScript_changed"); } ようやく往年の JavaScript らしいシンプルな処理にたどり着きました。読んで字のごとく、"hello from ExtendScript_changed" というアラートを出すのがここですね。 manifest.xml エクステンションにかかわる各ファイルの所在を記述していくもののようです。 例えば以下の箇所ではスクリプトの相対パスを指定しています。jsx ファイルを分けるようなときにはここに追記が必要なのかもしれませんが、1つのエクステンションでそこまで大規模な jsx を作ることもないので一旦カスタムなしで大丈夫です。 manifest.xml <ScriptPath>./jsx/hostscript.jsx</ScriptPath> ざっくりした流れ illustrator がエクステンドを読み込んだ状態における、各ファイルと見た目との対応はこんな感じでしょうか。やはりスクリプティングの理解の上では、index.html <--> main.js --> hostscript.jsx の連携が重要です。 なお、後でポイントになりますが main.js はコンソール出力などエクステンションと直結した処理ができますが、JSX はなぜかできません。JSX は main.js が呼びに行く形でしか触れず、JSX からエクステンション側への処理を書く方法は見つかりませんでした。もともと JSX は Adobe のオブジェクト操作用の JS を書くところなので、無くて当然かもしれません。 (JSX の関数の返り値として main.js に渡すことは可能) この意味で index <--> main.js と main --> hostscript.jsx は矢印の使い方を分けています。 まとめ サンプルスクリプトがアラートを出すまでに関わるファイルと流れを書き出してみました。 続いてボタンの作成などを試していきます。
- 投稿日:2021-06-23T22:25:28+09:00
TweetボタンのテキストをJavascriptで動的に変更する
概要 最近作ったサイトで、Tweet機能を実装する際に、診断メーカーとかでよくある「ツイート内容を動的に書き換える」のにひと工夫必要だったので、ここでまとめておこうと思う。 機能の詳細 ツイートボタンをクリックすると、Javascriptが動いて、テキストを生成してツイート画面に遷移、という流れ 実装 まずTwitter公式が提供するテンプレートのツイートボタンを試してみる Twitterの公式で、ツイートボタンを生成できる リンク:https://publish.twitter.com/# 下にスクロールすると、下記のオプションがあるので、右の"Tweet Buttons"をクリック 下記のようなポップアップが出てくるので、今回は一番左の"Share Button"を押す コードが生成されるので、コピーしてみる(下記はサンプル) <a href="https://twitter.com/share?ref_src=twsrc%5Etfw" class="twitter-share-button" data-show-count="false">Tweet</a><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 設定できるパラメータはここで見れる。よく使いそうなのは以下。 data-text="テキストの内容" data-url="ツイートに埋め込むURL(デフォルトはボタンのあるページ)" data-hashtags="ハッシュタグ" 仕組みとしては、"data-"の部分で内容が様々指定でき、それに従ってツイート画面へURLを生成してくれる しかしこれでは、内容を静的にしか指定できない そこで、Javascriptで動的にURLを生成することで好きなようにテキストをいじっていく とりあえずツイートボタンを作る さっき生成したaタグは使えないので、オリジナルでツイートボタンを作る 下記はFontAwesomeを使ったサンプル index.html <!-- FontAwesome5.1のCDN --!> <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.1.0/css/all.css" integrity="sha384-lKuwvrZot6UHsBSfcMvOkWwlCMgc0TaWr+30HWe3a4ltaBwTZhyTEggF5tJv8tbt" crossorigin="anonymous"> index.html <!-- ツイートボタン --!> <div class="tweet" id="tweet"> <button id="twitter-share-button"><i class="fab fa-twitter"></i>ツイートする</button> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </div> style.css div.tweet { /* display: none; */ text-align: center; } div.tweet button { /* font-family: serif; */ text-align: end; border: none; font-size: 26px; background-color: #1DA1F2; padding: 6px 16px; border-radius: 100vh; color: white; cursor: pointer; } i.fa-twitter { font-size: 26px; } サンプル画像 ここからJavascriptでURLを生成する 今回は、テキストだけを動的に変えたい 他に指定するのは、サイトのURLとハッシュタグ 中身が変わるHTML要素を作成 index.html <!-- 中身が変わる --> <div class="tweet-text" id="tweet-text"> <!-- ここのテキストをツイートしたい --> </div> index.js // ツイートボタン押下時にテキストを動的に変更してツイート document.getElementById("twitter-share-button").onclick = function() { // 出力結果を取得 let text = document.getElementById("tweet-text").innerText; // オプションパラメータを設定 let hashtags = "ハッシュタグ"; let url = encodeURIComponent(location.href) // location.hrefは今いるURL // URLを生成して遷移 window.open("https://twitter.com/share?text=" + text + "&hashtags=" + hashtags + "&url=" + url); } URLは、"https://twitter.com/share?"の後ろに、設定したいパラメータをつなげていけば良い 完成 まとめ ツイートボタンってもっと複雑なのかと思ってたけど、静的な内容ならテンプレートで完結するし、動的でもすぐ実装できることがわかった。 自分で何か作ったときには積極的に採用して、ツイートを見てニヤニヤしたい。 記事を読んでくださった方は、既読感覚でLGTMを押していただけるととても励みになるのでよろしくお願いします! 筆者 文系独学でプログラミングしてる大学生(@aiju__19)
- 投稿日:2021-06-23T22:18:27+09:00
Windows + Adobe CC illustrator / CEP 5 でイラレのカスタム UI を作ってみる (1) 環境準備
Introduction Adobe illustrator (イラレ) の仕事では線幅やカラーが厳格に指定されている図形を多用する場合があります。(例えば地図作成や誌面デザイン、手芸の手順書 etc.) illustrator では既定で直線や円などの描画モードがワンボタンで選べるので、同様に所定の図形をワンボタン化できないか調べてまとめました。 この記事は CC 版 illustrator / Windows 環境が対象です。この章では開発環境の準備をメモしました。 illustrator の開発 illustrator は以前から JavaScript によるスクリプティングが可能でした。スクリプトを記述して特定フォルダに保存しておきメニューから起動します。(当時は UI は特になし) (スクリプトのリファレンスは以下) https://www.adobe.com/devnet/illustrator/scripting.html 以前は開発・デバッグ用のツール "Adobe ExtendScript Toolkit" があったようなのですが、CC 向けはすでに非公開となっています。 代わりに、CC 向けには "CEP 5" (Common Extensibility Platform) が搭載されました。ざっくり言うと UI を HTML 5 で作って JavaScript の処理を動かせるということのようです。つまり今回のように、簡単な処理をワンボタン化したい場合に向いています。 概要はこちらの開発者ブログに記載されており、この流れに沿って UI を作っていきます。 https://aphall.com/2014/08/cep-mega-guide/ 環境準備 Adobe illustrator CC 版 … すでにインストール済みの前提 Visual Studio Code (VSCode) … エディタとして利用。プラグインを入れると CEP 開発ができる。 ちなみに VSCode は Markdown が書けるので Qiita や社内フォーラム等の投稿下書きとしても気軽に使えます。別に開発者だけのものではありません。 以下 URL から VSCode をインストールします。 https://azure.microsoft.com/ja-jp/products/visual-studio-code/ 表示言語が英語になっている場合は日本語化します。 2-1. [View] - [Command Palette] をクリックし、"Configure Display Language" と打ち込みます。 2-2. [Install additional languages] をクリックします。 2-3. [Japanese Language Pack...] を選択してインストールし、VSCode を再起動します。 もし英語に戻したい場合は [表示] - [コマンドパレット] をクリックして "Configure Display Language" と打ち込み、ドロップダウンから "en" を選択して再起動します。 いよいよ本題の、CEP 用拡張をインストールします。 4-1. 左端の [拡張機能] アイコン をクリックします。([Ctrl] + [Shift] + [x] でも OK) 4-2. "CC Extension Builder" を検索して出てきたものをインストールします。 まずはローカルでデバッグを進めるのでデバッグ モードにしておきます。後述の通り、どうやらこの操作は Adobe 側の設定レジを変えるものであり VSCode のモードが変わるわけではないようです。 5-1. [表示] - [コマンドパレット] をクリックするか、[Ctrl] + [Shift] + [p] でコマンドパレットを表示します。 5-2. "Debug Mode" と入力し、[CC Extension Builder : Enable Debug Mode] をクリックします。 5-3. もし上記手順で Running the contributed command: 'extension.enableDebugMode' エラーが発生する場合は以下資料の不具合に該当しています。 https://github.com/Hennamann/CC-Extension-Builder-For-Visual-Studio-Code/issues/10 https://ymdsny.com/cep-preparations/ 代わりに手動でレジストリ キーを作成して回避できるようですが、あくまでワークアラウンドであり保証されたものではありません。 キー:HKEY_CURRENT_USER\SOFTWARE\Adobe\CSXS.9 値の名前 (文字列):PlayerDebugMode 値:1 サンプルを作って中身を見る 早速サンプルを作ってみます。 1-1. [表示] - [コマンドパレット] をクリックするか、[Ctrl] + [Shift] + [p] でコマンドパレットを表示します。 1-2. "create" と入力し、[Extension Creator: Create a New CC Extension] をクリックします。 1-3. 適当な拡張名を入力します。(この例では "myTestExtension01") 1-4. テンプレートの種類を選びます。(topcoat/spectrum/theme/basic) basic は最低限のもの、それ以外は CSS の差らしいですが、今回は topcoat を選択てみます。 1-5. 新たに VSCode ウィンドウが立ち上がり、一通りの必要ファイルが作成された状態が読み込まれます。なお、作業フォルダは以下の場所になります。 C:\Users\<ユーザー名>\AppData\Roaming\Adobe\CEP\extensions\<拡張名> 必要ファイルがどのような配置になっているか対応をとってみましょう。テンプレから生成されたファイルはこんな感じで、上から css (その他) / manifest.xml / アイコン類 (その他) / js (スクリプト類) / index.html と必須のものはあらかじめ含まれるようになっていますね。 (必要ファイル種類は以下参照) https://aphall.com/2014/08/cep-5-overview/ それでは完成したモノを見てみましょう。いくつか見方を書いていきます。当面 c) で作業することが多くなります。 a) HTML をブラウザで開いてみる 生成されたファイルは結局 HTML なので、まずは直接見てみましょう。 index.html を右クリックして [エクスプローラーで開く] をクリックします。ファイルの場所が開くので index.html をダブルクリックします。 読み込みボタンがついています。おそらくこれをクリックするとスクリプトが実行されるような UI ということですね。UI の見た目確認だけであればブラウザでも行けそうです。開発者モード (F12) でちょっとしたデバッグもできそう。 b) illustrator で読み込む 当然ながら、最終的にはイラレで使うので読み込めないといけません。 イラレで読むためには、まずは manifest.xml のコメントの通り、イラレに対応する箇所のコメントを外して有効化します。既定では Photoshop のみが有効化されています。 manifest.xml <!-- Uncomment tags according to the apps you want your panel to support Make sure to change these tags to use realistic version ranges before releasing your extensions, not 99.9! You may also need to change the CEP version in order to support certain apps. --> 有効化前 <!-- Illustrator --> <!-- <Host Name="ILST" Version="[18.0,99.9]" /> --> 以下のように、CC 版はバージョン 10.0 という扱いで有効化します。 有効化後 <!-- Illustrator --> <Host Name="ILST" Version="10.0" /> ここまで進めるとエクステンションのメニューとして出てくるようになります。クリックすると作成したボタンが表示された状態になります。 このテンプレではボタンをクリックすると以下のようなアラートを出すスクリプトが設定されています。 ※デバッグモードになっていないと正しくスクリプトが読み込まれないため、a) で見たようなボタンが出てきません。 なお、スクリプト等を修正した後はイラレを再起動しないと読み込まれません。 このため試すのが少し面倒です。また細かいデバッグやエラー参照などはできません。 c) illustrator で開いた状態で、デバッグ用ブラウザで読み込む イラレでメニューを直に見たほうが正確だけど、ブラウザのほうがデバッグは楽。 このため、イラレのメニューと同じものにブラウザで裏口アクセスできればベターです。 この解決策が以下開発者ブログに書いてあります。デバッグ用ファイルを作っておいておくと localhost 上に検証ツール (というか、サーバー) が立ち上がっており、ここにブラウザでアクセスできます。 https://aphall.com/2014/08/cep-5-html-debug/ なお .debug ファイルを手動作成する旨が書いてありますが、実際には自動で作成されるようです。中を見てみると各 Adobe 製品に対応したポートが既に割り当てられています。イラレは 8089 番なので、ブラウザで localhost:8089 にアクセス。 <!-- Illustrator --> <Host Name="ILST" Port="8089"/> ところが、解説通り localhost:8089 にアクセスしてリンクを踏んでも何も表示されません。F12 メニューでコンソールを見てみると "document.registerElement is not a function" エラーが出ています。 調べてみると以下のフォーラムに行き当たりました。 https://github.com/Adobe-CEP/CEP-Resources/issues/272 そもそも CEP は CEF (Chrome Embedded Framework) をベースにしており、イラレに Chrome が埋め込まれたような形で動いています。 localhost:8089 へのアクセスは同メニューをイラレではなくブラウザで覗くことになります。イラレに比べ Chrome はバージョンアップが頻繁に入るため、イラレからは参照できても Chrome からは正しく参照できないということのようでした。 つまるところイラレが表示しているメニューを裏側から覗くデバッグ専用ブラウザというかクライアントがあれば良く、以下の Cefclient が推奨されています。 https://github.com/Adobe-CEP/CEP-Resources/tree/master/CEP_9.x ダウンロードしたら、以下パスの cefclient.exe を起動します。シンプルなブラウザが立ち上がります。 Cefclient_Win64\cef_binary_79.1.38+gecefb59+chromium-79.0.3945.130_windows64_client\Release アドレスバーに localhost:8089と入力してアクセスします。 ※illustrator を起動し、エクステンション画面を表示していないと失敗します。 index.html をクリックすると、illustrator で出ているエクステンション画面を開発者モードで触れる状態になります。左側が illustrator 画面、右が Cefclient の画面です。別ウィンドウで開いているのでなんだか違和感はありますが、Chrome の開発者モードと同様に使えます。 なお、エクステンションや illustrator を一度閉じるとこのデバッグページも終了してしまうため、再度 localhost:8089 にアクセスして index.html へ飛ぶ必要があります。 開発に際してはこの c) モード、つまり illustrator で画面を開きながら Cefclient で中身を覗き込むスタイルが主となります。 ここまでで環境準備は終了です。 後日、もう一度 VSCode を開くとき 左ペインの [フォルダーの追加] をクリックします。 前回作成した拡張 (myTestExtension01) のフォルダを VSCode の作業フォルダに指定します。前回同様、左ペインに関連するファイルが並んで作業しやすくなります。 まとめ 環境準備は以上で終了です。 今回作ったエクステンションを次章以降で改造していきましょう。
- 投稿日:2021-06-23T21:11:44+09:00
【Vue.js】ミックスインの使い方と注意
はじめに 仕事で使う事になったので1からVue.jsについて学んだ。ちゃんと覚えておかないとまずそうな事を備忘録として1つ1つ残しておく。 ミックスインの使い方と注意 ミックスインとは Vue.jsのインスタンスのオプション1を共通化させて再利用できるようにする仕組み。 以下の例では、import { tokyoNumber } from "@/tokyoNumber";としている部分でミックスインtokyoNumberを読み込んでいる。 CountNumber.vue <template> <div> <h2>{{ title | upperCase }}</h2> <h4>{{ subTitle | lowerCase }}</h4> <p>{{ number }}</p> <button type="button" class="btn btn-primary" @click="number++">+1</button> </div> </template> <script> import { tokyoNumber } from "@/tokyoNumber"; export default { mixins: [tokyoNumber], /** ミックスインにより以下は宣言する必要がなくなる */ // data() { // return { // title: "Welcome to Tokyo", // subTitle: "Tokyo is a great city", // number: 0, // }; // }, // filters: { // upperCase(value) { // return value.toUpperCase(); // }, // lowerCase(value) { // return value.toLowerCase(); // }, // }, }; </script> tokyoNumber.js export const tokyoNumber = { data() { return { title: "Welcome to Tokyo", subTitle: "Tokyo is a great city", number: 0, }; }, filters: { upperCase(value) { return value.toUpperCase(); }, lowerCase(value) { return value.toLowerCase(); }, }, created() { console.log('created in Mixin'); }, } ソースコード全体は以下。 ミックスインで定義しているオプションと同じオプションがコンポーネントにある場合、コンポーネントのオプションで上書きされる 以下の例では、dataのtitle: "Welcome to Los",がミックスインのtitle: "Welcome to Tokyo",と被っているが、この場合はコンポーネントの方のオプション(title: "Welcome to Los",)が適用されるので、画面は以下のようになる。 画像のソースコードは以下(tokyoNumber.jsは上記と同じ。) CountNumber.vue <template> <div> <h2>{{ title | upperCase }}</h2> <h4>{{ subTitle | lowerCase }}</h4> <p>{{ number }}</p> <button type="button" class="btn btn-primary" @click="number++">+1</button> </div> </template> <script> import { tokyoNumber } from "@/tokyoNumber"; export default { mixins: [tokyoNumber], data() { return { title: "Welcome to Los", }; }, }; </script> vue.App.vue <template> <div> <!-- 省略 --> <h2>{{ title | upperCase }}</h2> <h4>{{ subTitle | lowerCase }}</h4> <p>{{ number }}</p> <button type="button" class="btn btn-primary" @click="number++">+1</button> <hr /> <CountNumber></CountNumber> </div> </template> <script> import CountNumber from "./CountNumber.vue"; import { tokyoNumber } from "@/tokyoNumber"; export default { mixins: [tokyoNumber], components: { CountNumber, }, // 省略 }; </script> ※ただし、ライフサイクルフック関数2では、ミックスインのオプション・コンポーネントのオプションの両方が適用され、ミックスインのオプション→コンポーネントのオプションの順番で実行される。 ソースコード全体は以下。 グローバルミックスインとその注意点 グローバルミックスインとは グローバルミックスインは、Single File Componentのグローバル登録・カスタムディレクティブのグローバル登録などと同じでグローバルにミックインを登録する事。 グローバルミックスインのオプション、ローカルミックスイン(コンポーネントのmixins:に宣言)のオプション、コンポーネントのオプションの3つのVueインスタンスのオプションが存在するが、それぞれ以下の順番で適用される。 グローバルミックスインのオプションに宣言された内容 ローカルミックスイン(コンポーネントのmixins:に宣言)のオプションに宣言された内容 コンポーネントのオプションに宣言された内容 注意点 ただ、ミックスインのグローバル登録を行うと、自動的に全てコンポーネントに対してミックスインが登録される事になり、以下の画像で分かるように複雑になり分かりにくくなる。なので、基本はグローバルミックスインは使わない。 ライフサイクルフックのcreated(){ console.log("global mixin"); }オプションを持つグローバルミックスインを定義した時の、consoleの状態が以下の図。 ※グローバルミックスインを使う場面は、プラグインなどで全てのコンポーネントに適用させたい何かを定義した時などに限定するのが得策。 ソースコード全体は以下。 Vue.jsの勉強メモ一覧記事へのリンク Vue.jsについて勉強した際に書いた勉強メモ記事のリンクを集約した記事。 https://qiita.com/yuta-katayama-23/items/dabefb59d16a83f1a1d4 data(){}, filters(){}, など ↩ https://qiita.com/yuta-katayama-23/items/578bb0cecf165fd850db#vue%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9%E3%81%AE%E3%83%A9%E3%82%A4%E3%83%95%E3%82%B5%E3%82%A4%E3%82%AF%E3%83%AB ↩
- 投稿日:2021-06-23T20:50:37+09:00
要素がクラスを持つかを複数条件で判定する
クラスの有無を複数条件で調べたい JavaScriptで要素をいじっていると、クラス使って要素の判別をしたくなる時があります。 要素がクラスを持っているかを調べるときに便利なのがclassList。classListの仕様については説明に自信がないので割愛させていただきます。 singleClass.js if(Element.classList.contains("hoge")){ console.log("Element has 'huga'!"); } で、これに加えて"fuga"クラスを持っている場合を判定したいんです私は。 単純にやると、以下のようになります。 multiClass.js if(Element.classList.contains("hoge") || Element.classList.contains("fuga")){ console.log("Element has 'hoge' or 'fuga'!"); } // ちなみにこうやっても最初の1つしか見てくれない if(Element.classList.contains("hoge","fuga")){ console.log("Element has 'hoge' or 'fuga'!"); } もっと増えた時ただただ長くなるだけだし、同じこと何回も書くのが個人的に好みではありません。 調べていると、引数指定を"hoge"&&"fuga"とやれば複数指定できる!って記事見つけたんですけど、順番入れ替えて調べると最後の1つしか見てないようです。 解決法(力技) まあ、脳筋の私ですのでスマートなやり方ではないです。classNameを使いました。classNameの仕様については説明に自信(以下略失礼)。 multiClass.js if(/(hoge|fuga)/.test(Element.className.split(/\W/)){ console.log("Element has 'hoge' or 'fuga'!"); } 一応説明させていただきますと、Element.classNameでクラスリストを文字列で取得し、正規表現を使用してスペースで分割した配列を、これまた正規表現でtestしています。 このtest時に複数のチェックしたいクラス名を指定しています。(testって配列に対してもちゃんとチェックしてくれるんですね。) 私が気がついていない誤字・脱字、それは違うよボケェ!というところあれば私としても是非直したいので教えてください。
- 投稿日:2021-06-23T20:14:20+09:00
メールアドレスをWebに書くときのホスピタリティ
このように書くと、たぶん、きっと、おそらく、、、クロールbotに捕食されにくい。はず。知らんけど。 脆弱性診断などで引っかかった場合、この方法でパスできる。 ポイントは、CSSの content: attr(); を使うこと。 HTML <a class="email" data-domain="domain" data-user="user" data-subject="タイトル" data-body="本文">@</a> CSS .email { cursor: pointer; } .email::before { content: attr(data-user); } .email::after { content: attr(data-domain); } JavaScript var mailto = document.querySelector('.email') mailto.addEventListener('click', onClick) function onClick () { var user = event.target.getAttribute('data-user') var domain = event.target.getAttribute('data-domain') var subject = event.target.getAttribute('data-subject') var body = event.target.getAttribute('data-body') window.location.href = 'mailto:' + user + '@' + domain + '?subject=' + subject + '&body=' + body } DEMO CodePen https://codepen.io/YusukeNakaya/pen/LYWqJyW 免責 保証はできません(笑)
- 投稿日:2021-06-23T19:59:56+09:00
Nuxt.jsで作成されたテキストの入力フォームにjavascriptから文字列を入力してsubmitしたい
はじめに Nuxt.js で作成されたWebアプリの一部を jsで自動的に入力したいみたいな希望、ありますでしょうか(たぶんない) しかし、普通に input.value = 'hogehoge' としただけでは、 submitButton.submit() としただけでは、DOM入力した内容が反映されないと思います 本稿では、それの解決をします 解決策 この例では、このページにあるinputタグのtypeが text のものを参考にしています const input = document.querySelector('input[name="text"]') input.value = 'xxx' + const event = new Event('input') + input.dispatchEvent(event) - document.querySelector('form').submit() + document.querySelector('input[name="submit"]').click() ポイント ポイント1 Nuxt.jsの場合、ユーザがキーボード入力した内容をNuxt.jsの内部に反映させる必要があります この反映を実施するのが input イベントになりますので、これを dispatchEvent(...) で強制的に発火させます これは、 input.value = 'hogehoge' と javascriptで書き換えさせるだけでは、キーボードから入力したように、変更が反映されない為です ポイント2 ここはうろ覚えです、間違えていたらごめんなさい Nuxt.jsで submit() イベントを発火させる場合、純粋に form.submit() させても、実際にsubmitされません(超うろ覚えポイント / もしかしたらsubmitされてるかも、間違えたらごめんなさい) この為、 type="submit" が定義されているボタンに対して click() イベントを発火させる必要がある為、このように記述します まとめ inputの内容はとりあえずいつも通り input.value = 'hogehoge' すればよい 入力後、 input.dispatchEvent(new Event('input')) で input イベントを強制発火 submitも submit() ではなく button.click() でクリックイベントを発火させる 間違えていましたらご指摘をお願い致します この記事があなたのお役に立てることを祈ります
- 投稿日:2021-06-23T19:44:46+09:00
ブラウザのみでQRコードをARマーカーにしてみた
やったこと ARマーカーっぽくはないですが、QRコードの位置を取得し、QRコード内の色情報を取得し、その色の弾を撃てるゲームっぽいものができました ブラウザでいい感じの3Dグラフィックスを表示するライブラリ 使い慣れてるという理由でp5jsを使います 3Dも手軽に利用できます ブラウザでQRコードを読み込むライブラリ 調べたところ有名なのがzxing.jsでした ですが、たぶんQRコードの位置は取得できなさそう p5jsじゃなくてJavaのProcessingなら jsQRというのを見つけた 実際に利用されている方のところで四角形を描いているので位置取れそう コードを書いていきます p5jsとjsQRでQRコードの情報読み取り htmlは、p5jsのテンプレートにjsQRを追加で読み込んでいるだけです。 index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>p5</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.js"></script> <!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/addons/p5.sound.min.js"></script> --> <script src="https://cdn.jsdelivr.net/npm/jsqr@latest/dist/jsQR.min.js"></script> <script src="sketch.js"></script> <style> html, body { margin: 0; padding: 0; } </style> </head> <body></body> </html> sketch.jsを作って sketch.js let captureGraphic; function setup() { createCanvas(640, 480); captureGraphic = createGraphics(640, 480); capture = createCapture(VIDEO); capture.hide(); } function draw() { image(capture, 0, 0); const code = getCodeFromCapture(capture, captureGraphic); if (code) { print(code.data); } } function getCodeFromCapture(cap, g) { g.image(cap, 0, 0, cap.width, cap.height); const imgData = g.elt .getContext('2d') .getImageData(0, 0, cap.width, cap.height).data; return jsQR(imgData, cap.width, cap.height); } p5jsだとブラウザでカメラを利用するまでのコードがめちゃくちゃ短くなります。 ただ、カメラ映像から画像データまでの変換が(どちらにしろですが)必要になります。 できた!! QRコードの位置を取得したい 線を描くのもp5jsだと短めにかける感じに let captureGraphic; function setup() { createCanvas(640, 480); captureGraphic = createGraphics(640, 480); capture = createCapture(VIDEO); capture.hide(); } function draw() { image(capture, 0, 0); const code = getCodeFromCapture(capture, captureGraphic); if (code) { print(code.data); // QRコードを囲むように線を引く const pos = code.location; noFill(); stroke(255, 0, 0); strokeWeight(10); beginShape(); vertex(pos.topLeftCorner.x, pos.topLeftCorner.y); vertex(pos.topRightCorner.x, pos.topRightCorner.y); vertex(pos.bottomRightCorner.x, pos.bottomRightCorner.y); vertex(pos.bottomLeftCorner.x, pos.bottomLeftCorner.y); endShape(CLOSE); // ついでにテキスト stroke(0); strokeWeight(1); text(code.data, pos.topLeftCorner.x, pos.topLeftCorner.y); } } function getCodeFromCapture(cap, g) { g.image(cap, 0, 0, cap.width, cap.height); const imgData = g.elt .getContext('2d') .getImageData(0, 0, cap.width, cap.height).data; return jsQR(imgData, cap.width, cap.height); } 3Dグラフィックスを表示させてみる まずは単純なboxを表示してみた let captureGraphic; function setup() { createCanvas(640, 480, WEBGL); captureGraphic = createGraphics(640, 480); capture = createCapture(VIDEO); capture.hide(); } function draw() { // 3Dだと座標の原点が画面中央になるので translate(-width / 2, -height / 2); image(capture, 0, 0); const code = getCodeFromCapture(capture, captureGraphic); if (code) { // print(code); const pos = attendPositionData(code.location); push(); translate(pos.center.x, pos.center.y); box(100); pop(); } } function getCodeFromCapture(cap, g) { g.image(cap, 0, 0, cap.width, cap.height); const imgData = g.elt .getContext('2d') .getImageData(0, 0, cap.width, cap.height).data; return jsQR(imgData, cap.width, cap.height); } function attendPositionData(pos) { const retPos = pos; retPos.center = {}; retPos.center.x = (pos.topLeftCorner.x + pos.topRightCorner.x + pos.bottomLeftCorner.x + pos.bottomRightCorner.x) / 4; retPos.center.y = (pos.topLeftCorner.y + pos.topRightCorner.y + pos.bottomLeftCorner.y + pos.bottomRightCorner.y) / 4; return retPos; } 弾を撃てるようにしてみる classも使って動くsphereを描画してみる QRコードから受け取った値を色として出してみています let captureGraphic; let burrets = []; function setup() { createCanvas(640, 480, WEBGL); captureGraphic = createGraphics(640, 480); capture = createCapture(VIDEO); capture.hide(); } function draw() { // background(255); // 3Dだと座標の原点が画面中央になるので translate(-width / 2, -height / 2); image(capture, 0, 0); const code = getCodeFromCapture(capture, captureGraphic); if (code) { // print(code); const pos = attendPositionData(code.location); if (frameCount % 5 == 0) { burrets.push(new Barrett(pos.center.x, pos.center.y, code.data)); } } burrets.forEach((e) => { e.update(); e.draw(); }); burrets = burrets.filter((e) => e.z < 1000); } function getCodeFromCapture(cap, g) { g.image(cap, 0, 0, cap.width, cap.height); const imgData = g.elt .getContext('2d') .getImageData(0, 0, cap.width, cap.height).data; return jsQR(imgData, cap.width, cap.height); } function attendPositionData(pos) { const retPos = pos; retPos.center = {}; retPos.center.x = (pos.topLeftCorner.x + pos.topRightCorner.x + pos.bottomLeftCorner.x + pos.bottomRightCorner.x) / 4; retPos.center.y = (pos.topLeftCorner.y + pos.topRightCorner.y + pos.bottomLeftCorner.y + pos.bottomRightCorner.y) / 4; return retPos; } class Barrett { constructor(_x, _y, _col) { this.x = _x; this.y = _y; this.z = -100; this.col = _col; } update() { this.z += 10; } draw() { push(); translate(this.x, this.y, this.z); noStroke(); fill(this.col); sphere(20); pop(); } } M5Stackを使って色情報を持ったQRコードを表示 今回は簡単にUIFlowを使いました 使い方などは… いちおうPythonコードも from m5stack import * from m5ui import * from uiflow import * setScreenColor(0x222222) color = None def buttonA_wasPressed(): global color color = '#FF0000' lcd.qrcode(color, x=55, y=10, width=220, version=2) pass btnA.wasPressed(buttonA_wasPressed) def buttonB_wasPressed(): global color color = '#00FF00' lcd.qrcode(color, x=55, y=10, width=220, version=2) pass btnB.wasPressed(buttonB_wasPressed) def buttonC_wasPressed(): global color color = '#0000FF' lcd.qrcode(color, x=55, y=10, width=220, version=2) pass btnC.wasPressed(buttonC_wasPressed) color = '#FF0000' lcd.qrcode(color, x=55, y=10, width=220, version=2) 結果。AR銃みたいになった でも楽しかったし、勉強になった 記事にまとめようとするとやりたいこと増えるやつでした 今後 もっとQRコード自体に情報を持たせて、銃の性能とか撃ったタイミングとか見れたら面白そう こうなると、QRコードを表示している端末とカメラ端末を一切通信せずに色々やってみたい マーカーの歪みでQRコード端末の姿勢推定とかやりたい けど少し傾くだけで結構検出からもれる… せっかくブラウザなのでスマートフォンのカメラでやりたい 昔の記事 久々にやったらだめだった…更新したい
- 投稿日:2021-06-23T18:53:57+09:00
DFS系の問題の解き方@JS
JavaScriptでDFSを解くやり方を書いていきます。 重要なポイント 基本的には、下記のような木構造で表せるときにDFSを使うことができる 使うときのポイント ・一番深いときの数値を見つける ・ノード配列を用意する(<ーノード配列の詳細は後ほど) 一番深いときの数値とは、下記のように木構造があったとき、視点から一番遠いところまでの数のこと ・ノード配列を用意する ノード配列(ホントは名前とかあるのかもしれないですが、勝手に名付けました。すみません、、)とは 次にノードに行くときに候補として上がる物の配列のこと! 上の写真の場合、 『0』のノード配列は,[1,2,3] 『1』のノード配列は,[4,5] ... といった具合になる。 このポイントを抑えて、下記テンプレートを使えば、DFSは大体、実装できるはず、、 競プロの問題と参考のコード これは競プロのC問題。 あなたはスーパーハッカーです。高橋君を攻撃対象に定めたあなたは、 高橋君のパソコンのパスワードに関して次の事実を突き止めました。 長さは N 文字である。 a, b, c 以外の文字は含まれない。 高橋君のパソコンのパスワードの候補として考えられる文字列をすべて列挙してしまいましょう。 この問題は下のような、◯を視点とした木構造で表せる。 =>DFSが使える! |---a--- |-a---b--- | |---c--- |-a-b--- | |-c--- | ◯---b-- |-c-- 実際のコードがこちら。 index.js const depth = 3 //一番深い階層(今回は)入力値3が与えられたとする) const abc = ["a","b","c"] //今回のノード配列 dfs([]) //からの配列を引数に取る function dfs(arr){ //(ここの条件は問題によって変わるのでよく考える人用あり) if(arr.length === depth) return //配列の長さと深さが一致したらリターンする for(let i = 0;i<abc.length;i++){//ノード配列を回す arr.push(abc[i]) if(arr.length === depth){ ans += arr.join("") + "\n" } dfs(arr) //再起的に使う arr.pop() //末尾削除 } } このコードを実行すれば、下記のような文字列列挙ができる aaa aab aac aba abb abc aca acb acc baa bab bac bba bbb bbc bca bcb bcc caa cab cac cba cbb cbc cca ccb ccc
- 投稿日:2021-06-23T18:53:09+09:00
【初心者でもわかる】ポップアップウィンドウの作り方
どうも7noteです。ポップアップウィンドウの作り方 ポップアップを開いたまま、サイトの閲覧を継続してもらいたいなら、 ポップアップを別ウィンドウで開くように設定してみましょう。 最近のwebサイトのポップアップは全て閲覧中のウィンドウの真ん中に出るので ポップアップを消さないと閲覧を続行できません。 別タブで開くと、同様にタブを切り換えないと両方の情報を同時に見ることができません。 古典的な方法ですが、javascriptを使ってリンク先を専用の別ウィンドウでポップアップ表示させる方法を紹介します。 最近めっきりみなくなったので備忘録的に記事に残します (スマホが主流の時代なんで、別ウィンドウで開くはあんまり使われないかもですが。。。) ポップアップウィンドウの表示方法 index.html <a href="javascript:void(0);" onclick="window.open('{リンク先URL}','{ウィンドウ名}','{ウィンドウ設定}');return false;">別ウィンドウを開く</a> <!-- 入力例 --> <a href="javascript:void(0);" onclick="window.open('http://hoge-hoge.com/window/','window1','width=500,height=300');return false;">別ウィンドウを開く</a> 解説 window.open()で開いたウィンドウはタブ分けやブックマーク、拡張機能などのメニューなしで開かれます。 ウィンドウ設定の箇所で表示位置や大きさを変更することが可能です。 (top,left,width,hright等) ちなみにスマホの場合は単純に別タブで開かれます。ウィンドウの概念がないため。 まとめ PC表示の時しか使わない手法ですが、「別ウィンドウで情報を確認しながら入力フォームに情報を入力してもらう」みたいに使いどころはあるかなと思います。 javascriptといっても難しい計算や処理を入れていないので、普段javascriptに触れていない方でも簡単に実装が可能です。 おそまつ! ~ Qiitaで毎日投稿中!! ~ 【初心者向け】WEB制作のちょいテク詰め合わせ
- 投稿日:2021-06-23T17:45:51+09:00
Canned Dogelog Runtime for Prolog Snippets
The Dogelog runtime is a Prolog system that completely runs inside a browser. It is written in JavaScript for new browsers. We discuss how the Doglog runtime can be canned, so that it runs in old browsers setups. Using canned Dogelog runtime, stackoverflow snippets are made possible. Module Restrictions The Dogelog runtime is developed ES6 module style. ES6 modules have some benefit for development of the Dogelog runtime. However, ES6 modules also impose more restrictions on the resulting code. For example, ES6 modules are fetched via the Cross-Origin Resource Sharing (CORS) protocol, which not only requires a local server for testing, but also imposes by default a Same-Origin-Policy (SOP). So one might be tempted to do the following: <script type="module"> Import .. from "http://www.dogelog.ch/lib/index.js"; Etc.. </sctipt> This will result in a CORS error in a modern browser: Single File The transpiler offers the bundler predicates bundle_clear/1 and bundle_add/2. By means of these predicates, a canned Dogelog runtime can be put together, stripping ES6 module style. The following files need to be collected: runtime/machine.js runtime/special.js runtime/compile.js ---+ runtime/loader.js | runtime/index.js +---> canned/dogelog.js | transpiler/exchange.js ---+ By the canned distribution of the Dogelog runtime, it is possible to include Prolog code snippets in stack overflow answers. Stack overflow provides a wizard to insert a snippet into an answer. An example was put into the wild at this web address: http://stackoverflow.com/a/67990574/502187 A screenshot of running the stackoverflow snippet is seen here: Polyfill Issues Unfortunately, the canned Dogelog runtime does not yet solve all old browser compatibility problems. We discuss some remaining problems. Old browsers might not offer features from the newest 11th Edition of the ECMAScript standard (ES11) from 2020. Among features used by Dogelog runtime is the JavaScript bigint primitive numbers type. The Dogelog runtime switches to bigint for integers outside of the range from -94906266 to 94906266. The Dogelog runtime uses also bigint for floats that are integral and in the range from -94906266 to 94906266. The latter is needed so that 1 = 1.0 fails. Therefore, the lack of bigint type affects both Prolog integers and Prolog floats. For more details see the Dogelog reference manual: http://github.com/jburse/dogelog-moon/tree/main/manual Conclusion The Dogelog runtime is marturing. It has new applications like stackoverflow snippets we didn't even think of, when making the first prototype. To give the Dogelog runtime a platform for discussion, there is now this Facebook group: http://www.facebook.com/groups/dogelog
- 投稿日:2021-06-23T17:28:17+09:00
【Python】DjangoでHandsontableをAjaxしてみる 【JaveScript】
はじめに HandsontableというJavaScriptライブラリを使うと、WEBでExcelのようなスプレッドシートライクな入力が可能になります。が、更新方法が分からなかったので、いろいろ調べてみました。 なお、JavaScriptについては全くの初学者なので、おかしいところも多々あると思いますが、ご笑覧いただければ幸いです。 できたもの データベース管理システム(PostgreSQL)からデータを取得し、Handsontableを使ってテーブルを表示。 ブラウザ上でデータを更新し(garlicをAPPLEへ)、上書きボタンをクリックすると… Ajax(非同期通信)を使って、ポスグレ上のデータを更新します。 念のため、管理サイトでも更新を確認。 準備 概要は以下の通りです。 django admin startproject conf . タイムゾーン、言語の設定 STATICFILES_DIRSの設定 TEMPLATESの設定 DATABASE(PostgreSQL)の設定 アプリケーション作成 $ python manage.py startapp hot アプリケーションを作成します。 conf/settings.py INSTALLED_APPS = [ 'hot.apps.HotConfig', # 以下省略 ] 次にsettings.pyにアプリを登録します。 conf/urls.py from django.contrib import admin from django.urls import path, include urlpatterns = [ path('hot/', include('hot.urls') # 以下省略 ] confフォルダのurls.pyを修正します。 hot/urls.py from django.urls import path from . import views app_name = 'hot' urlpatterns = [ path('', views.Top.as_view(), name='top'), # path('ajax_update/', views.ajax_update, name='ajax_update') ] hotフォルダ内にurls.pyを作成し、上記のように書きますクラスベースビューのTopはテーブルを表示するページで、関数ビューのajax_updateはデータを上書きするためのページです。Ajaxなので、ページ遷移はしません。 モデルを作成 hot/models.py from django.db import models class Merchandise(models.Model): merchandise = models.CharField('merchandise', max_length=255) price = models.IntegerField('price') origin = models.CharField('origin', max_length=255) def __str__(self): return self.merchandise hot/admin.py from django.contrib import admin from .models import Merchandise admin.site.register(Merchandise) $ python manage.py makemigrations hot $ python manage.py migrate $ python manage.py createsuperuser モデルを作成します。商品名と価格と生産地をデータとしました。管理サイトへの登録、マイグレーション、スーパーユーザーの作成を忘れずに。 適当に三つほどデータを入れておきます。 テーブルを表示するページ(Top)を作成 hot/views.py import psycopg2 from django.http.response import JsonResponse from django.shortcuts import render from django.views import generic from .models import Merchandise class Top(generic.TemplateView): template_name = 'table/top.html' # contextをテンプレートファイルに渡す。 def get_context_data(self, **kwargs): # はじめに継承元のメソッドを呼び出す context = super().get_context_data(**kwargs) # データベースに接続→データ取得→切断 connect_str = '' connect_str += "dbname=test1 " connect_str += "user=postgres " connect_str += "password=password" conn = psycopg2.connect(connect_str) cur = conn.cursor() cur.execute('select * from hot_merchandise') table = cur.fetchall() cur.close() conn.close() # handsontable用のデータを作成 key = ('id', 'marchandise', 'price', 'origin') data = [] for record in table: data.append(dict(zip(key, record))) columns = [] for k in key: columns.append(dict(data=k)) colHeaders = list(key) context['data'] = data context['columns'] = columns context['colHeaders'] = colHeaders return context Topページのビューを作成します。データベースからデータを取得し、Handsontable用にかたちを整えて、テンプレートファイルに渡します。テーブルの名称はhot_merchandiseとなっています。アプリ名小文字_モデル名小文字になるみたいです。 参考 # table(整形前のデータ) [(1, 'carrot', 150, 'kanagawa'), (2, 'garlic', 200, 'aomori'), (3, 'onion', 300, 'hyogo')] # data [ {'id': 1, 'merchandise': 'carrot', 'price': 150, 'origin': 'kanagawa'}, {'id': 2, 'merchandise': 'garlic', 'price': 200, 'origin': 'aomori'}, {'id': 3, 'merchandise': 'onion', 'price': 300, 'origin': 'hyogo'} ] # columns [ {'data': 'id'}, {'data': 'merchandise'}, {'data': 'price'}, {'data': 'origin'} ] # colHeaders ['id', 'merchandise', 'price', 'origin'] ちなみに、handsontableに渡すデータの形状は上記のような感じです。 base.html {% load static %} <!doctype html> <html lang="ja"> <head> <!-- Required meta tags --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <!-- Bootstrap CSS --> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous"> <!-- handsontable --> <script src="{% static 'hot/jquery.min.js' %}"></script> <script src="{% static 'hot/handsontable.full.min.js' %}"></script> <link rel="stylesheet" href="{% static 'hot/handsontable.full.min.css' %}"> <title>HandsontableでAjax</title> </head> <body> <!-- ナビゲーションバー --> <nav class="navbar navbar-expand-lg navbar-light bg-light"> <a class="navbar-brand" href="{% url 'home:top' %}">Test</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarSupportedContent"> <ul class="navbar-nav mr-auto"> <li class="nav-item"> <a class="nav-link" href="{% url 'home:top' %}">Home</a> </li> <li class="nav-item"> <a class="nav-link" href="{% url 'table:top' %}">Table</a> </li> <li class="nav-item"> <a class="nav-link" href="{% url 'hot:top' %}">HOT</a> </li> <li class="nav-item"> <a class="nav-link" href="{% url 'admin:index' %}">Admin</a> </li> </ul> </div> </nav> <div class="container"> {% block content %}{% endblock %} </div> <!-- Optional JavaScript --> <!-- jQuery first, then Popper.js, then Bootstrap JS --> <!-- jQueryはslim版じゃないやつを使う。slimはajaxに対応していないため。 --> <script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script> {% block extrajs %}{% endblock %} </body> </html> ベースとなるテンプレートファイルを作成します。handsontableのjsやcssはCDNも公開されています。この記事では触れていないアプリ(home, table)へのリンクもありますが、そちらは無視してください。 hot/top.html {% extends 'base.html' %} {% load static %} {% block content %} <h3 class="mt-3">Table</h3> <div id="grid"></div> <div class="mt-1"> <button type="button" id="btn_update_data" class="btn btn-primary">上書き</button> </div> {% endblock %} {% block extrajs %} <script> 'use strict'; // 変数の定義 var data = {{ data | safe }}; var columns = {{ columns | safe }}; var colHeaders = {{ colHeaders | safe }}; // テーブルを表示 var container = document.getElementById('grid'); var hot = new Handsontable(container, { data: data, columns: columns, columnSorting: { // CDNはこの書き方じゃない? column: 0, sortOrder: 'asc' }, rowHeaders: true, colHeaders: colHeaders, filters: true, dropdownMenu: true }); </script> {% endblock %} top.htmlはbase.htmlをextendsします。block contentには見出しとテーブルと上書きボタンを設置しています。テーブルを書き込むdiv要素のid属性を'grid'としています。それを受けてextrajsブロックでgetElementById('grid')としています。 ブラウザ上ではこのような画面が表示されます。この時点でブラウザ上でデータは編集可能ですが、データベースの更新はできないので、リロードすると元に戻ります。 ↑ブラウザ上では編集できますが、更新はできません↓ ↑リロードすると元に戻ってしまいます。 データを上書きする データを上書き更新できるようにしたいと思います。おおまかな手順としては、テーブルの下に設置した上書きボタンを押下すると、その時点のデータを取得し、データベースを更新するようにしたいと思います。 hot/top.html {% block extrajs %} <script> // 省略 // Ajaxでデータベースを更新 function getCookie(name) { var cookieValue = null; if (document.cookie && document.cookie !== '') { var cookies = document.cookie.split(';'); for (var i = 0; i < cookies.length; i++) { var cookie = jQuery.trim(cookies[i]); // Does this cookie string begin with the name we want? if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } var csrftoken = getCookie('csrftoken'); function csrfSafeMethod(method) { // these HTTP methods do not require CSRF protection return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); } $.ajaxSetup({ beforeSend: function (xhr, settings) { if (!csrfSafeMethod(settings.type) && !this.crossDomain) { xhr.setRequestHeader("X-CSRFToken", csrftoken); } } }); // ↑ ここまではおまじない $('#btn_update_data').on('click', function(event){ event.preventDefault(); // ボタンをクリックした時点のデータを取得 var data_hot = hot.getData(); console.log(data_hot); // Ajax実行 $.ajax({ 'url': '{% url "hot:ajax_update" %}', 'type': 'POST', 'data': { 'temp_data': 'aaa', 'str_hot': JSON.stringify(data_hot), 'str_col': JSON.stringify(columns) }, 'dataType': 'json' }) .done(function(data) { window.alert("done!!"); }) .fail(function(){ window.alert("error!!"); }); }); </script> {% endblock %} hot/top.htmlのextrajsブロックにスクリプトを追記します。前半はおまじないらしく、よくわかりません…。 $('#btn_update_data').on('click', function(event)以降がデータの上書きです。上書きボタンがクリックされたら、データを取得します。次にhot:ajax_upで指定した関数を実行するのですが、その時にdataを渡します。dataの中身は'temp_data'と'str_hot'と'str_col'です。'temp_data'は適当なデータです。'str_hot'はテーブルのデータをJSON形式に変換したものです。今回はJavaScript(Handsontable)からPythonへデータを渡すのですが、いろいろ調べてなんとかこの形にたどり着きました。(もっと良い方法があったら知りたい…) hot/urls.py app_name = 'hot' urlpatterns = [ path('', views.Top.as_view(), name='top'), path('ajax_update/', views.ajax_update, name='ajax_update') # 追加 ] top.htmlにhot:ajax_updateというURLを指定しましたが、その様なURLは無いので作ります。次にviews.pyにajax_updateという関数ビューを作ります。 hot/views.py def ajax_update(request): # POSTされたデータを受け取る。この時点では文字列。 str_hot = request.POST.get('str_hot') # リストっぽい文字列[[1, "apple", 200, "aomori"], ...] str_col = request.POST.get('str_col') # リストっぽい文字列[{'data':'id'}, {'data':'merchandise}, ...] # 文字列をリストに変換 exec('data_hot={}'.format(str_hot), globals()) exec('data_col={}'.format(str_col), globals()) lis_col = [] for elm in data_col: lis_col.append(elm['data']) # 辞書を作成 # [{'id':1, 'marchandise'='apple', 'price':100, 'origin':"aomori"}, {...}, ...] lis_dic = [] for row in data_hot: lis_dic.append(dict(zip(lis_col, row))) # データベースに接続 connect_str = '' connect_str += "dbname=test1 " connect_str += "user=postgres " connect_str += "password=password" conn = psycopg2.connect(connect_str) cur = conn.cursor() # データ更新 for dic in lis_dic: id = dic['id'] sql = 'UPDATE hot_merchandise SET' for k,v in dic.items(): if k != 'id': if type(v) is str: sql += f" {k}='{v}'," elif type(v) is int or type(v) is float: sql += f" {k}={v}," sql = sql[:-1] # 末尾のカンマを削除 sql += f" where id={id};" print(sql) # 参考: "UPDATE hot_merchandise SET marchandise='orange', price=200, origin='aomori' WHERE id=1" cur.execute(sql) conn.commit() # データベース切断 cur.close() conn.close() # レスポンスを返さないといけないらしいので、適当に… return JsonResponse({'ret': 'return'}) いよいよデータベースの更新です。いろいろ書いてありますが、やっていることはデータを受け取り、形を整えて、SQL文を生成し、実行の繰り返しです。ポイントは、ajaxで送られてきたデータは一見リストの様な形式をしていますが文字列です。なので、それをリストに戻してやる必要があります。いろいろ試した結果exec関数を使うことになりました。(もっと良い方法があったら知りたい…切実に…) 実行してみる ブラウザ上でデータを更新し、上書きボタンをクリックします。 データの更新がうまくいけば、このようなウィンドウが開きます。ページ遷移はしません。 前回はリロードすると元に戻ってしまいましたが、今回は大丈夫です。 管理サイト上でもデータベースが更新されています! おわりに なんとか動いてよかった…。あやふやなところがたくさんあるので、少しずつ勉強していきたいと思います。
- 投稿日:2021-06-23T17:08:13+09:00
Vue.jsのslotあれこれ
Vue.jsのslot機能について勉強したので,備忘録的に書いてきます. はじめに そもそもslotとは何か. Vue.jsにおけるslotは,親コンポーネントから小コンポーネントにテンプレートを差し込むことができる機能です. 名前付きslot 例えば,タイトル,本文,ボタンなどを要素として持つ以下のようなarticleというコンポーネントをApp.vueから呼び出す場合を考えてみます. article.vue <template> <div style="width=500px;"> <h1>タイトル</h1> <p>本文</p> <button>ボタン</button> </div> </template> <script> export default { name: 'Article', } </script> App.vue <template> <Article /> </template> <script> import Article from './components/article.vue'; export default { name: 'App', components: { Article, } } </script> これらのファイルは,現状で以下のように動作します. しかし,article.vueを繰り返し使って複数の記事を作成したい場合,呼び出す際にタイトルや本文,ボタンの文字を指定できたら便利ですよね. それを可能にするのがslotです.上記のファイルを以下のように書き換えてみましょう. article.vue <template> <div style="width=500px;"> <h1> <slot name="title"><!-- ここにタイトルが差し込まれます --></slot> </h1> <p> <slot name="body"><!-- ここに本文が差し込まれます --></slot> </p> <button> <slot name="buttonText"><!-- ここにボタンテキストが差し込まれます --></slot> </button> </div> </template> <script> export default { name: 'Article', } </script> App.vue <template> <Article> <template v-slot:title> スロットで差し込んだタイトルです </template> <template v-slot:body> スロットで差し込んだ本文です </template> <template #buttonText> <!-- v-slotは省略記法として#で置き換えることができます --> スロットで差し込んだボタンテキストです </template> </Article> </template> <script> import Article from './components/article.vue'; export default { name: 'App', components: { Article, } } </script> 実行結果はこんな感じ このように,子コンポーネントで親からテンプレートを受け取りたい部分に<slot name="hogehoge">と書き,親コンポーネントで<template v-slot:hogehoge>と差し込みたいスロットの名前を指定してあげることで可読性も高く,汎用性の高いコンポーネントを作ることができます. v-slotは,v-bindにおける:,v-onにおける@のように省略記法を持っており,#を使って省略することができます. <template #buttonText> <!-- v-slotは省略記法として#で置き換えることができます --> スロットで差し込んだボタンテキストです </template> このように,差し込みたい箇所(slot)に名前をつけ,親コンポーネントからslotを指定されるslotを名前付きslotと言います. デフォルトスロット 名前付きslotに対して,名前のないslotも作ることができます.名前のないslotはdefault slotとも呼ばれます.先程の例では,テンプレートを差し込みたい箇所が複数ありましたが,一つで良い場合などにdefault slotを使います. 以下のような<a>タグをラップしたようなコンポーネント,navigation-link.vueを用意します. navigation-link.vue <template> <a :href="url"> <slot><!-- slotが一つだけなのでname属性がいらない --></slot> </a> </template> <script> export default { name: 'NavigationLink', props: { url: String, }, } </script> App.vue <template> <navigation-link url="https://www.google.com/">googleへのリンク</navigation-link> </template> <script> import NavigationLink from './components/navigation-link.vue'; export default { name: 'App', components: { NavigationLink, }, } </script> 実行結果はこんな感じ navigation-link.vueのように,スロットが一つだけの場合,name属性をつけないことでdefault slotになります.また,name属性に明示的にdefaultを指定することでも同様に,default slotにすることができます.親コンポーネント側でもslotの名前を指定する必要はありません. また,名前付きslotがある場合にも,名前なしslotを利用することができます. navigation-link.vue <template> <div style="width=500px;"> <h1> <slot name="title"></slot> </h1> <h2> <slot name="subTitle"></slot> </h2> <button> <slot name="buttonText"></slot> </button> <p style="color: red;"> <slot><!-- デフォルトスロット --></slot><!-- slot name="default"でも同様 --> </p> </div> </template> slotのデフォルトコンテンツ(フォールバックコンテンツ) 子コンポーネントに用意されたスロットに,何も渡されなかった場合に表示されるコンテンツを,子コンポーネントであらかじめ記述することができます(公式ドキュメントではこのようなコンテンツのことをフォールバックコンテンツと呼んでいます).記述する方法は簡単で,小コンポーネントの<slot></slot>タグの間にあらかじめコンテンツを書いておくだけで良いです. <slot name="hogehoge">デフォルトコンテンツ</slot> こうすることでhogehogeスロットにコンテンツが渡されなかった場合には”デフォルトコンテンツ”という文字が表示され,親コンポーネントで何かしらのコンテンツをhogehogeスロットに渡すとそのコンテンツでslot内を書き換えます. スコープ付きスロット 親コンポーネントで小コンポーネントへ渡すテンプレートを記述する際に,子コンポーネントが持っているデータを用いて記述したい場合もあると思います.article.vueを以下のように書き換えてみます. article.vue <template> <div style="width=500px;"> <h1> <slot name="title"></slot> </h1> <h2> <slot name="subTitle"></slot> </h2> <button> <slot name="buttonText"></slot> </button> <p> <slot name="updatedAt"> {{ updatedAt.year }} </slot> </p> </div> </template> <script> export default { name: 'Article', data() { return { updatedAt: { year: '2021', month: '06', date: '22', }, } } } </script> 更新日時を差し込むスロットupdatedAtを用意しました.また,データとして子コンポーネントであるarticle.vueに更新日時のデータを持たせています.updatedAtでは,デフォルトコンテンツとして{{ updatedAt.year }}と記述しているので,親コンポーネントであるApp.vueからupdatedAtスロットにコンテンツを指定しなければ2021という数字が表示されます. App.vue <template> <Article> <template v-slot:title> タイトルです </template> <template v-slot:body> サブタイトルです </template> <template #buttonText> 次へ </template> </Article> </template> <script> import Article from './components/article.vue'; export default { name: 'App', components: { Article, }, } </script> ここで,更新日時のうち,年ではなく,月を表示させたい場合はどのようにすれば良いのでしょうか.実は,スコープ付きスロットを用いることでこれを実現できます.article.vueを以下のように書き換えてみます. article.vue <template> <div style="width=500px;"> <h1> <slot name="title"></slot> </h1> <h2> <slot name="subTitle"></slot> </h2> <button> <slot name="buttonText"></slot> </button> <p> <slot name="updatedAt" v-bind:slotProps="updatedAt"><!-- スコープ付きスロット --> {{ updatedAt.year }} </slot> </p> </div> </template> <script> export default { name: 'Article', data() { return { updatedAt: { year: '2021', month: '06', date: '22', }, } } } </script> このように,子コンポーネントでデータを親コンポーネントに渡したい時にv-bindで変数をバインドしてあげます.ここではupdatedAtというオブジェクトをslotPropsという変数でバインドしています(slotPropsは任意の変数名で可). App.vueは次のように書き換えます. App.vue <template> <Article> <template v-slot:title> タイトルです </template> <template v-slot:body> サブタイトルです </template> <template #buttonText> 次へ </template> <template v-slot:updatedAt="{ slotProps }"><!-- スコープ付きスロットから変数(オブジェクト)を取得 --> {{ slotProps.month }}<!-- デフォルトコンテンツをmonthを表示するように上書き --> </template> </Article> </template> <script> import Article from './components/article.vue'; export default { name: 'App', components: { Article, }, } </script> このように,呼び出し側でv-slotの値として変数を受け取ることでそのスコープ内で値を使ってコンテンツを記述することができます.当然ですが,他のスロットなどではこの変数は機能しません. 実行結果はこんな感じ. おわりに Vue.jsのslotについて解説しました.今回紹介した方法以外にも,記述のバリエーションがいくつかあるので,この記事と少し状況が違うなという方は公式のドキュメントを参照してみてください. 参考 Vue.js公式ドキュメント - Slot:https://jp.vuejs.org/v2/guide/components-slots.html
- 投稿日:2021-06-23T16:18:43+09:00
JavaScriptのコールバック関数
今ProgateでJavaScriptやってるんですけど、コールバック関数の 直接定義みたいなので、左の事前に定義した関数は理解できるんですけど、右の直接引数の中で定義してる方がいまいち理解に苦しみます 左のやつはこんな感じのイメージで理解してるんですけど 右のやつは引数としてcallbackにconsole.logが代入されても、 ちゃんと呼び出されない感覚があるのであります。 どんなイメージなら理解出来そうですか?
- 投稿日:2021-06-23T13:58:32+09:00
【JavaScript】js-cookieでcookieを操作する
インストール npmを使う場合は、 npm npm i js-cookie yarnを使う場合は、 yarn yarn add js-cookie 使用方法 使うファイルにimportします。 import import Cookies from 'js-cookie'; cookieに値を保存 cookiesに値を保存する Cookies.set('cookie_name', 'value') //7日後に有効期限が切れる、vokkiesを保存する Cookies.set('name', 'value', { expires: 7 }) //pathを指定して、それ以下のpathでのみ有効なcookieを保存する Cookies.set('name', 'value', { expires: 7, path: '' }) cookieに値を取得する cookieに値を取得する Cookies.get('name') // => 'value' //cookieで取得できるすべての値を取得する Cookies.get() // => { name: 'value' } ちなみに、指定したcookieが存在しない場合、undefindedを返します。 cookieで指定した値がない Cookies.get('hoge') //=> undefined cookieに値を削除する cookieに値を削除する Cookies.remove('name') 参考
- 投稿日:2021-06-23T13:50:55+09:00
[Webで物理エンジン?!] Matter.jsの紹介と導入
Matter.jsとは Matter.jsとはWebで実装できるJavaScriptの2次元物理エンジンライブラリです。 これを使えば今までとは違った新しいweb開発ができるでしょう。 以下のURLに他の方が作った素晴らしいアプリケーションがたくさんあります。ぜひ見てみてください。 https://codepen.io/collection/Fuagy/ 目次 1. 記事の対象者 2. 実装方法 3. 日本人にはとっつきづらい 4. 実装 5. この後の進め方 この記事の対象者 英語が苦手な方 Matter.jsを使ってみたい方 HTMLとJavaScriptの知識が最低限ある方 実装方法 私が色々調べながらたどり着いた最も良い方法は、公式のWikiを見て真似て実装することです。この記事では公式wikiをたどりながらどのように実装すればいいのか紹介したいと思います。 日本人にはとっつきづらい 公式を見るなんて当たり前じゃん。そう思う方も多いと思いますが、このMatter.jsというライブラリ、日本語での記事がほとんどありません。あったとしても情報が古く、あまり参考にはなりません。そこでこのMatter.jsについてどのように導入したらいいのかを紹介したいと思います。 実装 まずMatter.jsをインストールしましょう。公式からファイルをダウンロードして、buildフォルダから、「matter.js」または「matter.min.js」を、これから開発するフォルダ内に入れましょう。 出来たら公式Wikiに飛んで一緒に見ていきましょう。 URL: https://github.com/liabru/matter-js/wiki 流れ Getting started Rendering Getting started Getting startedから見ていきます。まずUsage example(使用例)のコードを見てください。 // module aliases var Engine = Matter.Engine, Render = Matter.Render, Runner = Matter.Runner, Bodies = Matter.Bodies, Composite = Matter.Composite; // create an engine var engine = Engine.create(); // create a renderer var render = Render.create({ element: document.body, engine: engine }); // create two boxes and a ground var boxA = Bodies.rectangle(400, 200, 80, 80); var boxB = Bodies.rectangle(450, 50, 80, 80); var ground = Bodies.rectangle(400, 610, 810, 60, { isStatic: true }); // add all of the bodies to the world Composite.add(engine.world, [boxA, boxB, ground]); // run the renderer Render.run(render); // create runner var runner = Runner.create(); // run the engine Runner.run(runner, engine); 一番上のコードだけ説明すると、それらは今後開発する上でいちいちMatter.Engine等書くのが面倒なので毎回最初に書く慣習だと思ってください。 上記のコードを保存してサイトを開くと四角形が上から降ってくると思います。そしたら成功です。 このコードの流れ 流れを説明します。やることは主に4つです。 1. エンジンを作る。 2. エンジンの世界を設定をする。 3. 剛体(物体)を作ってエンジンの世界に投下する。 4. エンジンを起動する これさえ押さえれば、Matter.jsの基本的なことはマスターしたと言ってもいいでしょう。 1. エンジンを作る // create an engine var engine = Engine.create(); これだけです。エンジンは世界のシミュレーションの更新を管理するコントローラです。とりあえずこれでエンジンを作るんだなって思っておいてください。これ以上はいじりません。 2. エンジンの世界を設定する // create a renderer var render = Render.create({ element: document.body, engine: engine }); renderを設定することでこの世界を変えることができます。つまりあなたはこの世界の神なのです 冗談は置いておいて説明します。これがこの世界を作るうえで重要なところです。 element まずこの世界をどこに配置するのか、どのHTMLの要素に入れるのかを、elementで決めることができます。初期設定ではbodyに入れるようになってますが以下のように変えることができます。 // create a renderer var render = Render.create({ element: document.getElementById('canvas'), engine: engine }); getElementByIdはhtmlにあるIdがcanvasの要素を取得しています。括弧内('canvas')は好きなように変更してください。 engine そしてengineで使うエンジンを設定しましょう。これは先ほど作ったengineですね。 options 実はrenderで重要なのはこの後で、さらにoptionsを追加して設定していきます。ですがこれは項目2のRenderingで説明します。 3. 剛体(物体)を作ってエンジンの世界に投下する。 var boxA = Bodies.rectangle(400, 200, 80, 80); var boxB = Bodies.rectangle(450, 50, 80, 80); var ground = Bodies.rectangle(400, 610, 810, 60, { isStatic: true }); // add all of the bodies to the world Composite.add(engine.world, [boxA, boxB, ground]); まず世界に投下したい物体を定義しましょう。 boxAでは四角形rectangle(x座標, y座標, 横の長さ, 縦の長さ) を設定しています。 groundでは isStaticというパラメータがあると思います。これは固定を意味します。このように中括弧にパラメータを設定することで、物体の特性を変えることができます。以下に例を一部紹介します。 var boxA= Bodies.rectangle( Math.floor(Math.random()*width), // x座標をランダムに設定 200, 40, 40, { isStatic: false, // 固定しない density: 1.5, // 質量 friction: 0, // 摩擦 restitution: 1.1, // 跳ね返り } ); Math.floor(Math.random()*width)のようにすればx座標をランダムにできます。このようにするとまた少し面白くなりますね。ちなみにwidthはご自身で適切なパラメータを指定してください。 そしてこれらの作った物体を世界に投下します。 // add all of the bodies to the world Composite.add(engine.world, [boxA, boxB, ground]); Compositeで世界を直接いじることができます。2つ目のパラメータに世界に投下したい物体を入れます。複数入れる場合は上記のように配列にして入れましょう。 Compositeで世界をいじれるということはaddだけでなくremove等もあります。以下に使用例を示します。 Composite.remove( engine.world, boxA ); これでこの世界からboxAは消えました。 4. エンジンを起動する // run the renderer Render.run(render); // create runner var runner = Runner.create(); // run the engine Runner.run(runner, engine); 実際に使用する場合はこのまま使ってくれれば問題ありません。 1, 3行目に関しては書いてある通りですが、renderとrunnerを実行します。2行目に関してはどのように走らせるか設定できます。 var runner = Runner.create({ delta: 1000 / 60, isFixed: false, enabled: true }); ここは本記事の内容とは少しずれるので扱いません。ただいじらなくても問題ないので、気になる方はパラメータを変えたりして遊んでみてください。 以上でGetting startedは終了です。ここまで理解すれば後はご自身で何でもできると思います。これ以降はある意味補足のような内容です。 Rendering 次に公式WikiのRenderingページを一緒に見ていきましょう。まず初めにUsageを見てください。コードは以下です。 // create a renderer var render = Render.create({ element: document.body, engine: engine options: options }); これは先ほど説明した「2. エンジンの世界を設定する」のコードにoptionsが追加されています。 これをいじることでエンジンの世界を変えることができます。以下にoptionsのパラメータを載せます。 var render = Render.create({ element: document.body, engine: engine, options: { width: 800, // 世界の横幅 height: 600, // 世界の縦幅 pixelRatio: 1, background: '#fafafa', wireframeBackground: '#222', hasBounds: false, enabled: true, wireframes: true, showSleeping: true, showDebug: false, showBroadphase: false, showBounds: false, showVelocity: false, showCollisions: false, showSeparations: false, showAxes: false, showPositions: false, showAngleIndicator: false, showIds: false, showShadows: false, showVertexNumbers: false, showConvexHulls: false, showInternalEdges: false, showMousePosition: false } }); 私も全ては把握しきれていないのですべては説明できません。いろいろいじって試してみてください。 1つ重要なこととしてbackgroundについて説明します。 background: 'transparent', 通常、エンジンを起動すると背景が真っ黒で嫌だと方もいると思います。そこでtransparentを指定することで背景を透明にできます。もしできなかったらwireframesを false にしてみてください。そこらへんはいろいろいじって確かめてみましょう。 おまけ, マウスコントロールを追加する マウスで物体をつかんでみましょう。以下がコードです。 var MouseConstraint = Matter.MouseConstraint, // 一番上に追加 Mouse = Matter.Mouse, // 同様 var mouse = Mouse.create(render.canvas), mouseConstraint = MouseConstraint.create(engine, { mouse: mouse, constraint: { render: { visible: false, // マウスを表示するかしないか } } }); // keep the mouse in sync with rendering render.mouse = mouse; Composite.add( engine.world, mouseConstraint ); 今までやってきた操作と同じです。mouseを定義して世界に追加します。これで物体をつかむことができるようになったはずです。 最後に ここまででMatter.jsの使い方はほぼマスターしたと言っていいでしょう。あとはJavaScriptとjQueryなどを駆使してオリジナルのゲーム等作ってみてください。 基本的にはWikiを参照すればたいていは載っています。以下に私が良く参考にするリンクを載せておきます。なにかに詰まった時はこれらを参考にしてみてください。 公式のexamples https://github.com/liabru/matter-js/tree/master/examples 他の方が作ったゲーム https://codepen.io/collection/Fuagy/ ここまでお疲れさまでした。ぜひ自分の手でアプリを作ってみてください。
- 投稿日:2021-06-23T13:50:55+09:00
[Webで物理エンジン?!] matter.jsの紹介と導入
Matter.jsとは Matter.jsとはWebで実装できる2次元物理エンジンのライブラリです。 これを使えば今までとは違った新しいweb開発ができるでしょう。 以下のURLに他の方が作った素晴らしいアプリケーションがたくさんあります。ぜひ見てみてください。 https://codepen.io/collection/Fuagy/ 目次 1. 記事の対象者 2. 実装方法 3. 日本人にはとっつきづらい 4. 実装 5. この後の進め方 この記事の対象者 英語が苦手な方 Matter.jsを使ってみたい方 HTMLとJavaScriptの知識が最低限ある方 実装方法 私が色々調べながらたどり着いた最も良い方法は、公式のWikiを見て真似て実装することです。この記事では公式wikiをたどりながらどのように実装すればいいのか紹介したいと思います。 日本人にはとっつきづらい 公式を見るなんて当たり前じゃん。そう思う方も多いと思いますが、このMatter.jsというライブラリ、日本語での記事がほとんどありません。あったとしても情報が古く、あまり参考にはなりません。そこでこのMatter.jsについてどのように導入したらいいのかを紹介したいと思います。 実装 まずMatter.jsをインストールしましょう。公式からファイルをダウンロードして、buildフォルダから、「matter.js」または「matter.min.js」を、これから開発するフォルダ内に入れましょう。 出来たら公式Wikiに飛んで一緒に見ていきましょう。 URL: https://github.com/liabru/matter-js/wiki 流れ Getting started Rendering Getting started Getting startedから見ていきます。まずUsage example(使用例)のコードを見てください。 // module aliases var Engine = Matter.Engine, Render = Matter.Render, Runner = Matter.Runner, Bodies = Matter.Bodies, Composite = Matter.Composite; // create an engine var engine = Engine.create(); // create a renderer var render = Render.create({ element: document.body, engine: engine }); // create two boxes and a ground var boxA = Bodies.rectangle(400, 200, 80, 80); var boxB = Bodies.rectangle(450, 50, 80, 80); var ground = Bodies.rectangle(400, 610, 810, 60, { isStatic: true }); // add all of the bodies to the world Composite.add(engine.world, [boxA, boxB, ground]); // run the renderer Render.run(render); // create runner var runner = Runner.create(); // run the engine Runner.run(runner, engine); 一番上のコードだけ説明すると、それらは今後開発する上でいちいちMatter.Engine等書くのが面倒なので毎回最初に書く慣習だと思ってください。 上記のコードを保存してサイトを開くと四角形が上から降ってくると思います。そしたら成功です。 このコードの流れ 流れを説明します。やることは主に4つです。 1. エンジンを作る。 2. エンジンの世界を設定をする。 3. 剛体(物体)を作ってエンジンの世界に投下する。 4. エンジンを起動する これさえ押さえれば、Matter.jsの基本的なことはマスターしたと言ってもいいでしょう。 1. エンジンを作る // create an engine var engine = Engine.create(); これだけです。エンジンは世界のシミュレーションの更新を管理するコントローラです。とりあえずこれでエンジンを作るんだなって思っておいてください。これ以上はいじりません。 2. エンジンの世界を設定する // create a renderer var render = Render.create({ element: document.body, engine: engine }); renderを設定することでこの世界を変えることができます。つまりあなたはこの世界の神なのです 冗談は置いておいて説明します。これがこの世界を作るうえで重要なところです。 element まずこの世界をどこに配置するのか、どのHTMLの要素に入れるのかを、elementで決めることができます。初期設定ではbodyに入れるようになってますが以下のように変えることができます。 // create a renderer var render = Render.create({ element: document.getElementById('canvas'), engine: engine }); getElementByIdはhtmlにあるIdがcanvasの要素を取得しています。括弧内('canvas')は好きなように変更してください。 engine そしてengineで使うエンジンを設定しましょう。これは先ほど作ったengineですね。 options 実はrenderで重要なのはこの後で、さらにoptionsを追加して設定していきます。ですがこれは項目2のRenderingで説明します。 3. 剛体(物体)を作ってエンジンの世界に投下する。 var boxA = Bodies.rectangle(400, 200, 80, 80); var boxB = Bodies.rectangle(450, 50, 80, 80); var ground = Bodies.rectangle(400, 610, 810, 60, { isStatic: true }); // add all of the bodies to the world Composite.add(engine.world, [boxA, boxB, ground]); まず世界に投下したい物体を定義しましょう。 boxAでは四角形rectangle(x座標, y座標, 横の長さ, 縦の長さ) を設定しています。 groundでは isStaticというパラメータがあると思います。これは固定を意味します。このように中括弧にパラメータを設定することで、物体の特性を変えることができます。以下に例を一部紹介します。 var boxA= Bodies.rectangle( Math.floor(Math.random()*width), // x座標をランダムに設定 200, 40, 40, { isStatic: false, // 固定しない density: 1.5, // 質量 friction: 0, // 摩擦 restitution: 1.1, // 跳ね返り } ); Math.floor(Math.random()*width)のようにすればx座標をランダムにできます。このようにするとまた少し面白くなりますね。ちなみにwidthはご自身で適切なパラメータを指定してください。 そしてこれらの作った物体を世界に投下します。 // add all of the bodies to the world Composite.add(engine.world, [boxA, boxB, ground]); Compositeで世界を直接いじることができます。2つ目のパラメータに世界に投下したい物体を入れます。複数入れる場合は上記のように配列にして入れましょう。 Compositeで世界をいじれるということはaddだけでなくremove等もあります。以下に使用例を示します。 Composite.remove( engine.world, boxA ); これでこの世界からboxAは消えました。 4. エンジンを起動する // run the renderer Render.run(render); // create runner var runner = Runner.create(); // run the engine Runner.run(runner, engine); 実際に使用する場合はこのまま使ってくれれば問題ありません。 1, 3行目に関しては書いてある通りですが、renderとrunnerを実行します。2行目に関してはどのように走らせるか設定できます。 var runner = Runner.create({ delta: 1000 / 60, isFixed: false, enabled: true }); ここは本記事の内容とは少しずれるので扱いません。ただいじらなくても問題ないので、気になる方はパラメータを変えたりして遊んでみてください。 以上でGetting startedは終了です。ここまで理解すれば後はご自身で何でもできると思います。これ以降はある意味補足のような内容です。 Rendering 次に公式WikiのRenderingページを一緒に見ていきましょう。まず初めにUsageを見てください。コードは以下です。 // create a renderer var render = Render.create({ element: document.body, engine: engine options: options }); これは先ほど説明した「2. エンジンの世界を設定する」のコードにoptionsが追加されています。 これをいじることでエンジンの世界を変えることができます。以下にoptionsのパラメータを載せます。 var render = Render.create({ element: document.body, engine: engine, options: { width: 800, // 世界の横幅 height: 600, // 世界の縦幅 pixelRatio: 1, background: '#fafafa', wireframeBackground: '#222', hasBounds: false, enabled: true, wireframes: true, showSleeping: true, showDebug: false, showBroadphase: false, showBounds: false, showVelocity: false, showCollisions: false, showSeparations: false, showAxes: false, showPositions: false, showAngleIndicator: false, showIds: false, showShadows: false, showVertexNumbers: false, showConvexHulls: false, showInternalEdges: false, showMousePosition: false } }); 私も全ては把握しきれていないのですべては説明できません。いろいろいじって試してみてください。 1つ重要なこととしてbackgroundについて説明します。 background: 'transparent', 通常、エンジンを起動すると背景が真っ黒で嫌だと方もいると思います。そこでtransparentを指定することで背景を透明にできます。もしできなかったらwireframesを false にしてみてください。そこらへんはいろいろいじって確かめてみましょう。 おまけ, マウスコントロールを追加する マウスで物体をつかんでみましょう。以下がコードです。 var MouseConstraint = Matter.MouseConstraint, // 一番上に追加 Mouse = Matter.Mouse, // 同様 var mouse = Mouse.create(render.canvas), mouseConstraint = MouseConstraint.create(engine, { mouse: mouse, constraint: { render: { visible: false, // マウスを表示するかしないか } } }); // keep the mouse in sync with rendering render.mouse = mouse; Composite.add( engine.world, mouseConstraint ); 今までやってきた操作と同じです。mouseを定義して世界に追加します。これで物体をつかむことができるようになったはずです。 最後に ここまででMatter.jsの使い方はほぼマスターしたと言っていいでしょう。あとはJavaScriptとjQueryなどを駆使してオリジナルのゲーム等作ってみてください。 基本的にはWikiを参照すればたいていは載っています。以下に私が良く参考にするリンクを載せておきます。なにかに詰まった時はこれらを参考にしてみてください。 公式のexamples https://github.com/liabru/matter-js/tree/master/examples 他の方が作ったゲーム https://codepen.io/collection/Fuagy/ ここまでお疲れさまでした。ぜひ自分の手でアプリを作ってみてください。
- 投稿日:2021-06-23T13:32:02+09:00
非同期処理の基本的な制御
非同期処理の動作を確認 setTimeout関数で簡単な非同期な関数を確認することができます。 // setTimeout:第一引数の関数を、第二引数のミリ秒後に実行 // 2秒後に'Hello'を出力 const outputHello = () => { setTimeout(() => { console.log('Hello'); }, 2000); } // 1秒後に'World'を出力 const outputWorld = () => { setTimeout(() => { console.log('World!'); }, 1000); } outputHello(); outputWorld(); 実行結果 前ページのコードをブラウザのコンソールに貼り付けて実行します。 出力結果は期待通りだったでしょうか? 先に書いたsetTimeoutのコールバック関数の実行より前に、後ろで書いたsetTimeoutのコールバック関数が実行されています。 非同期処理制御の必要 非同期処理が絡むと、単純に順番に書いただけでは、想定する順番で動かないことがあります。 例えば、サーバAPIでJSONデータをフェッチした後に、ローカル変数に代入する場合は、レスポンスが返ってくる前に、代入の処理に進んでしまいます。 コールバック(非推奨) 非同期処理の古典的な実装にコールバックを段々と積み上げることで順序保障する方法があります。 例外処理などを書いていくと、コード見通しが非常に悪くなるので、現在ほぼ使う機会はないかと思います。 // 2秒後に'Hello'を出力 const outputHello = (callback) => { setTimeout(() => { console.log('Hello'); callback(); // コールバック関数を実行 }, 2000); } // 1秒後に'World'を出力 const outputWorld = () => { setTimeout(() => { console.log('World!'); }, 1000); } outputHello(outputWorld); // outputWorld関数をoutputHello内で実行する Promise JavaScript標準でpromiseという非同期処理を制御するためのものが用意されています。 後述するasync/awaitなどもフロントエンドの非同期処理の基盤となっている機能です。 Promiseに書き換え さきほどのsetTimeoutの関数をPromiseで扱えるようにします。 API通信時はaxiosなどライブラリを使うことが多いので、実業務でPromiseを返す関数を作成することは少ないかと思います。 // 2秒後に'Hello'を出力 const outputHello = () => { return new Promise(function(resolve, reject) { setTimeout(() => { // 乱数(0〜10)が3未満の場合はreject Math.random() *10 > 3 ? resolve(console.log('Hello')) : reject(Error('outputHelloでエラー発生')); }, 2000); }); } // 1秒後に'World'を出力 const outputWorld = () => { return new Promise(function(resolve, reject) { setTimeout(() => { // 乱数(0〜10)が3未満の場合はreject Math.random() *10 > 3 ? resolve(console.log('World!')) : reject(Error('outputWorldでエラー発生')); }, 1000); }); } outputHello() .then(outputWorld) .catch((e) => { console.log(e.message); }) .finally(()=>{ console.log('処理終了'); }); Promiseの状態 Promiseを返却する関数は3つ状態を持ちます。 pending resolveもrejectも実行していない状態。例えば。APIサーバに投げたりクエストが返ってきていない状態など。 fulfill resolveが実行されて、正常に処理が完了した状態。thenでチェインしている場合、次の関数に進む。 reject rejectが実行された状態。基本的に例外が発生した場合にrejectとなる。thenでチェインしている場合でも、次の関数に進まず、catchメソッドに飛ぶ。 便利メソッド(Promise.all) 複数のPromiseを返す関数を一斉に走らせて、全ての関数のPromiseの状態がfullfillになるとthenを実行する。 それぞれの非同期処理の順序はどうでも良いが、全て完了したあとに処理を行いたい際に使う。 ※1つでもrejectがあると、catchを実行する。 Promise.all([outputHello(), outputWorld()]) .then(()=> { // all fulfill }) .catch((e)=> { // exist reject }); たいていの処理がPromise.thenとPromise.allで対応できますが、必要に応じて他のメソッドも検討します。 async/await Promiseは非同期処理をコールバックを使わずに記述できますが、async/awaitを使うことで、さらに簡略化できます。 awaitの役割 awaitを付けるとPromiseの結果が返却されるまで処理を待機します。 thenでチェインする必要なく記述した順序で実行され、Promiseの結果を待ちます。 後述するasyncを付けた関数内でしか使用できません。try-catchで例外を捕捉することができます。 try { await outputHello(); await outputWorld(); } catch (e) { console.log(e.message) } finally { console.log('処理終了') } asyncの役割 asyncを付けた関数(async function)がPromiseを返すようになる。 async function内でreturnすると、resolveを実行する async function内でthrowすると、rejectを実行する 要するに非同期処理をあまり意識せずに、returnしたら正常終了、throwしたら例外発生とすることができる。 asyncのサンプルコード // 2秒後に'Hello'を出力 const outputHello = () => { return new Promise(function(resolve, reject) { setTimeout(() => { // 乱数(0〜10)が3未満の場合はreject Math.random() *10 > 3 ? resolve(console.log('Hello')) : reject(Error('outputHelloでエラー発生')); }, 2000); }); } // 1秒後に'World'を出力 const outputWorld = () => { return new Promise(function(resolve, reject) { setTimeout(() => { // 乱数(0〜10)が3未満の場合はreject Math.random() *10 > 3 ? resolve(console.log('World!')) : reject(Error('outputWorldでエラー発生')); }, 1000); }); } const sequence = async () => { try { await outputHello(); await outputWorld(); } catch (e) { throw e; } finally { console.log('sequence処理終了') } } // asyncをつけるとPromiseを返す関数となるので、Promiseのメソッドが使える! sequence() .then(sequence) .catch((e)=> console.log(e.message)) .finally( ()=> console.log('処理終了')); おまけ 課題1 コンソール出力内容は? setTimeout(function() { console.log('A'); }, 5000); setTimeout(function() { console.log('B'); }, 4000); setTimeout(function() { console.log('C'); }, 3000); setTimeout(function() { console.log('D'); }, 2000); setTimeout(function() { console.log('E'); }, 1000); 課題2 コンソール出力内容は? setTimeout(function() { console.log('A'); setTimeout(function() { console.log('B'); setTimeout(function() { console.log('C'); setTimeout(function() { console.log('D'); setTimeout(function() { console.log('E'); }, 1000); }, 2000); }, 3000); }, 4000); }, 5000); 課題3 コンソールにHelloと出力されましたが、続いてWorldが出力されません。 const outputHello = () => { return new Promise(function(resolve, reject) { setTimeout(() => { console.log('Hello'); }, 2000); }); } const outputWorld = () => { return new Promise(function(resolve, reject) { setTimeout(() => { console.log('World!'); }, 1000); }); } outputHello() .then(outputWorld) .catch((e) => { console.log(e.message); }) .finally(()=>{ console.log('処理終了'); }); 課題4 asyncの機能の認識を間違って実装している部分があります。指摘してください。 // httpリクエストを発行する関数 public async executeRequest ():Promise<any> { if (!this.isValid) { return new Promise(() => { throw new InvalidError('無効なリクエストです。'); }); } // axiosを使ってJSONデータをリクエスト return axios.request(this.config) .then((res) => { return res.data; }) .catch((e) => { throw new Error(e.message); });
- 投稿日:2021-06-23T13:19:41+09:00
TypeScript①(基礎)
はじめに この記事は私がTypeScriptについて学んだ内容を TypeScript①(基礎) TypeScript②(型の機能) TypeScript③(ユーティリティタイプ) の3回に分けて網羅的に紹介していきます。 ①(基礎)では導入方法と様々な型について簡単な紹介 ②(型の機能)では型が実際にどういう働きをするのかについて ③(ユーティリティタイプ)では型変換を容易にするユーティリティ型について 参考リンク TypeScript公式ドキュメント サバイバルTypeScript 〜実務で使うなら最低限ここだけはおさえておきたいTypeScript入門〜 TypeScript Deep Dive 日本語版 実践TypeScript ~ BFFとNext.js&Nuxt.jsの型定義~ 単行本 Typescriptの導入 TypeScriptのインストール npm install -g typescript TypeScriptを使用するプロジェクトフォルダを作成し移動したら 以下のコマンドでtsconfig.jsonを作成 tsc --init 以上でTypeScriptの導入できます。 tsconfig.jsonによる型チェックの厳密さの調整 初期状態のstrict: trueの指定では以下のルールが一括で有効になっています。 alwaysStrict noImplicitAny noImplicitThi strictBindCallApply strictFunctionTypes strictNullChecks strictPropertyInitialization tscong.jsonに追加ルールを指定して型チェックを弱くする 暗黙的なany型を許可 "noImplicitAny": false null型に他のプリミティブ型の割り当てを許可 "strictNullChecks": false 状況によって上記のようにfalse指定することによって型チェックを弱める事ができます。 ただし、これはJavaScriptプロジェクトを段階的にTypeScriptに移行するために許容されるべきであり、TypeScriptを利用する目的から外れてしまわないように注意が必要です。 基本の型 プリミティブ型 string, number, boolean, symbol, bigint, null, undefinedがあります。 リテラル型 プリミティブ型を細分化したのがリテラル型で、エディターにベタ書きした文字列や数字や真偽値をリテラルを言います。 boolean型 falseまたはtureの値を持つ事ができます。 let flag = true; number型 16進数、10進数、8進数、2進数をサポートしています。 let decimal: number = 42; let hex: number = oxee; let octal: number = 0o213; let binary: number = 0b1001; string型 文字列を扱います。またダブルクウォート(")、シングルクウォート(')、バッククウォート(`)で囲います。 let name: string = "Jacob"; name = 'Michael'; let myName: string = `my name is ${name}`; array型 配列型の記述方法は以下の2つがあります。 配列に入る値の型の後ろにブラケット[]をつける。 let data: number[] = [1, 2, 3]; Array型で記述する。 let data: Array<number> = [1, 2, 3]; tuple型 配列内の型を指定できます。 let x: [string, number]; x = ["hello", 3]; // OK x = [3, "hello"]; // Error x = ["hello", 3, 0]; // Error any型 any型はどのような値も代入する事ができ、型が不定の時に使われます。 またany型は型チェックを無効にし、コンパイルを通過させることができるので、不定の型でもコンパイルを通してしまう危険な型とも言えます。 unknown型 unknown型はany型と似ていて、どのような値も代入する事ができ、型が不定の時に使われます。 any型と異なる点は、コンパイルを通さないという事です。そのため型安全なanyと呼ばれることもあります。 void型 void型はany型とは逆にundefinedとnull以外代入することができません。 戻り値を持たない関数の型として利用されます。 null型/undefined型 全ての型はnullまたはundefiendを代入する事ができ、null型、undefiend型というサブタイプの型を得ることができます。 また、nullは意図的にnullを定義した場合にだけ使う事ができ、undefinedは意図的に限らず 変数に何も代入されていない状態を表す。 never型 never型は発生し得ない型を表します。 function log(text: string): never { throw new Error(text); } function roop(): never { while (true) {} } object型 object型はプリミティブ以外の値の型で、引数にオブジェクトのみ受け取る関数に使用します。 const obj = { foo: 'foo' }; const obj2: {} = obj; 高度な型 Intersection Types IntersectionTypesは複数の型をアンバサンド&で結合した型です。 type Address = { City: "Seattle"; state: "WA"; }; type Name = { first: "Matthew"; last: "Smith"; }; type Profile = Address & Name; Union Types 複数の型のうち一つが当てはまることをします場合に使用します。 let data: number | string | boolean; クラス TypeScriptではクラスのインスタンス化をする際、受け取った引数をコンストラクターからメンバー変数に代入します。 class Point { x: number; y: number; constructor(x: number, y: number) { this.x = x; this.y = y; } } const coordinates = new Point(3, 0); 継承 継承(extends)すると継承元のすべてのプロパティとメソッドを受け継ぎ、受け継いだ先で、呼び出しと、追加のメンバーの定義をする事ができます。 class Animal { run() { console.log("走る!"); } } class Dog extends Animal { woof(times: number) { for (let i = 0; i < times; i++) { console.log("ワン!"); } } } const d = new Dog(); d.run(); d.woof(3); // 結果 // 走る! // ワン! // ワン! // ワン! superを使って継承元のプロパティを上書きする事ができます。 class Base { greet() { console.log("こんにちは、いい天気ですね。"); } } class Derived extends Base { greet(name?: string) { if (name === undefined) { super.greet(); } else { console.log(`こんにちは! ${name.toUpperCase()}`); } } } const d = new Derived(); d.greet(); // こんにちは、いい天気ですね。 d.greet("ウィルさん!"); // こんにちは! ウィルさん! クラスメンバー修飾子(public・private・protected) publicはどこででも参照・実行が可能 privateは同一のクラスでのみ参照・実行が可能 protectedは継承先でのみ参照・実行が可能 同一クラス内での検証 class Base { public name1: string; protected name2: string; private name3: string; constructor() { this.name1 = "Ichiro"; this.name2 = "Jiro"; this.name3 = "Saburo"; } greet1() { console.log(`こんにちは! ${this.name1}`); // OK } greet2() { console.log(`こんにちは! ${this.name2}`); // OK } greet3() { console.log(`こんにちは! ${this.name3}`); // OK } } const human = new Base(); console.log(human.name1); // Ichiro console.log(human.name2); // error console.log(human.name3); // error human.greet1(); // こんにちは! Ichiro human.greet2(); // こんにちは! Jiro human.greet3(); // こんにちは! Saburo 継承先での検証 class Base { public name1: string; protected name2: string; private name3: string; constructor() { this.name1 = "Ichiro"; this.name2 = "Jiro"; this.name3 = "Saburo"; } } class Derived extends Base { greet01() { console.log(`こんにちは! ${this.name1}`); // OK (public) } greet02() { console.log(`こんにちは! ${this.name2}`); // OK (protected) } greet03() { console.log(`こんにちは! ${this.name3}`); // error (private) } } const human = new Derived(); human.greet01(); // こんにちは! Ichiro human.greet02(); // こんにちは! Jiro human.greet03(); // error 列挙型(Enum 列挙型は、関連する値の集合を編成する方法です。 また数値列挙は自動で0から順に割り振られ、代入した時点から再度割り振られます。 enum Color { red, white, green, blue, black = 2, pink, yellow, } console.log(Color); // 結果 // { // '0': 'red', // '1': 'white', // '2': 'black', // '3': 'pink', // '4': 'yellow', // red: 0, // white: 1, // green: 2, // blue: 3, // black: 2, // pink: 3, // yellow: 4 // } 文字列列挙は、数字の自動割り当てはありませんが、別ブロックからもプロパティーを追加することが可能です。 enum Env { USER_API = "abcd", USER_KEY = "ABCD", APP_ID = "0123", } console.log(Env); // { // USER_API: 'abcd', // USER_KEY: 'ABCD', // APP_ID: '0123' // } enum Env { PUBLIC_KEY = "abcd0123", } console.log(Env); // { // USER_API: 'abcd', // USER_KEY: 'ABCD', // APP_ID: '0123', // PUBLIC_KEY: 'abcd0123' // } TypeScript②(型の機能)へ 関連記事 TypeScript③(ユーティリティタイプ)
- 投稿日:2021-06-23T12:49:09+09:00
InDesign スクリプト 変数を削除
変数を削除するスクリプトは、これで良いのかな・・・? /* 更新 2021/6/22 */ // アプリ指定 #target "indesign"; // スクリプト名 var scriptName = "変数を削除"; //スクリプトの動作指定(一つのアンドゥ履歴にする、及び、アンドゥ名) app.doScript(function () { // ダイアログ var dialogueFlg = confirm("インスタンスを持たないカスタムテキスト変数を削除します。","", scriptName); // Noの場合 if (dialogueFlg == false) { // 終了 exit(); } // 削除した数 var removeNumber = 0; // テキスト変数の数だけ逆に繰り返す for(var i = app.activeDocument.textVariables.count() - 1; i >= 0; i--){ // カスタムテキスト変数の場合 if(app.activeDocument.textVariables.item(i).variableType == VariableTypes.CUSTOM_TEXT_TYPE){ // インスタンスがない場合 if(app.activeDocument.textVariables.item(i).associatedInstances.length == 0){ // 削除 app.activeDocument.textVariables.item(i).remove(); // 数を増やす removeNumber++; } } } // 結果を表示 alert("削除数 " + removeNumber, scriptName); //スクリプトの動作指定の続き }, ScriptLanguage.JAVASCRIPT, [scriptName], UndoModes.ENTIRE_SCRIPT, scriptName);
- 投稿日:2021-06-23T12:04:36+09:00
【nhk.or.jp】番組紹介にあるプレイリストの曲をSpotifyで検索するためのブックマークレット
はじめに よくラジオでNHK-FMの音楽番組を聴きます。 先日ふと見ると、過去に放送されたプレイリストがウェブサイトに掲載されていることに気づきました。 その中の気に入った曲をSpotifyのプレイリストに簡単に追加できたら嬉しいかも! 普段Spotifyを使う音楽ファンの私はそう思いました。 皆さんは音楽配信サービスを使っていますか? そういうわけで今回紹介するのは「楽曲検索用ボタンを追加するブックマークレット」です。 コード javascript:(()=>{const r=[],o=document.querySelector(".program-onair-music li"),e=o.innerHTML.split(/<br><br>/);for(const o of e){const e=o.split("<br>");r.push('<a href="//open.spotify.com/search/'+encodeURIComponent(e[0].slice("1","-1")+" "+e[1].replace(/:作曲/,""))+'" style="background-color:#fff;padding:3px" target="_blank">🎵</a> 「'+e[0].slice("1","-1")+"」<br>"+e[1]),e[2]&&r.push("<br>"+e[2]),e[3]&&r.push("<br>"+e[3]),e[4]&&r.push("<br>"+e[4]),r.push("<br><br>")}o.innerHTML=r.join("")})(); 使い方 デスクトップでNHKラジオ番組案内の過去の放送(例えば https://www4.nhk.or.jp/jazz/5/ )を開く。 お好みの日付をクリック。 ブックマークレットを実行する。 プレイリストの各曲の先頭に追加された♫をクリックする。 解説 上記のコードは以下のコードを圧縮したものです。 ( () => { // --------- const html_data = [];// for update // playlist const li = document.querySelector('.program-onair-music li'); const list_item = li.innerHTML.split(/<br><br>/); for (const elm of list_item) { const playlist = elm.split('<br>'); // playlist[0]: song name // playlist[1]: artist name // playlist[2]: time // playlist[3]: product code html_data.push( '<a href="//open.spotify.com/search/' + encodeURIComponent(playlist[0].slice('1','-1') + ' ' + playlist[1].replace(/:作曲/, '')) + '" style="background-color:#fff;padding:3px" target="_blank">🎵</a> 「' + playlist[0].slice('1','-1') + '」<br>' + playlist[1] ); if (playlist[2]) html_data.push('<br>' + playlist[2]); if (playlist[3]) html_data.push('<br>' + playlist[3]); if (playlist[4]) html_data.push('<br>' + playlist[4]); html_data.push('<br><br>'); } // html update li.innerHTML = html_data.join(''); // --------- } )(); javascriptのコードを見る前に、nhk.or.jp のhtmlを確認しときましょう。プレイリストの該当部分はざっと次のようになっています。「曲名」の先頭に検索用リンクボタン♫を追加するのが最終目標です。 <div class='program-onair-music'> <ul><li> 「曲名」<br> アーティスト<br> 演奏時間<br> 音源<br><br> 「曲名」<br> アーティスト<br> 演奏時間<br> 音源<br><br> 「曲名」<br> アーティスト<br> 演奏時間<br> 音源<br><br> ... </li></ul> </div> 1つの<li>の中に全曲が記述され、曲と曲は<br>を連打して分けられているので、どうにも困ります。 これを踏まえてjavascriptを考えます。ちょっと面倒ですが、この場合<li>内の文字列を<br><br>で分割し、これをさらに<br>で分割すると全ての曲のデータが得られます。以上が曲情報の取得部分です。 次にhtmlを組み立てます。<li>内を一度に置換するやり方でいきます。他にいい方法を思いつきません。 予め、置換用htmlを保存する配列html_dataを用意し、次の順番で曲に関するデータを追加していきます。 ♫(曲名とアーティストで検索するためのリンクを設定している) 「曲名」: playlist[0] アーティスト: playlist[1] 演奏時間: playlist[2] 音源: playlist[3] 元のhtmlに倣って<br>を挟みながら、これを全曲分繰り返します(番組によって配列playlistの長さは変わり、その項目も異なります)。最後に配列html_dataの要素を連結して、現在のプレイリストとまるごと置き換えて完成です! あとがき 一部の番組紹介に関しては明らかにDOMの構成が異なるので、コードの修正が必要です。形式が統一されていないのが残念です。 最近はプレイリストを公開しているFM局が多いです。今度は、そちらの方の攻略を目指したいと思います。 本日もありがとうございました。 参考 https://qiita.com/embokoir/items/d667a6802105b842fb48
- 投稿日:2021-06-23T12:01:32+09:00
Railsアプリのリンクにショートカットキーを割り当てたいときに絶対に実現できる記事
shortcuts.jsという、アプリに自身で決めたショートカットキーを指定することができるjsのファイルが配布されているが、 どの記事もアラートを出すような内容だったから、今回はRailsのlink_toをクリックするときのショートカットキーを作成していく。 【ステップ1】shortcuts.jsをゲット http://www.openjs.com/scripts/events/keyboard_shortcuts/index.php このサイトのCodeというセクションでjsのコードが表示されるリンクがあるから、そこからコードをゲットする。 その後に、そのコードをペーストしたshortcuts.jsを作成する。 一応コードも載せておく。 shortcuts.js shortcut = { 'all_shortcuts':{},//All the shortcuts are stored in this array 'add': function(shortcut_combination,callback,opt) { //Provide a set of default options var default_options = { 'type':'keydown', 'propagate':false, 'disable_in_input':false, 'target':document, 'keycode':false } if(!opt) opt = default_options; else { for(var dfo in default_options) { if(typeof opt[dfo] == 'undefined') opt[dfo] = default_options[dfo]; } } var ele = opt.target; if(typeof opt.target == 'string') ele = document.getElementById(opt.target); var ths = this; shortcut_combination = shortcut_combination.toLowerCase(); //The function to be called at keypress var func = function(e) { e = e || window.event; if(opt['disable_in_input']) { //Don't enable shortcut keys in Input, Textarea fields var element; if(e.target) element=e.target; else if(e.srcElement) element=e.srcElement; if(element.nodeType==3) element=element.parentNode; if(element.tagName == 'INPUT' || element.tagName == 'TEXTAREA') return; } //Find Which key is pressed if (e.keyCode) code = e.keyCode; else if (e.which) code = e.which; var character = String.fromCharCode(code).toLowerCase(); if(code == 188) character=","; //If the user presses , when the type is onkeydown if(code == 190) character="."; //If the user presses , when the type is onkeydown var keys = shortcut_combination.split("+"); //Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked var kp = 0; //Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken var shift_nums = { "`":"~", "1":"!", "2":"@", "3":"#", "4":"$", "5":"%", "6":"^", "7":"&", "8":"*", "9":"(", "0":")", "-":"_", "=":"+", ";":":", "'":"\"", ",":"<", ".":">", "/":"?", "\\":"|" } //Special Keys - and their codes var special_keys = { 'esc':27, 'escape':27, 'tab':9, 'space':32, 'return':13, 'enter':13, 'backspace':8, 'scrolllock':145, 'scroll_lock':145, 'scroll':145, 'capslock':20, 'caps_lock':20, 'caps':20, 'numlock':144, 'num_lock':144, 'num':144, 'pause':19, 'break':19, 'insert':45, 'home':36, 'delete':46, 'end':35, 'pageup':33, 'page_up':33, 'pu':33, 'pagedown':34, 'page_down':34, 'pd':34, 'left':37, 'up':38, 'right':39, 'down':40, 'f1':112, 'f2':113, 'f3':114, 'f4':115, 'f5':116, 'f6':117, 'f7':118, 'f8':119, 'f9':120, 'f10':121, 'f11':122, 'f12':123 } var modifiers = { shift: { wanted:false, pressed:false}, ctrl : { wanted:false, pressed:false}, alt : { wanted:false, pressed:false}, meta : { wanted:false, pressed:false} //Meta is Mac specific }; if(e.ctrlKey) modifiers.ctrl.pressed = true; if(e.shiftKey) modifiers.shift.pressed = true; if(e.altKey) modifiers.alt.pressed = true; if(e.metaKey) modifiers.meta.pressed = true; for(var i=0; k=keys[i],i<keys.length; i++) { //Modifiers if(k == 'ctrl' || k == 'control') { kp++; modifiers.ctrl.wanted = true; } else if(k == 'shift') { kp++; modifiers.shift.wanted = true; } else if(k == 'alt') { kp++; modifiers.alt.wanted = true; } else if(k == 'meta') { kp++; modifiers.meta.wanted = true; } else if(k.length > 1) { //If it is a special key if(special_keys[k] == code) kp++; } else if(opt['keycode']) { if(opt['keycode'] == code) kp++; } else { //The special keys did not match if(character == k) kp++; else { if(shift_nums[character] && e.shiftKey) { //Stupid Shift key bug created by using lowercase character = shift_nums[character]; if(character == k) kp++; } } } } if(kp == keys.length && modifiers.ctrl.pressed == modifiers.ctrl.wanted && modifiers.shift.pressed == modifiers.shift.wanted && modifiers.alt.pressed == modifiers.alt.wanted && modifiers.meta.pressed == modifiers.meta.wanted) { callback(e); if(!opt['propagate']) { //Stop the event //e.cancelBubble is supported by IE - this will kill the bubbling process. e.cancelBubble = true; e.returnValue = false; //e.stopPropagation works in Firefox. if (e.stopPropagation) { e.stopPropagation(); e.preventDefault(); } return false; } } } this.all_shortcuts[shortcut_combination] = { 'callback':func, 'target':ele, 'event': opt['type'] }; //Attach the function with the event if(ele.addEventListener) ele.addEventListener(opt['type'], func, false); else if(ele.attachEvent) ele.attachEvent('on'+opt['type'], func); else ele['on'+opt['type']] = func; }, //Remove the shortcut - just specify the shortcut and I will remove the binding 'remove':function(shortcut_combination) { shortcut_combination = shortcut_combination.toLowerCase(); var binding = this.all_shortcuts[shortcut_combination]; delete(this.all_shortcuts[shortcut_combination]) if(!binding) return; var type = binding['event']; var ele = binding['target']; var callback = binding['callback']; if(ele.detachEvent) ele.detachEvent('on'+type, callback); else if(ele.removeEventListener) ele.removeEventListener(type, callback, false); else ele['on'+type] = false; } } 【ステップ2】shortcuts.jsをRailsアプリのディレクトリに入れる app/javascript/の配下にshortcuts.jsを置く。 自分の場合は、分かりやすいように app/javascript/shortcuts/shortcuts.jsというように shortcutsフォルダを作成して、その下にshortcuts.jsを置いた。 【ステップ3】application.jsでshortcuts.jsを読み込む app/javascript/packs/application.jsに以下を追記する。 application.js require ('shortcuts/shortcuts') これで下準備は完了 【ステップ4】ショートカットキーを割り当てたいリンクにidをつける shortcuts.jsとlink_toを結びつけるには同じidで連携する必要がある。 ショートカットキーを割り当てたいlink_toやbtnにidをつける。 下の例では、オプションの一番最後に追記した。 sample.html.slim / link_toの場合 = link_to '追加', new_task_path, remote: true, class: "nav-link btn", :style=>"color:#6558F5; border: 1px solid #6558F5;", id: 'task-add-shortcut' 【ステップ5】ショートカット作成のjsのコードを作成する 先ほど作成したshortcuts.jsのコードの下に追記する。 大まかなコードの書き方は決まり文句である。中身を変更したら別の処理が作れる。 shortcut.js // タスク追加ショートカット shortcut.add("Ctrl+N",function() { var a = document.getElementById("task-add-shortcut"); a.click(); },{ 'type':'keydown', 'propagate':true, 'target':document }); ・addの横の第一引数に割り当てたいショートカットキーと指定。 ・getElementByIdの引数に割り当てたい先程のリンク(link_toなど)のidを指定。 工程は以上。 ページをリロードして、ショートカットを試してみてください。
- 投稿日:2021-06-23T11:15:15+09:00
Node.jsのnpxでBusiness Card(電子名刺)を作る方法
はじめに 先日、仕事でご一緒した方が、npxを使ったBusiness Cardで自己紹介をされていて、素敵だな〜と思ったので、真似させていただき作ってみました!! デモ まず、早速デモをご紹介します。 ターミナルで npx misa335 と入力してみてください。 このように表示されるはずです! `♥・*:.。 。.:*・゚♡・*:.。 。.:*・゚♡・*:.。 。.:*・゚♡・*:.。 。.:*・゚♡・*:.。 。.:*・゚♥ Misako Kondoh GitHub: https://github.com/misa335 Twitter JP: https://twitter.com/misas2pink Twitter EN: https://twitter.com/__misakd Qiita: https://qiita.com/__misakd LinkedIn: https://www.linkedin.com/in/misa-k-engineer/ Please contact me with one of channels above ;D ♥・*:.。 。.:*・゚♡・*:.。 。.:*・゚♡・*:.。 。.:*・゚♡・*:.。 。.:*・゚♡・*:.。 。.:*・゚♥` npmパッケージのリンク https://www.npmjs.com/package/misa335 作り方 ① プロジェクトの作成 npm initで新規プロジェクトを作成します。 ② package.jsonの編集 package.jsonを編集して、npx <your-project-name>を実行すると、npmパッケージを落として、package.jsonに記載された"main": "index.js"が実行されるように設定します。 package.json { ... "main": "index.js", "bin": { "misa335": "index.js" }, ... ③ Business Cardの作成 Business Cardとして表示させたい項目を、index.jsのconsole.log内に記載します。 また、1行目に#!/usr/bin/env nodeと、nodeのインタプリタを実行する記述をします。 index.js #!/usr/bin/env node console.log(' Business Cardとして表示する項目 '); ローカルでBusiness Cardが表示されるかどうか、 node index.jsで確認することができます。 ④ Githubにpushする 任意のGithubレポジトリにpushします。 npmに公開する方法 ① npmアカウントの作成 https://www.npmjs.com/ で、npmアカウントを作成します。 ② npmへのログイン ターミナルでnpm loginと打ち、Username, Password, Email Addressを順に入力してnpmにログインします。 ③ プロジェクトの公開 npm publish --access publicと入力すると、npm上でプロジェクトが公開されます。 最後に、<npx your-project-name>を試して、Business Cardが表示されるか確認します。 おわりに 簡単にnpxを使ったBusiness Cardが作成できました! よかったらぜひ試してみてください♪ また、SNSでつながっていただけると嬉しいです?
- 投稿日:2021-06-23T10:39:53+09:00
ReactでTextareaの高さを可変にする
問題点 textareaのstyleにline-heightを指定すると 入力された値が1行の時でも、余分に余白ができてしまった。。。 textareaの高さを入力された行数によって、可変にしたい。 結論 textareaのrow属性を動的に指定して、実現できた。 コード(React_classコンポーネント) constructor() constructor(){ super(); this.state={ text:"サンプルテキスト" } } 高さを計算する関数 textarea内の改行は\nで取得できる 入力値を\nで分割すれば、改行数が取得できる calcTextAreaHeight(value){ let rowsNum = value.split('\n').length; return rowsNum } render() <textarea defaultValue={this.state.text} rows={this.calcTextAreaHeight(this.state.text)} onChange={e => this.setState({text:e.target.value})} />
- 投稿日:2021-06-23T09:03:12+09:00
input type=dateの未入力時の「年/月/日」「yyyy/mm/dd」を非表示にする
input type=dateの値が未入力のときに「年/月/日」「yyyy/mm/dd」と表示されたままになるのが見にくい、未入力のときは空欄のままにしてほしいという要望があり、いろいろ調べたけれど設定で変えることはできないようなので、強引ですがJavaScriptで文字色を変えるという方法で対処しました。 ※「年/月/日」「yyyy/mm/dd」等の表記はブラウザに依存するため変更できません。これも属性値で変更出来たら楽なのになあ。 // 初期表示時の文字色変更 $(window).on('load', function () { $.each($('input[type=date]'), (index, datebox) => { datebox.style.color = (datebox.value) ? 'black' : 'white'; } ); } ); $(function () { // フォーカス取得時は入力用にいったん色を付ける $('input[type=date]').focus(function (event) { this.style.color = 'black'; } ); // フォーカス喪失後の文字色変更 $('input[type=date]').blur(function (event) { this.style.color = (this.value) ? 'black' : 'white'; } ); } ); 一部だけに適応したいときは、セレクタに条件を追加してください。 色はわかりやすいように black white と書いていますが、画面デザインに合わせてお好きなように。 テキストボックスの色が複数ある場合は、white を transparent としておけばOKです。
- 投稿日:2021-06-23T08:59:00+09:00
[日付の表示]yyyy/mm/ddのフォーマットにしたい
備忘録。 WebアプリでTo-doリストを作成中。 アイテムを作成した日付を付与し、アイテムと一緒に表示させたい。 その際、日付のフォーマットをyyyy/mm/ddにする 。←本記事で書くこと (※9以下の数字には左に0を置きたい) ↓ 日時の前に「00」、後に「slice(-2)」を置くことで実現! ※Nuxtで書いているので;省略などJsの文法無視しています ※ 記事作成日: 2021年06月21日(日本) const d = new Date() const fmt = d.getFullYear() // document.write(d) // => Mon Jun 21 2021 11:32:57 GMT+0900 (日本標準時) // 年の表示 document.write(fmt) // => 2021 document.write('/') // 月の表示 document.write(d.getMonth() + 1) // => 6 document.write('/') // 月の表示(9以下の数字で左に0を置く) document.write(('00' + (d.getMonth() + 1)).slice(-2)) // => 06 document.write('/') // 日にちの表示 document.write(('00' + d.getDate()).slice(-2)) //=>21
- 投稿日:2021-06-23T07:20:20+09:00
今更ながらRedux Toolkitに入門してみた
はじめに 今更ながらですがRedux Toolkit入門編①です。 ②ではRedux Toolkitの主な機能を紹介し ③ではそれらを踏まえてTodoリストの作成をします。 間違っているところがあれば指定してくれると助かります! この記事ではTypeScriptを使って行うのでTypeScriptを軽くわかった方向けになります。 また下記で行っているテンプレートの作成は最新版で少し中身が変わっている点があるのでご了承ください。 そもそもReduxって何?ってなる人向け Reduxは状態を管理してくれるフレームワークです。 開発の規模が大きくなるほど開発が容易になったりすることができるので大規模な開発においてよく使われていたりします。 Reduxの流れ ユーザの操作により、componentのstateが変更される Action Creatorによって、Actionを生成する そのActionをdispatchする(Storeに送信) ActionとstateをReducerが受け取り、storeのstateを更新する 最後にstoreから更新されたstateが渡ってくる 大まかなまとめ state stateとは日本語で状態を意味しています。その名前の通り そのComponentが持っている状態のことを示します。 Action アクションはUserがinputなど、何かのアクションが起きた時、Storeにどんなデータを利用するかということを定義する。その際にActionCreatorsによってActionを生成する。 Store Storeとはアプリケーションの全てのStateを管理してくれる場所 Dispatch ActionCreatorで生成されたActionを呼び出してStoreに渡してくれます。 storeにdispatchというメソッドを用意して、引数でアクションを呼び出しで使います。 reducer storeで受け取ったアクションを読み取り、stateを更新する必要があればstateを更新する処理を行う場所 ざっくりとした説明ですが最下部に参考文献があるのでそちらをみてさらに理解を深めるといった形がおすすめです。 上記を踏まえた上でRedux Toolkitとはなんぞや 簡潔にいうとReduxを簡潔に使用できるツールキットです。 Redux Toolkitは公式に作られたツールでReduxの公式サイトでも紹介するページがあります。 そこに書かれている記事を抜粋し,なぜRedux Toolkitが作成されたかというと 「Redux storeの設定は複雑すぎる」 「Reduxに何か便利なことをさせるには、たくさんのパッケージを追加する必要がある」 「Reduxにはボイラープレートコードが多すぎる」 実装方法 create-react-appの場合 mkdir practice cd practice npx create-react-app . --template redux-typescript 上記のテンプレートはRedux Toolkitが使われていて簡単に実装ができるのでめんどくさい設定が嫌いな方はこれでいいと思います。 既存のアプリケーションに導入する場合 npm install @reduxjs/toolkit 参考文献 参考にさせていただいたきました!!ありがとうございます!! https://note.yuuniworks.com/study/redux-toolkit.html#%E5%90%AB%E3%81%BE%E3%82%8C%E3%82%8B%E3%82%82%E3%81%AE
- 投稿日:2021-06-23T07:20:20+09:00
今更ながらRedux Toolkitに入門してみた①
はじめに 今更ながらですがRedux Toolkit入門編①です。 ②ではRedux Toolkitの主な機能を紹介し ③ではそれらを踏まえてTodoリストの作成をします。 間違っているところがあれば指定してくれると助かります! この記事ではTypeScriptを使って行うのでTypeScriptを軽くわかった方向けになります。 また下記で行っているテンプレートの作成は最新版で少し中身が変わっている点があるのでご了承ください。 そもそもReduxって何?ってなる人向け Reduxは状態を管理してくれるフレームワークです。 開発の規模が大きくなるほど開発が容易になったりすることができるので大規模な開発においてよく使われていたりします。 Reduxの流れ ユーザの操作により、componentのstateが変更される Action Creatorによって、Actionを生成する そのActionをdispatchする(Storeに送信) ActionとstateをReducerが受け取り、storeのstateを更新する 最後にstoreから更新されたstateが渡ってくる 大まかなまとめ state stateとは日本語で状態を意味しています。その名前の通り そのComponentが持っている状態のことを示します。 Action アクションはUserがinputなど、何かのアクションが起きた時、Storeにどんなデータを利用するかということを定義する。その際にActionCreatorsによってActionを生成する。 Store Storeとはアプリケーションの全てのStateを管理してくれる場所 Dispatch ActionCreatorで生成されたActionを呼び出してStoreに渡してくれます。 storeにdispatchというメソッドを用意して、引数でアクションを呼び出しで使います。 reducer storeで受け取ったアクションを読み取り、stateを更新する必要があればstateを更新する処理を行う場所 ざっくりとした説明ですが最下部に参考文献があるのでそちらをみてさらに理解を深めるといった形がおすすめです。 上記を踏まえた上でRedux Toolkitとはなんぞや 簡潔にいうとReduxを簡潔に使用できるツールキットです。 Redux Toolkitは公式に作られたツールでReduxの公式サイトでも紹介するページがあります。 そこに書かれている記事を抜粋し,なぜRedux Toolkitが作成されたかというと 「Redux storeの設定は複雑すぎる」 「Reduxに何か便利なことをさせるには、たくさんのパッケージを追加する必要がある」 「Reduxにはボイラープレートコードが多すぎる」 実装方法 create-react-appの場合 mkdir practice cd practice npx create-react-app . --template redux-typescript 上記のテンプレートはRedux Toolkitが使われていて簡単に実装ができるのでめんどくさい設定が嫌いな方はこれでいいと思います。 既存のアプリケーションに導入する場合 npm install @reduxjs/toolkit 参考文献 参考にさせていただいたきました!!ありがとうございます!! https://note.yuuniworks.com/study/redux-toolkit.html#%E5%90%AB%E3%81%BE%E3%82%8C%E3%82%8B%E3%82%82%E3%81%AE