20210405のJavaScriptに関する記事は21件です。

正規表現備忘録

受け取った値が電話番号の形式かどうかを判定するだけの正規表現 以下の電話番号の形式であれば、trueを返す。 (USの電話番号に倣ったもの。またcountry codeを使う場合、行頭に1を付与する。) 555-555-5555 (555)555-5555 (555) 555-5555 555 555 5555 5555555555 1 555 555 5555 test.js function telephoneCheck(str) { var reg = /^(1\s?)?(\(\d{3}\)|\d{3})[\s\-]?\d{3}[\s\-]?\d{4}$/; return reg.test(str); } ^ ...文字列の始まりを表す。 \s ...空文字を表す。 ? ...直前の1文字またはグループは省略可、を表す。 (1\s?)? ..."1 " または "1"が1つ以上あるかどうかの判定 \d ...数字1文字を表す。 {n}... 直前の文字のn回の繰り返し \ ...つぎに来る文字または記号を正規表現ではなく、単なる文字として扱いたい場合に使う。 | ...orを表す。 ((\d{3}\)|\d{3})... (\d{3}) または d{3}を判定。 [ ] ...[ ]の中のどれか1文字を対象にする [\s\-]? ...空文字 または - の探索 $... 行末 以上。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JSでフィボナッチ数列のアルゴリズム組んでみた

はじめに jsの勉強をしていてjsに慣れるために自力でフィボナッチ数列のアルゴリズム組んでみました 冗長なところがあれば教えてください ソースコード function fibonacci(n) { // 引数が数値でなければErrorを返す if (typeof n !== 'number') { throw new Error('引数が数値ではありません:' + n) } // 引数が自然数でなければErrorを返す if (n <= 0) { throw new Error('引数は自然数で指定してください') } // 初期値指定 let result = [1]; // ループ処理 初期値が指定されているのでループ回数は n - 1 回 for (let i = 0; i < n - 1; i++) { // 足す数値の配列 let addNums = result.slice(-2) // 計算結果をresultにプッシュ result.push(sum(...addNums)) } return result } // 計算処理(可変変数を使いたかっただけ) function sum(...nums) { let result = 0 for(let num of nums) { result += num } return result } try { console.log(fibonacci('計算回数入れてください')) } catch(e) { window.alert(e.message); } おわり
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Google Maps JavaScript APIを使ってWordPressページにルートマップを埋め込む(WordPress編)

はじめに で書いたGoogle Maps Javascript Apiについて、WordPress側でやったことを書きます。 この記事ではHTMLのformにhidden属性のinputタグ設定し、idとvalueでキーバリュー型の情報を格納しています。 こんな感じ <form id=""> <input type="hidden" id="count" value="4" /> <input type="hidden" id="p0-label" value="Start" /> <input type="hidden" id="p0-title" value="Kamakura Station" /> <input type="hidden" id="p0-latlng" value="35.319264, 139.550004" /> <input type="hidden" id="p0-description" value="Tour starts from here" /> <input type="hidden" id="p1-label" value="1" /> <input type="hidden" id="p1-title" value="Tsurugaoka Hachimangu Shrine" /> <input type="hidden" id="p1-latlng" value="35.326074, 139.556485" /> <input type="hidden" id="p1-description" value="Built as a tutelary shrine for the Genji family in 1191, the most gorgeous shrine in Kamakura attracts millions of visitors for its history, culture, architecture and various traditional events." /> <input type="hidden" id="p2-label" value="2" /> <input type="hidden" id="p2-title" value="Hase-dera Temple" /> <input type="hidden" id="p2-latlng" value="35.312416, 139.533042" /> <input type="hidden" id="p2-description" value="This legendary old temple boasts of its large wooden gilded statue of Eleven-faced Goddess of Mercy, a beautiful Japanese-style garden and the nice view of the ocean from a terrace." /> <input type="hidden" id="p3-label" value="3" /> <input type="hidden" id="p3-title" value="the Great Buddha" /> <input type="hidden" id="p3-latlng" value="35.315858, 139.535371" /> <input type="hidden" id="p3-description" value="The Great Buddha, a National Treasure and a Symbol of Kamakura, was cast in bronze in the middle of the 13th C. The hollow interior of the statue is open to the public." /> </form> WordPressはphpとMySqlで動いているので、MySqlにルート設定用のテーブルをもたせ、functions.phpでデータを呼び出してformを生成しています。 また、使う際にはWordPressのショートコードを使うことでWordPressのPage作成時に簡単に地図が埋め込めるようにしました。 DataBase wp2_course、wp2_route、wp2_placeの3つのテーブルを使っています。プレサフィックスの"wp2_"はWordPress用のテーブルがすべてこの形式だったので真似しただけで、理由はないんですが、今思うと別に真似しなくても良かったような。 wp2_course(コース設定用テーブル) 物理名 データ型 NULL 既定値 説明 id text no なし コース識別コード。ショートコードでどのルート表示するか指定します。 zoom int(11) yes NULL 地図の初期表示縮尺 travelmode varchar(10) yes NULL WALKING:徒歩(既定値)DRIVING:車BICYCLING:自転車TRANSIT:公共交通機関 maptypeid varchar(10) yes NULL ROADMAP:道路や建物などが表示される地図(既定値)SATELLITE:衛星写真HYBRID:ハイブリッドTERRAIN:地形情報地図 実際にはid以外ほぼNULLで使っています。 こんな感じ id zoom travelmode maptypeid K1 NULL NULL NULL YM1 NULL NULL NULL wp2_place(地点テーブル) 物理名 データ型 NULL 既定値 説明 name varchar(50) no なし 地点の物理名 title varchar(50) no なし Markerクリック時のポップアップウィンドウのタイトル latlang geometry no なし 緯度経度 description varchar(400) no なし Markerクリック時のポップアップウィンドウに表示する説明文 例: name title latlng description KAMAKURA_STATION Kamakura Station 'POINT(35.319264 139.550004)',0 Tour starts from here. HACHIMAN_GU Tsurugaoka Hachimangu Shrine 'POINT(35.326074 139.556485)',0 Built as a tutelary shrine for the Genji family in 1191. wp2_route(経路テーブル) 物理名 データ型 NULL 既定値 説明 index int(11) no なし インデックス(Auto_Increment) id varchar(5) no なし コースid name varchar(50) no なし 地点の物理名 order int(11) no なし 経路の順序 label varchar(10) no なし Markerに表示するラベル。"-b-"とすると経路を分割する marker tinyint(1) no 1 1:マップ上にMarker生成する、0:Marker生成しない skip tinyint(1) no 0 1:経路に含める,0:経路に含めない marker/skipの組み合わせでこんな感じになります。 marker skip 説明 1 0 (既定値)Marker生成して経路に含まれる 0 0 Markerなしの通過点となり、細かい経路設定が可能 1 1 Markerのみで経路に含まれない 0 1 この設定かつlabel="-b-"で分割に使用しています。 marker=0,skip=1の場合は最初使用しない組み合わせだったんですが、あとで経路を分割する必要が生じた際に使うことにしました。 例: index id name order label marker skip 1 K1 KAMAKURA_STATION 0 Start 1 0 2 K1 HACHIMAN_GU 1 1 1 0 3 K1 HASE_DERA 2 2 1 0 4 K1 GREAT_BUDDHA 3 3 1 0 正直、placeとrouteはテーブル分けなくても良かったなって後で思いました。使うときはJoinして使うし、メンテナンス性があんまり良くなくて。 courseテーブルもいらないっちゃいらなかったんですが・・・ functions.php WordPressに組み込むため、functions.phpを記述します。 最初はスクリプト登録用の手続き <?php add_action( 'wp_enqueue_scripts', 'theme_enqueue_styles' ); function theme_enqueue_styles() { wp_enqueue_style( 'parent-style', get_template_directory_uri() . '/style.css' ); wp_enqueue_style( 'child-style', get_stylesheet_directory_uri() . '/style.css', array('parent-style') ); } function register_script(){ // 登録の項目 wp_register_script( 'maps', get_stylesheet_directory_uri() . '/js/maps.js', false, '', true); wp_register_script('googlemaps', '//maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&language=en&callback=initMap', array('maps'), '', true ); } function add_script() { // 装備の項目 register_script(); wp_enqueue_script('maps'); wp_enqueue_script('googlemaps'); } add_action('wp_enqueue_scripts', 'add_script'); 次にマップ用の関数を作ります テーブルから情報とってきてformを生成しています。 YOUR_API_KEYにはGoogle Maps Api使用する際に必要なキーを取得して埋め込んでください。 // 地図用 function gmap($atts) { //ショートコードパラメータからコース名取得 extract(shortcode_atts(array( 'course' => 'undefined', 'height' => '480px', ), $atts)); if($course=='undefined'){ echo "[gmap course='???']course not defined"; return; } // wpdbオブジェクト global $wpdb; // ★デバッグ用 $wpdb->show_errors(); // コース取得用SQL $sql_course=$wpdb->prepare(" SELECT c.id, c.travelmode, c.maptypeid, c.zoom FROM wp2_course c WHERE c.id='%s' ",$course); // ルート取得用SQL $sql_route=$wpdb->prepare(" SELECT r.id, r.name, r.order, r.label, r.marker, r.skip, p.title, p.description, X( p.latlng ) AS x, Y( p.latlng ) AS y FROM wp2_route r JOIN wp2_place p ON r.name = p.name WHERE r.id = '%s' ORDER BY r.order ",$course); // クエリ実行 $rows_course = $wpdb->get_results($sql_course); if($rows_course){ $rows_route = $wpdb->get_results($sql_route); if($rows_route){ $num = $wpdb->num_rows; echo <<< EOM <form id="{$wpdb->id}"> <input type="hidden" id="count" value="{$num}" /> EOM; foreach($rows_course as $row_course){ if($row_course->travelmode!=null){ echo "<input type='hidden' id='travelmode' value='{$row_course->travelmode}' />"; } if($row_course->maptypeid!=null){ echo "<input type='hidden' id='maptypeid' value='{$row_course->mapTypeId}' />"; } if($row_course->zoom!=null){ echo "<input type='hidden' id='zoom' value='{$row_course->zoom}' />"; } } $index=0; foreach($rows_route as $row ){ echo <<< EOM <input type="hidden" id="p{$index}-label" value="{$row->label}" /> <input type="hidden" id="p{$index}-title" value="{$row->title}" /> <input type="hidden" id="p{$index}-latlng" value="{$row->x}, {$row->y}" /> <input type="hidden" id="p{$index}-description" value="{$row->description}" /> EOM; if($row->marker==0){ echo <<< EOM <input type="hidden" id="p{$index}-marker" value="{$row->marker}" /> EOM; } if($row->skip==1){ echo <<< EOM <input type="hidden" id="p{$index}-skip" value="{$row->skip}" /> EOM; } $index=$index+1; } echo "</form>"; // echo "<div id='map' style='height: 480px'></div>"; } } return "<div id='map' style='height: {$height}'></div>"; } 最後にショートコードを追加します。 add_shortcode('gmap', 'gmap'); 使い方 WordPressダッシュボードのPagesでPageを作成し、本文に [gmap course="K1"] というふうに記述します。 ここで"K1"はコースidで設定したものを指定します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.js入門編

Node.jsとは? フロントエンドエンジニアですが、技術の幅を広げる為、JavaScript力をあげる為にNode.jsをやろうと思い、まずは概要を簡単ですがまとめました。ノンブロッキングI/Oとか難しい部分は追って学んで行こうと思います。 Node.jsはサーバーサイドのJavaScript実行環境と言われてますが、これだけだとピンとこないですね。さらに詳しく説明すると、 Node.jsは「V8」というJavaScript実行エンジンで作られてる。 (V8はGoogleが開発しているオープンソースのJavaScript実行エンジンです。V8はNode.jsに限らず、GoogleChromeでも採用されています。) つまり、Node.js環境をサーバー上に作る → サーバーサイドでJavaScriptを実行できる → JavaScriptでサーバーサイドを実装できるという事です。 もちろんNode.js固有の文法やAPIが沢山あるので、フロントエンドのJS力だけで良いという訳ではないですが、慣れ親しんだJSでサーバーも作れるのは全く違う言語を覚えるよりはとっつきやすいかもしれません。(ただ本気でNode.jsを習得するのと他のサーバー言語を習得するコストはあまり変わらないとも言われてます。。) ちなみに、他ブラウザのJavaScript実行エンジンは、 ・Firefox: SpiderMonkey ・Safari: JavaScriptCore ・MicrosoftEdge: Chakra/V8 らしいです。 クライアントの開発環境としても使われる ReactやVueなどの開発に使われるwebpack-dev-serverを使ってローカルサーバーを立てるのにNode.jsは使われています。 npmとは? Node Package Managerの略で、Node.jsの実装はnpmを活用して行ないます。 npmに登録されてるパッケージであれば(=Node.jsパッケージとして流通してる)、npmを使って簡単にインストールできます。npmでインストールしたパッケージはnode_modules配下に入りimport, requireで使えます。 「パッケージ」というのはライブラリやフレームワークのことで、わざわざライブラリのLPに行ってダウンロードして、プロジェクトディレクトリに置いて、なんて事しなくてもnpmコマンドを使えば一発で取り込めると言うことです。 yarn: Facebook製のパッケージマネージャー npmでインストールする各種パッケージは本体はリモートにあるので、それを取得してインストールするクライアントはnpmじゃなくてもいい。yarnは高速で信頼性が高く、依存関係も安全に保てる。 余談: 以前業務でフロントのプロジェクトの中に組み込まれていた結構大きめな処理を外に出して、npmに登録し、外部ライブラリとして取り込んで使う運用になった事がありました。 Express Expressは、Node.jsで一番人気なフレームワークです。 プレーンなNode.jsだと大変な開発を楽にしてくれる優れものです。 以下、基本手順のコードだけを抜粋して紹介します。 ・Expressオブジェクトの用意 ・appオブジェクトの作成 ・ルートの設定 ・待ち受け開始 //Expressオブジェクト、appオブジェクトの用意 import * as express from "express"; const app = express(); //第1引数のアドレスにアクセスした(リクエストが来た)時に、第2引数の関数を呼ぶ app.use('/users', users) ルートの設定は、routesディレクトリに各ルートのJSファイルを用意して設定します。 const router = express.Router(); router.get('/', (req, res) => { //この中で処理をして、最終的に何かしらのレスポンスをする //res.send()であれば、引数の値をレスポンスとして返す const responseData = "Welcome to Express"; res.send(responseData); }) app.listen(3000) これでプログラムを実行して、http://localhost:3000にアクセスすると「Welcome to Express」と表示されます。 Express Generator Expressでアプリケーションを作っても、必要なファイル、モジュールなどは全て自前で用意しないといけないのですが、「基本セット」を最初から用意してくれるライブラリがExpress Generatorです。 Reactでいうところのcreate-react-appみたいなものかと思います。個人開発や学習の為なら間違いなくこれを使ってやるべきですね。 まとめ 本編では、あくまでも基本的な概念やExpressの基本のまとめです。 次は実際に定番のToDoアプリを作ってデプロイまでやってみますので、そこでは詳しいコードを交えた記事にしたいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Promiseメソッドの実行順序

この記事ではJavaScriptのイベントループベースの並行モデルの基礎を紹介した上、Promiseメソッドが一般的な非同期処理の実行順序の違いについてお話す ※ この記事にある英語を含める画像はほとんど以下のコースから取得した  The Complete JavaScript Course 2021: From Zero to Expert! @ Udemy Promiseメソッドは何か MDNにより、 Promise オブジェクトは非同期処理の最終的な完了処理 (もしくは失敗) およびその結果の値を表現します。 promiseを宝くじ券と考えると、当たる(fulfilled)場合と当たらない(rejected)場合がある 当たる(fulfilled)場合、thenメソッドが呼び出される 当たらない(rejected)場合、catchメソッドが呼び出される 当たる・当たらないと関係しなくfinallyメソッドが呼び出される (then method only called when promise is fulfilled, catch method is only called when the promise is reject, finally method always been called no matter the result of the promise.) Promiseメソッドの処理順序 まずは一般的な非同期処理はどの仕組みで実行されるか見ていきましょう JavaScript(JS) の実行について Javaがランタイムを持つと同様に、JavaScriptはJSランタイムを持っている。このJSランタイムは実ブラウザの上にある。 Node.jsの例で言うと、 Node.js is a platform built on Chrome's JavaScript runtime for easily building fast and scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices. また、MDNにより、 従来より JavaScript はシングルスレッドです。複数のコアを利用しても、メインスレッドと呼ばれる単一のスレッド上でタスクを実行できるだけでしょう。これまでの例は、次のように実行されます。 ということで、JSでは同時並行処理ではなく、イベントループに基づく同時実行モデル仕組みであり、JSランタイムの中でタスクの実行順序を調整される。 具体的にどういう仕組みかを分かるため、JSランタイムを詳しく見ていきましょう。 JSランタイム JSランタイム中に主にJSエンジン、Web APIs、イベントループ、キュー四つのコンポーネントが存在する。 JS全体なイメージは以下の通り それぞれの役割は、 JSエンジン JSランタイムのハットになり、エンジンの中にさらにヒップとコールスタックが存在する ヒップはオブジェクトの保存場所、動的にメモリ領域を確保・解放する コールスタックは、 インタープリターの仕組みの一つ 複数階層の関数を呼び出したスクリプト内の位置を追跡し続けること 関数が呼びされるとここに追加、値が返された時にpopする LIFOの原則でに基づく機能する グローバルエクスキューションコンテキストを持つ Web APIs Webブラウザに組込まれているAPI、非同期処理の実行場所。ここでメインにクライアントサイド(ブラウザ)のJSでのAPIを指す。 (これ以外に、サードパーティAPI(Twitter APIなど)という分類もある) よく使われるWeb APIが、例えば、 ブラウザで読み込んだ文書を操作するためのDOM サーバからデータ取得をするAPIのXMLHttpRequestやFetch API キュー 実行待ちのコールバック関数を持つキュー。主にはコールバックキューとマイクロタスクキュー2種類がある(以下の図をご参照)。 一般的なFIFOの原則でに基づく機能する WEB APIでは実行される関数は、実行時点で引数にコールバック関数を取ることが多い。これらのコールバック関数はすぐにコールスタックに追加されるのではなく、まず、Web API環境で非同期処理が実行される、そして関連のコールバック関数をキューに渡される イベントループ コールスタックとキューの間のタスクコーディネーター機能する。 コールスタックが(グローバルエクスキューションコンテキスト以外に)空白かどうかを確認 空白の場合、あるいは実行中のコードがない場合コールバックキューからタスクを取得し、コールバックに置く この流れは1つのevent loop tickと呼ばれる Promiseメソッドの実行と一般的なタスクとの違い 一般的になタスクはコールバックキューに、Promiseメソッド(Promise以外もある)のコールバック関数はマイクロタスクキューに渡される。 マイクロタスクキュー内のタスクはコールバックキューのタスクより優先度が高い。 つまり、一event loop tickの後、イベントループがキューからタスクをコールスタックに調達する時、WEB APIがマイクロタスクキューにタスクが継続に追加されるさえ、コールバックキューのタスクが永遠に調達・実行されないこと コールバックキュー マイクロタスクキュー JSコード実行の流れを例で説明 el = document.querySelector('img'); el.src = 'dog.jpg'; el.addEventListener('load', () => { el.classList.add('fadeIn'); }); fetch('https://url.com/api') .then(res => console.log(res)); こちらのコード実行の流れは以下の通り、 イメージのロードは、WEB API環境でバックグランドで走らせ、非同期(このシーンでnon-blockingも言う)形式でローディングされる ロードイベントのコールバック関数はまずWEB APIに登録され、ロードイベント発火までWEB API環境に残る ロードができた次第コールバック関数が実行されるため、ロード完了を待つ間その次のコード、ここではfetch関数のdata fetch、が実行される。同じくfetchのコールバック関数もWEB APIに登録される イメージのロードが完了したらロードイベントが発火し、この時点でロードイベントのコールバック関数をタスクとしてコールバックキューに渡される 同じくデータfetchingができたら、fetchのコールバック関数をタスクとしてマイクロタスクキューに渡される コールバックキュー、またはマイクロタスクキューに渡したタスクはすぐに実行されることではなく、行列で並んで、イベントループがコールスタックに実行中のコードがないと確認したら、先入れ先出し順序でタスクを順番にコールスタックに渡して実行される イベントループがキューにタスクを見に行く時点、もしマイクロタスクキューにfetchのコールバック関数が存在、コールバックキューにロードイベントのコールバック関数が存在する場合、マイクロタスクキューのfetchのコールバック関数を優先にコールスタックに渡し実行される ※ ここでfetch()の結果はPromiseで返される 参考 Fetch の使用 Promise Understand promises before you start using async/await How to make HTTP requests using Fetch API and Promises A simple guide to JavaScript concurrency in Node.js and a few traps that come with it JavaScriptの非同期処理を並列処理と勘違いしていませんか? How JavaScript works: an overview of the engine, the runtime, and the call stack How JavaScript works: inside the V8 engine + 5 tips on how to write optimized code JavaScript の仕組み:メモリ管理+ 4つの共通のメモリリーク処理方法 Even with async/await, raw promises are still key to writing optimal concurrent javascript The Javascript Runtime Environment
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】分割代入について

配列やオブジェクトの値を分割して代入したいときに有効 配列 sample.js const array = [1, 2, 3]; // e0...array[0]...e1...array[1]...e2...array[2]............ const [e0, e1, e2] = array; console.log(e0)// 1 console.log(e1)// 2 console.log(e2)// 3 オブジェクト オブジェクトを変数に代入するときに分割して代入することができる sample.js const obj = {a: 10, b: 20, c: 30}; const {a, c} = obj; console.log(a); // 10 console.log(c); // 30 動作確認 docker run -it --rm node:lts-alpine sh /# vi test.js # ファイルをコピペ /# node test.js
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

親コンポーネントから子コンポーネントへのデータの渡し方

データの渡し方を忘れないように、メモとして残しておこうと思います。 v-bindとpropsを使いました。 App.vue <template> <div id="app"> <Number :number="number"></Number> </div> </template> <script> import Number from "./components/Number.vue"; export default { data() { return { number: 10, }; }, components: { Number } }; </script> Number.vue <template> <div id="Header"> <p>{{ number }}</p> </div> </template> <script> export default { props: ["number"], }; </script>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React Hook Form 7.0.0アップデートにあたってマイグレーションを行う

7.0.0アップデート React Hook Form 7.0.0へアップデートしたということで、v6.15.1からアップデートしたことで、変更が必要だった点をアウトプット 注意:すべてはmigration guideに書かれている!! 今回の修正コード login.tsx import { useEffect } from 'react' import { useDispatch, useSelector } from 'react-redux' import { jsx } from '@emotion/react'; import { useForm } from "react-hook-form"; import { fetchAsyncLogin, selectUser } from '../../../stores/slices/userSlice' import { BrowserRouter as Router, useHistory } from 'react-router-dom' import { hot } from 'react-hot-loader' import Header from '../../components/block/Header' import Footer from '../../components/block/Footer' import { LoginTitle } from '../../../style/pages/Login' interface loginFormInput { userName: string; passWord: string; } const Login = () => { // 一時的な保持は各コンポーネントで行う const { register, handleSubmit, errors, reset } = useForm<loginFormInput>(); const dispatch = useDispatch() const history = useHistory() // storeに保存されている情報を変数にいれる const user = useSelector(selectUser) const onSubmit = (data: loginFormInput) => { dispatch(fetchAsyncLogin(data)) reset() if (user.isLogin === true) { history.push('/top') } } // Loginを検知してページ遷移 useEffect(() => { console.log(user) if (user.isLogin === true) { history.push('/top') } }, [user.isLogin]) return ( <div> <Header /> <h1 css={LoginTitle}>Login Page</h1> {errors.userName && '入力に不備がございます'} <form onSubmit={handleSubmit(onSubmit)}> <input type="text" name="userName" aria-invalid={errors.userName ? "true" : "false"} ref={register({ required: true })} placeholder="user" /> {errors.userName && errors.userName.type === "required" && <span role="alert">必須だよ!</span>} <input type="text" name="passWord" aria-invalid={errors.passWord ? "true" : "false"} ref={register({ required: true, pattern: /^[A-Za-z]+$/i })} placeholder="pass" /> {errors.passWord && errors.passWord.type === "required" && <span role="alert">必須だよ!</span>} {errors.passWord && errors.passWord.type === "pattern" && <span role="alert">半角英数字でお願いします!</span>} <input type="submit" /> </form> <Footer /> </div> ) } export default hot(module)(Login) プロパティ 'errors' は型 'UseFormReturn' に存在しません。 下記コードで7へアップデートするとタイトルの注意が出てくる。 const { register, handleSubmit, errors, reset } = useForm<loginFormInput>(); migration guideをみてみる。 errors: errors object has been moved into formState object. This will info hook form that errors object is been subscribed. なるほど errosオブジェクトはformStateの中へ移動したと多分書かれています。 - const { register, handleSubmit, errors, reset } = useForm<loginFormInput>(); + const { register, handleSubmit, formState: { errors }, reset } = useForm<loginFormInput>(); これで注意は消えます。 FormRegisterReturn' を型 'LegacyRef | undefined' に割り当てることはできません。プロパティ 'current' は型 'UseFormRegisterReturn' にありませんが、型 'RefObject' では必須です。 以下のコードだとタイトルのような注意が出る。 <input type="text" name="userName" aria-invalid={errors.userName ? "true" : "false"} ref={register({ required: true })} placeholder="user" /> migration guideをみてみる。 register method is no longer occurred at ref, instead invoke the function itself and spread the props into the input. The function itself will return the following props: onChange, onBlur, name and ref. refの中でregisterは呼び出せなくなったぞ!ただregister関数を呼び出し、そこから色々設定できるぞい的なことが書かれているぽい <input type="text" - name="userName" aria-invalid={errors.userName ? "true" : "false"} - ref={register({ required: true })} + {...register("userName", { required: true })} placeholder="user" /> registerの中にname属性が指定できるようになり、name属性の指定がいらなくなりました。 第二引数にrequired trueを付与することで、同じ挙動を再現できます。 今回このような形になったのは以下の理由があるそうです。 register 関数を呼び出すことで型チェックができるようになりました。元のAPIには賛否両論ありますし、型安全性と相性が悪いのは間違いありません。この変更によって、 useController, Controller などのAPIでも型チェックも行ってくれるようになります。(引用: React Hook Formのアップデート内容 - Version 7) 別のinputでバリデーションパターンを設定している箇所があります。こちらも直します。 <input type="text" name="passWord" aria-invalid={errors.passWord ? "true" : "false"} ref={register({ required: true, pattern: /^[A-Za-z]+$/i })} placeholder="pass" /> <input type="text" - name="passWord" aria-invalid={errors.passWord ? "true" : "false"} - ref={register({ required: true, pattern: /^[A-Za-z]+$/i })} + {...register("passWord", { required: true, pattern: /^[A-Za-z]+$/i })} placeholder="pass" /> {}の中で続けて書けば良いって言うだけですね 最後に 以上がアップデートによるマイグレーションでした。 今回は小さめのフォームであったためそこまで大きな変更はなかったのですが、他にも変更箇所はありますので確認した上でアップデートは行っていきましょう。 参考文献 React Hook Formのアップデート内容 - Version 7 Migration Guide
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript(ES6)の配列メソッドについて学習したのでまとめてみた

Vue.jsを利用したポートフォリオを作成している段階で、JavaScriptの知識が浅はか過ぎると実感した為、Udemyの教材を参考に改めて勉強することにしました。 今回は、配列メソッドについて学んだことを自分用のメモとしてアプトプットしていきます。 参考教材 世界で3万人が受講】JavaScriptエンジニアのためのES6完全ガイド https://www.udemy.com/course/javascriptes6/ forEachメソッド 配列内の各要素を抽出することができる main.js const users = [ 'yamada', 'ooshima', 'suzuki' ] users.forEach(user => { console.log(user) } // yamada, ooshima, suzuki findメソッド 配列内の指定要素を一つだけ抽出できるメソッド main.js const users = [ { name: 'yamada', country: 'japan', age: 28 }, { name: 'ooshima', country: 'japan', age: 23 }, { name: 'michael', country: 'usa', age: 31 }, ] let japanese = users.find(user => { return user.country === 'japan' }) console.log(japanese) /* { name: 'yamada', country: 'japan', age: 28 } findメソッドは指定要素を1つ抽出すると実行が終了する */ filterメソッド 配列内の指定した要素を全て抽出することができるメソッド javascript/main.js let stations = [ { name: '東京駅', line: '中央線' }, { name: '新宿', line: '山手線' }, { name: '北千住', line: '常磐線' }, { mame: '恵比寿', line: '山手線' } ] let yamanoteLine = stations.filter(station => { return station.line === '山手線' }) console.log(yamanoteLine) /* { name: '新宿', line: '山手線' }, { name: '恵比寿', line: '山手線' } findメソッドとは異なり、指定要素を全て抽出することができる */ mapメソッド 新しい配列を作り格納するメソッド javascript/main.js const developTeams = [ { name: 'yamada', age: 34, position: 'director' }, { name: 'sakurai', age: 25, position: 'manager' }, { name: 'suzuki', age: 21, position: 'engineer' }, { name: 'saito', age: 22, position: 'engineer' }, ] let teamMemberNames = developTeams.map(developTeam => { return developTeam.name }) console.log(teamMemberNames) /* [ 'yamada', 'sakurai', 'suzuki', 'saito' ] teamMemberNamesという新しい配列に格納される */ reduceメソッド 第一引数に各要素に関数を実行し、値を単一にすることができるメソッド javascript/main.js const tests = [ { subject: '国語', score: 69 }, { subject: '数学', score: 79 }, { subject: '英語', score: 87 }, { subject: '理科', score: 53 }, { subject: '社会', score: 91 }, ] let total = tests.reduce((sum, test) => { return sum += test.score }, 0) console.log(total) // 379 今回は、ポートフォリオ作成時に使えそうな配列メソッドをピックアップしてアウトプットしました。 今後もどんどんアウトプットしていきたいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

メモ:nuxt/imageでSSGする際に処理する画像が多いとipxがエラー吐きまくる問題

画像が多いサイトで、nuxt/imageでSSG(静的サイトジェネレーター)で書き出ししようとするとエラーが出て、ネット上に解決方法が見つからなかったのでメモ。 nuxt/imageとは? nuxtの公式の画像最適化用のコンポーネントで、next.jsでいう next/image と同じような位置付けのコンポーネントです。 エラーの詳細 画像の多いサイトで、以下のコマンドで書き出ししようとすると yarn generate このようなエラーがたくさん出て画像の書き出しに失敗します。 ERROR request to http://localhost:57406/_ipx/images/hoge.jpg?s=640_298 failed, reason: connect ECONNRESET 127.0.0.1:57406 Githubにこのようなissueがありましたが、ちゃんと解決に至っていません。 https://github.com/nuxt/image/issues/198 原因を探ってみると画像変換に使っている ipx というローカルサーバへのアクセス過多でエラー吐いているようだったので、とりあえず応急処置的に対応しました。画像ファイルの処理を一斉に行わず少しずつ遅延させながら処理するようにしています。 @nuxt/image の /src/generate.ts を以下のように修正しました。 export function setupStaticGeneration (nuxt: any, options: ModuleOptions) { const staticImages = {} // url ~> hashed file name nuxt.hook('vue-renderer:ssr:prepareContext', (renderContext) => { renderContext.image = renderContext.image || {} renderContext.image.mapToStatic = <MapToStatic> function ({ url, format }: ResolvedImage) { if (!staticImages[url]) { const ext = (format && `.${format}`) || extname(parseURL(url).pathname) || '.png' staticImages[url] = hash(url) + ext } return staticImages[url] } }) nuxt.hook('generate:done', async () => { const { dir: generateDir } = nuxt.options.generate let delay = 0 // ← この行を追加 const downloads = Object.entries(staticImages).map(([url, name]) => { if (!hasProtocol(url)) { url = joinURL(options.internalUrl, url) } delay += delayOffset // ← この行を追加 return downloadImage({ url, name, outDir: resolve(generateDir, '_nuxt/image' /* TODO: staticImagesBase */), delay // ← この行を追加 }) }) await Promise.all(downloads) }) } async function downloadImage ({ url, name, outDir, delay }) { // 引数にdelayを追加 try { await new Promise(resolve => setTimeout(resolve, delay)) // ← この行を追加 const response = await fetch(url) if (!response.ok) { throw new Error(`Unexpected response ${response.statusText}`) } const dstFile = join(outDir, name) await mkdirp(dirname(dstFile)) await pipeline(response.body, createWriteStream(dstFile)) logger.success('Generated static image ' + relative(process.cwd(), dstFile)) } catch (error) { logger.error(error.message) } } 本当は上記のissueにもコメントされてますが、本当は最大の並行処理数を決めて、処理の完了を監視して次の処理をやるみたいなことをやるべきですが、とりあえず動けば良いという雑なやり方です。 一応GithubでForkしています。 追記: nuxtを使ったプロジェクトの package.json の @nuxt/image を以下のように書き換えるとこちらのForkを読み込むようになりますが、設定が足りていないのか、普通に yarn install しても buildコマンドが走らずdistディレクトリが生成されないです。 "dependencies": { "@nuxt/image": "git+https://github.com/kjkmr/nuxt-image.git#main" } とりあえずForkしたプロジェクトを npm run build することで吐き出されるdistディレクトリを nuxtのプロジェクトの node_modules/@nuxt/image 以下にコピーして無理矢理解決しています。(今度ちゃんとやり方調べたい。。。)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JS~alert,confirm~

今回はJSでのアラートボックスとダイアログボックスについて学習していきます! アラートボックス▶ユーザーへの確認や注意喚起のために使われます。 ダイアログボックス▶ ユーザーへの確認と注意喚起のために使われるが、ボタンの情報を受け取ることができます。 index.js <script> 'use strict' alert('シャットダウンしますか??'); const answer = confirm('削除しますか??'); if (answer) { document.write('削除しました'); } else { document.write('キャンセルしました'); } </script> ここで注意なのは、このダイアログボックスはCSSで装飾することができません。 あくまで、ユーザーに注意喚起をする目的になっていいます。 CSSでデザインする場合は モーダルウィンドウを作ります。 次の記事でモーダルウィンドウを作って行きたいと思います?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

オブジェクトにreduceを用いる

vueでreduceメソッドを久しぶりに用いて混乱した部分があるので整理した。 また、オブジェクトにreduceを用いて解説した記事はなかったので自分でまとめた。 ソースコード index.html <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> </head> <body> <div id="app"> <p>合計:{{ totalPrice }}</p> </div> <script> const items = [ { name: '鉛筆', price: 300, quantity: 3 }, { name: 'ノート', price: 400, quantity: 4 }, { name: '消しゴム', price: 500, quantity: 1 }, ] const vm = new Vue({ el: '#app', data: { items: items }, computed: { totalPrice: function () { return this.items.reduce(function (sum, item) { return sum + (item.price * item.quantity) },0) }, } }) </script> </body> この時の reduce の挙動を totalPrice のなかで初期値などを変えつつ整理する ケース1:初期値を0としたとき ソースコード index.html totalPrice: function () { return this.items.reduce(function (sum, item) { return sum + (item.price * item.quantity) },0) 1回目 2回目 3回目 sum 0 900 2500 item オブジェクト(鉛筆の内容) オブジェクト(ノートの内容) オブジェクト(消しゴムの内容) reduce は配列の内容を出力するので1回目の item はオブジェクト(鉛筆の内容)となります。 2回目の sum では1回目の戻り値が入るので、 sum(初期値0) + (300 * 3) の値となり900 となります。 3回目の sum では2回目の戻り値が入るので、 sum(0 + 900) + (400 * 4) の値となり2500 となります。 最後の return sum + (item.price * item.quantity) では3回目の戻り値が sum に入り 2500 + (500(消しゴムの値段) * 1)となるので 3000 となります。 ケース2:初期値を設定しないとき ソースコード index.html totalPrice: function () { return this.items.reduce(function (sum, item) { return sum + (item.price * item.quantity) }) 1回目 2回目 3回目 sum オブジェクト(鉛筆の内容) [object Object]1600 なし item オブジェクト(ノートの内容) オブジェクト(消しゴムの内容) なし 1回目の sum に入る値は items[0] つまり鉛筆のオブジェクトとなるので各値は上記のようになります。 1回目の item は 配列の2番目の要素が入るので、index[1]が対象となり、 オブジェクト(ノートの内容) となります。 2回目の sum は1回目の戻り値が入るので オブジェクト(ノートの内容) + (400 * 4)となり [object Object]1600 となります。 2回目の item は配列の3番目の要素が入るので、index[2] が対象となり、 オブジェクト(消しゴムの内容) となります。 3回目の sum はすでに2回目の item で items の要素を出力し切っているので実行されません。 3回目の item はこちらも試行回数は終わっているので実行されません。 最後の return sum + (item.price * item.quantity) では2回目の戻り値が sum に入るので、 [object Object]1600 + 500となります。[object Object]1600は文字列とみなされてこのような表記になると思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[RailsAPI x React]環境構築する方法

作成経緯 RailsAPIモード使い方をまとめたかったので作成してます。備忘録なので所々違うかもしれません。 もっといい方法があればぜひ参考にしたいと考えています。今回はローカルで動くようにしていますので 本番環境では動きません。 RaislAPI作成 $ rails new アプリ名 --api Gemfile gem 'rack-cors' #コメントアウトを外してbundle installする config/initializers/cors.rb #コメントアウトを外す Rails.application.config.middleware.insert_before 0, Rack::Cors do allow do origins 'http://localhost:3001' #変更します。 #origins 'example.com' #'http://localhost:3001'許可する resource '*', headers: :any, methods: [:get, :post, :put, :patch, :delete, :options, :head] end end モデル作成 $ rails g migration モデル名 $ bundle exec rails db:migrate 普段と変わらないのでいつも通り作成してください。 ルーティング作成 config/routes.rb Rails.application.routes.draw do #----------ここから---------------- namespace :api do namespace :v1 do resources :todos, only: %i[index show create update destroy] end end #----------ここまで追加------------- end バージョン管理する時にnamespaceを使うと便利なので分けています。 コントローラ作成 app/controllers/api/v1/todos_controller.rb class Api::V1::TodosController < ApplicationController def index todos = Todo.order(updated_at: :desc) render json: todos end def show todo = Todo.find(params[:id]) render json: todo end def create todo = Todo.new(todo_params) if todo.save render json: todo else render json: todo.errors, status: 422 end end def update todo = Todo.find(params[:id]) if todo.update(todo_params) render json: todo else render json: todo.errors, status: 422 end end def destroy if Todo.destroy(params[:id]) head :no_content else render json: { error: "Failed to destroy" }, status: 422 end end private def todo_params params.require(:todo).permit(:name, :is_completed) end end 今回はJSON形式でデータを返すだけです。 destroyは返すデータがないためheadを使っています。 headについてはRailsガイド確認 controllerの書き方Part2 app/controllers/api/v1/todos_controller.rb module Api module V1 class TodosController < ApplicationController def index todos = Todo.all render json: { todos: todos }, status: :ok end end end end module ディレクトリ〜end囲んでもnamespace使うことが出来ます。 React作成 アプリの直下でReactのアプリ作成する。 $ npx create-react-app front $ cd front $ npm install react-router-dom $ npm install axios $ npm start Reactの初期画面が出ていることを確認します。 $ mkdir src/urls $ touch src/urls/index.js src/urls/index.js const DEFAULT_API_LOCALHOST = 'http://localhost:3000/api/v1' export const todosIndex = `${DEFAULT_API_LOCALHOST}/todos` axiosで通信するものを定数で定義します。 src/apis/todos.js import axios from 'axios'; import { todosIndex } from '../urls/index' export const fetchTodos =() => { return axios.get(todosIndex) .then(res => { return res.data //通信成功 }) .catch((e) => console.error(e)) //通信失敗 } axios.getでHTTPリクエストをしています。 通信に成功した場合は.then()なり、通信に失敗・例外の場合は.catch()になります。 作成した関数fetchTodos()呼び出したら'http://localhost:3000/api/v1/todosを呼び出し、JSON形式でデータが返って来ます。 .then(res => { return res.data //通信成功 }) resの中にデータが入り、returnでres.dataデータだけ返しています。 import React, { Fragment, useEffect } from 'react'; import { fetchTodos } from '../apis/todos'; export const Todos = () => { useEffect(() => { fetchTodos() .then((data) => console.log(data) ) }, []) return ( <Fragment> Todo一覧 </Fragment> ) } rails側でrails sをし、localhost:3000立ち上げ、 front側でnpm startします。localhost:3000はrailsで使用しているためlocalhost:3001で立ち上げます。 これで環境構築は完成していると思います。 毎回、railsとreactを両方立ち上げるのがめんどくさいので 今後の目標はDockerで環境構築したいと考えています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JS~mapの使い方~

今回はmapというメソッドの使い方について学習していきます。 mapとは このメソッドには配列から配列を取り出すことができます。また、もとにある配列を壊さずに新しい配列を作ることができます。引数では、もとの配列の各要素を受け取って、新たな配列の要素を返り値とすることができます。 index.js <script> 'use strict' const numbers = [1,2,3]; const upnumber = numbers.map((price) => { return price * 2 }); document.write(upnumber); </script> return(返り値)▶与えられた関数を配列のすべての要素に対して呼び出し、その結果からなる新しい配列のことです。 今回はpriceの2倍の値が表示されているはずです。 mupは新しい配列が生成されるため、その配列を使わない場合は、mupは使いません。 上記のように書くことができますが、省略することもできます。 index.js <script> 'use strict' const numbers = [1,2,3]; const upnumber = numbers.map(price => price * 2); document.write(upnumber); </script> 今回は引数はひとつなので、()は省略することができます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Gatsbyのテーマをカスタマイズする

GatsbyでCMS構築して、デザインをちょこっと替えたい時にハマったので備忘録的にメモ

今回利用させて頂いたStarters
gatsbyjs - LekoArts/gatsby-starter-minimal-blog

トップページの文字列をカスタマイズする

FYI. トップページの文字の変更について
Github - Changing the hero text

トップページの「Hi. I'm Lupin - currently...」を変更する対応

1.上書きするソースをコピーする

cp -r node_modules/@lekoarts/gatsby-theme-minimal-blog/src/texts/hero.mdx src/@lekoarts/gatsby-theme-minimal-blog/texts/

2.hero.mdxを編集する

hero.mdx
-  Hi.
+  Hello!

3.ブラウザで表示を確認する

変更前
スクリーンショット 2021-04-05 13.52.37.png

変更後
スクリーンショット 2021-04-05 13.52.50.png

footerのレイアウトをカスタマイズする

FYI. デザインのカスタマイズについて
gatsbyjs - Plugins and themes/shadowing

footerのStartersのGithubリンクを消す対応

1.上書きするソースをコピーする

cp -r node_modules/@lekoarts/gatsby-theme-minimal-blog/src/components/footer.tsx src/@lekoarts/gatsby-theme-minimal-blog/components/footer.tsx

2.use-site-metadataが見つからない系のエラーになるので修正する。

footer.tsx
-    import useSiteMetadata from "../hooks/use-site-metadata"
+    import useSiteMetadata from "@lekoarts/gatsby-theme-minimal-blog/src/hooks/use-site-metadata"

3.消したい箇所を削除する
私の場合は、「Theme by LekoArts」というリンクを削除しました。

footer.tsx
-      <div>
-        <Link
-          aria-label="Link to the theme's GitHub repository"
-          href="https://github.com/LekoArts/gatsby-themes/tree/master/themes/gatsby-theme-minimal-blog"
-        >
-          Theme
-        </Link>
-        {` `}
-        by
-        {` `}
-        <Link aria-label="Link to the theme author's website" href="https://www.lekoarts.de/en">
-          LekoArts
-        </Link>
-      </div>

4.再度コンパイルする

gatsby develop

3.ブラウザをリロードし、表示を確認する

変更前
スクリーンショット 2021-04-05 13.49.06.png

変更後
スクリーンショット 2021-04-05 13.49.21.png

まとめ

フロントエンド触りなれていなくてもサクッと構築できるGatsbyはとても便利です。
セキュアでカスタマイズ性も高く、今後も活用していこうと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

複数の画像を重ね合わせレスポンシブ対応できる、モック画像表示スクリプトを作ってみた。

実際の作ったモック画像のページ

JavaScriptの勉強(複数の画像を重ね合わせて、重ね合わせ位置と比率を保ちながらレスポンシブ対応して表示する方法を)がてら作ってみました。

GitHub

サンプルページ

下は実際の作ったサンプルページのスクリーンショット
iPhoneとMacBook proとそれぞれの画面に表示する画像で計4枚使用して表示しています。

out.gif

サンプルページで利用した画像

上記の利用例では、以下4つの画像ファイルを利用してます。

imagex4.png

きっかけ

現在、職業訓練校に通っているオッサンです。

職業訓練校の授業でモック表示としてiMacなどのデバイスに、自分たちが作ったページのはめ込み画像をPhotoShopで作ること。そして、その画像を作成ページ説明のビジュアルトップとして掲載することを習いました。

以下はその時作った画像です。

でもどうせなら・・複数のレイヤーを重ねて画像をはめ込む方法で表現したい

ので、とりあえず作ってみる。ざっと制作時間:12時間(結構かかりました。)

利用方法は、こちら(GitHub)のREADME.mdを参照

方法

結局は、表示させたい領域にCSSでposition: relativeとして、各画像のスタイルにposition: absoluteを指定し、それぞれのサイズと表示位置を計算し指定するだけです。

もちろんCSSのみでも実装できますが、メディアクエリー毎に固定値を指定するしかないと思います。また、SCSSでも今表示されている画像幅の取得とかできないですよね。

そこでJavaScriptで処理することに。
どうやって解決したかというと以下の処理を行なっただけ。

  • 表示画像の縮尺比率を取得(表示サイズ ÷ 実画像サイズ)
    • 一旦画像は読み込んだ後表示して、基準となる最初の画像の(CSSで定義したフルードイメージ化しておく)表示サイズを取得
    • 同じく基準となる最初の画像の基サイズを取得する。
  • 画像のサイズを指定する
    • 表示画像の縮尺比率と、表示する画像の実画像サイズより、表示させたい画像サイズを指定する
  • 画像の位置を指定する
    • デバイスの画面の位置を、最初に元画像から座標を確認しておき、その情報を基にする。
    • 確認する座標は、画面の開始位置と終了位置のそれぞれのX,Y座標

また、利用する画像は以下の2つに分けて、それぞれz-indexを指定してレイヤーをコントロールした。
具体的には、後から追加するデバイスを手前に表示する様にした。

  • PCやスマートフォンのデバイスの画像(透過PNGで、スクリーン部分がくり抜かれている画像を想定)
  • デバイスの画面に表示したい画像

基準になる画像を読み込んでその

ポートフォリをやサイトやアプリ紹介によく出てくる表現なので、利用用としてはあるのかな?と思って作りました。

以上

筆者

職業訓練校の内容はWebデザインが中心となり、一応AIが絡むのでJavaScriptとPythonを学ぶが、jQueryのデザイン系プラグイン利用やjupyter-notebookで解析や出力中心で、ほとんどプログラミングについては学べない状況。(どうやら僕が勘違いして入ったらしい。)
なので、現在個人的に家に帰ったらJavaScriptの実践と、Python実践し学習中です。

具体的には、もちろんReactなどフロントエンドのライブラリも使いたいところだが、基礎力をつけるためグッと堪えてネイティブJavaScriptで色々作っているところです。

何かいい方法がありましたら、お声かけください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JS~forEach~

今回は配列の繰り返し処理のforEach文について学習していきます。

まず、一般的使わえる繰り返し分は以下になります。

index.js
<script>
    'use strict'

    const cities = ["東京", "大宮", "横浜"]

    for (let i=0; i<cities.length; i++) {
      document.write(city[i]);
    }
  </script>

*length ▶主に文字列の長さや配列の要素数を取得することができるプロパティになります。

この記述でもいいのですがもう少し簡単に書くことができます。

index.js
 cities.forEach((city,index) => {
    document.write(`都市${index}: ${city}`);
    });

forEachの引数には、cityとindexを入れました。
そして下の方でそれぞれを呼び出してもらいます。

要素の数がなくなるまで、繰り返し処理することができます?

スクリーンショット 2021-04-05 13.03.08.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

transformプロパティについて

はじめに

今回はスワイプ機能の実装の際に使った、transformプロパティについてまとめていきます。

transformプロパティとは

与えられた要素を回転、拡大縮小、傾斜、移動することできます。
今回は要素の「移動」についてまとめていきます。

translate

要素の表示位置を移動させる際に使用します。

種類

  • translate(X方向の距離, Y方向の距離)
     (translate()関数では、X方向とY方向の距離で2D移動を指定します。)

  • translateX(X方向の距離)

  • translateY(Y方向の距離)

  • translateZ()関数にはパーセンテージ値を指定することができない

  • translate3d(X方向の距離, Y方向の距離, Z方向の距離)
     (X方向とY方向とZ方向の距離で3D移動を指定します。)

初期値・適用対象・値の継承

初期値:none
適用対象:ブロックレベル要素、インライン要素
値の継承:しない

構文

transform: translate(12px, 50%);
transform: translateX(2em);
transform: translateY(3in);
transform: translateZ(2px);
transform: translate3d(12px, 50%, 3em);

(※数値と単位は変更可)

参考記事

http://www.htmq.com/css3/transform_translate.shtml
https://developer.mozilla.org/ja/docs/Web/CSS/transform

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JS~配列~

今回は、JSで配列を作っていきます!

配列

index.js
<body>
  <script>
    'use strict'

    const fruits = ["バナナ", "りんご", "みかん"]
    document.write(fruits);
  </script>
</body>

定数にフルーツを代入していきます。今回は、バナナとりんごとみかんを並べました。
ブラウザで表示すると以下のようになります。
スクリーンショット 2021-04-05 10.23.17.png

取り出す

では、この配列から1つ取り出して行きたいと思います。

配列は、左から順番に0番目と考えます。
今回は、りんごを取り出したいと思います。

index.js
<script>
    'use strict'

    const fruits = ["バナナ", "りんご", "みかん"]

    document.write(fruits[1]);
  </script>

今回のりんごは1番目の配列になります。

スクリーンショット 2021-04-05 10.27.10.png

追加、削除

次に、この配列に追加と削除をしていきたいと思います。

index.js
<script>
    'use strict'

    const fruits = ["バナナ", "りんご", "みかん"]
    // document.write(fruit);
    // document.write(fruit[1]);
    fruits.pop();
    fruits.push("もも");
    document.write(fruits);
 </script>

popで末尾のみかんを削除して、pushで末尾にももを追加しました。

スクリーンショット 2021-04-05 10.43.54.png

index.js
const fruits = ["バナナ", "りんご", "みかん"]
    fruits.shift();
    fruits.unshift("マンゴー")
    document.write(fruits);

文頭のバナナを削除し、マンゴーを追加しました。

スクリーンショット 2021-04-05 11.57.34.png

*pushとunshifyは一度に複数の要素を追加することができます。

途中の要素を追加、削除

削除数よりも多い数を追加する場合に使います。

index.js
<script>
    'use strict'

    const fruits = ["バナナ", "りんご", "みかん"]
    fruits.shift();
    fruits.unshift("マンゴー");
    fruits.splice(1,2,"さくらんぼ","ぶどう");
    document.write(fruits);
  </script>

以下のようになりました!!
スクリーンショット 2021-04-05 12.11.24.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

シニアフロントエンド開発者みたいにChromeデベロッパーツールを使おう

本記事は、bytefish氏による「Use Chrome DevTools Like a Senior Frontend Developer」(2020年7月21日公開)の和訳を、著者の許可を得て掲載しているものです。

シニアフロントエンド開発者みたいにChromeデベロッパーツールを使おう

開発環境にChromeを選ぶなら知っておきたい12のテクニック

Image for post

Photo by Morning Brew on Unsplash

さて、何らかの理由で、開発ブラウザとしてChromeを選んだとします。次は、デベロッパーツールを開き、コードのデバッグを開始します。

Image for post

Consoleパネルを開いてプログラムの出力を確認したり、Elementsパネルを開いてDOM要素のCSSコードを確認したりします。

Image for post

でも、Chromeデベロッパーツールを本当に理解していますか?実は、パワフルだけど知られていない機能がたくさん用意されていて、開発効率を大幅に改善できるのです。

ここでは、最も便利な機能を紹介します。お役に立てたら嬉しいです。

ChromeのCommandメニューから始めましょう。ChromeのCommandメニューは、LinuxのShellのようなものです。コマンドを入力してChromeを操作できます。

まず、Chromeデベロッパーツールを開き、次のショートカットでCommandメニューを開きます。

  • windows:Ctrl + Shift + P
  • macOS:Cmd + Shift + P

または、次のボタンをクリックして開くこともできます。

Image for post

次に、Commandパネルに移動します。さまざまなコマンドで、さまざまなパワフルな機能を実行できます。

Image for post

1. パワフルなスクリーンショットを撮る。

画面のキャプチャは日常的によく必要になるものなので、使っているコンピュータにとても便利なスクリーンショットソフトがもうあるでしょう。でも、次のようなことはできますか?

  • ビジュアルウィンドウに表示されていないものも含めて、ウェブページ上のすべてのスクリーンショットを撮る。
  • DOM要素のコンテンツを正確にキャプチャする。

この2つは日常的によく必要になるものですが、OS付属のスクリーンショットツールでは簡単に対応できません。このような場合、コマンドを使えばできます。

対応するコマンドは次の通りです。

  • Screenshot Capture full size screenshot
  • Screenshot Capture node screenshot

任意のウェブページを開きます。例えば、MediumのJavaScriptトップストーリーページなど。https://medium.com/tag/javascript

Commandメニューを開き、Screenshot Capture full size screenshotを実行します。

Image for post

すると、現在のページのフルスクリーンショットを撮ることができます。

Image for post

上のオリジナル画像はとても鮮明ですが、ここではトラフィックの節約のため圧縮画像をアップロードしています。

同様に、DOM要素のスクリーンショットを撮りたい場合、システム独自のスクリーンショットツールを使うことができますが、要素を正確にキャプチャすることはできません。このような場合、Capture node screenshotを使えばできます。

まず、Elementsパネルで要素を選択してから、コマンドを実行します。

Image for post

これが正確なスクリーンショットの結果です。

Image for post

2. 最後の操作結果をConsoleで参照する。

Consoleでのコードデバッグはよく必要になります。例えば、JavaScriptで文字列を逆順にする方法を知りたい時に、関連情報をウェブで検索して、次のコード行を見つけたとします。

'abcde'.split('').reverse().join('')

Image for post

上のコードは文字列を逆順にしています。でも、split() reverse() join()メソッドが何をするのか、この中間ステップの実行結果をまだ理解していません。そこで、上のコードを段階的に実行したいなら、次のように記述します。

Image for post

このステップを経て、実行した各メソッドの戻り値が分かりました。

でも、これでは冗長です。エラーが発生しやすく、理解しにくいです。実はConsoleでは、魔法の変数$_を使って、前の操作の結果を参照できます。

Image for post

$_は特別な変数で、値は常にConsoleの最後の操作の結果と等しくなります。このテクニックは、コードをデバッグするのに便利です。

Image for post

3. XHRリクエストを再送する。

フロントエンドプロジェクトでは、データを取得するためによくXHRを使ってバックエンドにリクエストします。もし、XHRリクエストを再送したい時はどうしますか?

初心者ならページを更新するかもしれませんが、これは面倒です。実は、Networkパネルで直接デバッグできるのです。

Image for post

  1. Networkパネルを開く。
  2. XHRボタンをクリックする。
  3. 再送したいXHRリクエストを選ぶ。
  4. XHRを再生する。

Image for post

4. ページの読み込み状況を監視する。

ページが最初から完全に読み込まれるまでに、10秒以上かかることがあります。このような場合、さまざまな時間帯でページの読み込み状況を監視する必要があります。

Chromeデベロッパーツールでは、NetworkパネルのCapture Screenshotsを使って、ページ読み込み中のスクリーンショットをキャプチャできます。

Image for post

各スクリーンショットをクリックすると、対応する時間のネットワーク要求が表示されます。このように視覚的に表示することで、どのネットワーク要求が常に発生しているかをより正確に把握できます。

Image for post

5. 変数をコピーする。

JavaScriptの変数の値を別の場所にコピーできますか?

これは不可能なことに思えるかもしれませんが、Chromeには、変数をコピーするためのcopyという関数があります。

Image for post

copy関数は、ECMAScriptでは定義されていませんが、Chromeでは提供されています。この関数を使えば、JavaScriptの変数の値をクリップボードにコピーできます。

6. 画像をData URIとしてコピーする。

ページ上の画像を処理する方法には2つあります。外部リソースリンクで読み込む方法と、画像をData URLにエンコードする方法です。

Data URLとは、URLの前にdata:スキームをつけたもので、コンテンツ制作者が小さなファイルを文書の中にインラインで埋め込むことができるものです。以前は「data URI」と呼ばれていましたが、WHATWGがその名称を廃止しました。

小さな画像をデータURLにコーディングし、コードに直接埋め込むことで、ページが必要とするHTTPリクエストの数が減り、ページの読み込みが速くなります。

Chromeでは、どのように画像をデータURLにするのでしょうか?

Image for post

7. オブジェクトの配列を表形式にする。

次のようなオブジェクトの配列があるとします。

let users = [{name: 'Jon', age: 22},
   {name: 'bitfish', age: 30},
   {name: 'Alice', age: 33}]

Image for post

このような配列をコンソールで表示するのは簡単ではありません。配列が長く、要素が複雑になると、さらに理解しにくくなります。

幸いChromeにはオブジェクトの配列を表形式にするtable関数が用意されています。

Image for post

多くの場合、この関数はとても便利です。

8. Elementsパネルでドラッグ&ドロップする。

UI テストのために、ページ上の特定の DOM要素の位置を調整したいことがあります。Elementsパネルでは、任意のHTML要素をドラッグ&ドロップして、ページ上の位置を変更できます。

Image for post

上の git では、Elementsパネルで divの位置をドラッグすると、ウェブページ上の位置が同期して変更されています。

9. 現在選択されている要素をConsoleで参照する。

$0は、Elementsパネルで現在選択されている要素を参照する、もう1つの魔法の変数です。

Image for post

10. CSS疑似クラスをトリガーする。

擬似クラスを使用すると、ドキュメントツリーのコンテンツに関して要素にスタイルを適用するだけでなく、ナビゲータの履歴(:visitedなど)、コンテンツの状態(特定のフォーム要素の:checkedなど)、マウスの位置(マウスが要素の上にあるかが分かる:hoverなど)のような外部要因に関してスタイルを適用できます。

1つの要素に対して複数の擬似クラスを記述することがあります。スタイルのテストを簡単にするため、Elementsパネルで直接これらのスタイルをトリガーできます。

Image for post

あるウェブページです。

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>Document</title>
   <style>
      body{
         font-size: 150px;
      }

      div:hover{
         color: red;
      }
      div:active{
         color: blue;
      }
      div:focus{
         color: brown;
      }
   </style>
</head>
<body>
   <div>hello world</div>
</body>
</html>

rawhover.html

次にブラウザで開き、Elementsパネルから擬似クラスのスタイルをデバッグします。

Image for post

11. ショートカットで要素を非表示にする。

CSSスタイルをデバッグする時に、要素を非表示にしたいことがよくあります。要素を選択してキーボードのHキーを押すと、要素を素早く非表示にできます。

Image for post

キーボードのHを押す。

この操作で、対応する要素にvisibility: hidden !important;のスタイルが追加されます。

12. DOM要素をグローバル一時変数に格納する。

ConsoleでDOM要素を素早く参照したい場合は、次のようにします。

  1. Elementsを選択する。
  2. マウスを右クリックする。
  3. グローバル変数として保存する。

Image for post

関連記事

シニア開発者みたいにVSCodeを使おう
コードエディタにVSCodeを選ぶなら知っておきたい5のテクニック
levelup.gitconnected.com

翻訳協力

この記事は以下の方々のご協力により公開する事ができました。改めて感謝致します。

Original Author: bytefish
Original Article: Use Chrome DevTools Like a Senior Frontend Developer
Thank you for letting us share your knowledge!

選定担当: @gracen
翻訳担当: @gracen
監査担当: -
公開担当: @gracen

ご意見・ご感想をお待ちしております

今回の記事はいかがでしたか?
・こういう記事が読みたい
・こういうところが良かった
・こうした方が良いのではないか
などなど、率直なご意見を募集しております。
頂いたお声は、今後の記事の質向上に役立たせて頂きますので、お気軽に
コメント欄にてご投稿ください。Twitterでもご意見を受け付けております。
皆様のメッセージをお待ちしております。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【ルビ振り】形態素解析で取得した読みがなが原文のどの文字と対応するか調べる【javascript】

概要 形態素解析で読みがなを取得し、取得した読みがなの各文字が原文のどの文字と対応するか調べる関数を作りました。形態素解析だけだと単語の対応までしかわからず、もっと細かい対応を知る必要があったので、作りました。主な用途はルビ振りです。 基本は、以前書いた記事のjavascript版です。 今回は以下のような出力を得ることを目指します。 入力:str 解析したい文章。 出力:objectのlist [{"pronunciation":"カ","surface":"解","in_surface_pos":0,"type":"nonkana"}, {"pronunciation":"イ","surface":"解","in_surface_pos":1,"type":"nonkana"}, {"pronunciation":"セ","surface":"析","in_surface_pos":0,"type":"nonkana"}, {"pronunciation":"キ","surface":"析","in_surface_pos":1,"type":"nonkana"}, {"pronunciation":"シ","surface":"し","in_surface_pos":0,"type":"hira"}, {"pronunciation":"タ","surface":"た","in_surface_pos":0,"type":"hira"}, {"pronunciation":"イ","surface":"い","in_surface_pos":0,"type":"hira"}, {"pronunciation":"ブ","surface":"文","in_surface_pos":0,"type":"nonkana"}, {"pronunciation":"ン","surface":"文","in_surface_pos":1,"type":"nonkana"}, {"pronunciation":"シ","surface":"章","in_surface_pos":0,"type":"nonkana"}, {"pronunciation":"ョ","surface":"章","in_surface_pos":1,"type":"nonkana"}, {"pronunciation":"ー","surface":"章","in_surface_pos":2,"type":"nonkana"}, {"pronunciation":"。","surface":"。","in_surface_pos":0,"type":"sign"}] Github: https://github.com/JiroShimaya/KanaCorresponadance Demo: https://jiroshimaya.github.io/KanaCorresponadance/ 処理の流れ 入力(漢字仮名交じり文)に対し、以下の順序で処理を行っています。 入力を形態素解析し、発音(カタカナ)を含む情報を取得する。 表層形をかな部分とかな以外の部分に分ける 分けられた表層形の各要素と対応する発音のかな部分を見つける。 発音の各文字と表層形の対応を見つける。 準備 javascriptで形態素解析をするため、kuromoji.jsを使えるようにします。 環境はブラウザでもnode.jsでも構いません。今回はブラウザです。 入力の形態素解析 解析したい文章textとkuromojiのtokenizerを引数として、形態素解析結果を返す //kuromojiのtokenizerによる形態素解析結果を少し修正して返す function tokenize(text, tokenizer){ if(!text)return []; const result = []; let tokens = tokenizer.tokenize(text); tokens = tokens.map(token=>{ let surface = token.surface_form; //あとで一括で処理するため、発音が定義されていなければ、surfaceを代入しておく if(!token.pronunciation){ token.pronunciation=surface; } return token; }); return tokens; } 表層形のかな部分とそれ以外を分ける 漢字かな交じりの入力文textを、正規表現を使って、カタカナ、ひらがな、カナ以外、の部分に分離します。 kana_correspondance.js //表層形のかな部分とそれ以外を分割し、タグを付けて返す。 function kanaTokenize (text) { //例外処理。万が一空文字であれば空のリストを返す if(text == "") return []; //正規表現の宣言 let re = /(?<kata>[ァ-ヴー]+)|(?<hira>[ぁ-ゔー]+)|(?<nonkana>[^ぁ-ゔァ-ヴー]+)/g //カタカナ、ひらがな、カナ以外にグループマッチ //マッチする文字列を種類とともに取得 let match = text.matchAll(re); match = [...match].map(m=>m.groups); let output = match.map(m=>{ let token = {} for(let type in m){ if(m[type]){ token = {"surface":m[type],"type":type} break; } } return token; }); return output; } 分けられた表層形の各要素と対応する発音文字列を見つける。 カタカナ、ひらがな、それ以外に分けられた表層形separated_surfaceと表層形全体の発音pronunciationを引数として、表層系の各要素がpronunciationのどの部分と対応する見つけます。 kana_correspondance.js //ひらがなをカタカナに変換 function hiraToKata (str) { return str.replace(/[\u3041-\u3096]/g, function(match) { var chr = match.charCodeAt(0) + 0x60; return String.fromCharCode(chr); }); } function kanaAllocate (separated_surface, pronunciation) { //例外処理。万が一表層形の長さが0のとき、空の配列を返す if(separated_surface.length == 0) return []; //カナ始まりかどうかを取得 let first_kana_index = 1; if(separated_surface[0]["type"] != "nonkana") first_kana_index = 0; let first_nonkana_index = (1-first_kana_index); let output = [] let rest_text = pronunciation; for(let i=0;i<separated_surface.length;i++){ //surfaceのカナ部分がpronunciationのどこから始まっているかを取得 let type = separated_surface[i]["type"] let surface = separated_surface[i]["surface"] if(type == "nonkana") continue; let katakana = surface; if(type == "hira") katakana = hiraToKata(surface); let start = rest_text.indexOf(katakana); //カナ部分の始まりが途中からだったら、始めのカナ以外の部分を先に格納する if(start > 0){ let nonkana = separated_surface[i-1] output.push({"surface":nonkana["surface"],"pronunciation":rest_text.slice(0,start-rest_text.length), "type":nonkana["type"]}); rest_text = rest_text.slice(start); } //カナ部分の終わりまでを、格納する output.push({"surface":surface,"pronunciation":rest_text.slice(0,katakana.length),"type":type}); rest_text = rest_text.slice(katakana.length); } //ループを終えてもカナ以外の部分が残っていたら追加する if(rest_text != ""){//rest_textが空文字とならないのはsurfaceがカナ以外で終わるとき let last = separated_surface[separated_surface.length-1]; output.push({"surface":last["surface"], "pronunciation":rest_text,"type":last["type"]}); } return output; } 漢字と発音の対応を辞書ベースで調べる 単漢字の読みがなに関する辞書を使って、熟語surfaceと発音pronunciationの対応を調べます。 辞書に情報がなかった場合は、各漢字に平均的に発音文字列を割り当てます。 今回はWikipediaから取得した情報で常用漢字辞書を作りました。 kanjiyomi.json { "亜": [ "ア" ], "哀": [ "アワ", "アイ" ], "挨": [ "アイ" ], "愛": [ "アイ" ], "曖": [ "アイ" ], "悪": [ "ワル", "アク", "オ" ], ... kana_correspondance.js //辞書ベースで、漢字(熟語)と発音のなるべく細かい対応を見つける //pronunciationはsurfaceよりも長い必要あり function kanjiAllocate (surface, pronunciation, kanji_dict = {}) { let rest_text = pronunciation; let skipped_char = ""; let output = []; for(let i=0;i<surface.length;i++){ let char = surface[i]; if(char in kanji_dict == false){ skipped_char += char; continue; } let yomi_candidates = kanji_dict[char];//長さの降順にソート済みとする let start = -1; let yomi = ""; for(let y of yomi_candidates){ start = rest_text.indexOf(y); if(start >= 0){ yomi = y; break; } } //マッチする読みが見つからなければスキップ if(start == -1){ skipped_char += char; continue; } if(start > 0){ if(output.length == 0){ if(skipped_char != ""){ output.push([skipped_char, rest_text.slice(0,start)]); skipped_char = ""; rest_text = rest_text.slice(start); output.push([char, yomi]); rest_text = rest_text.slice(yomi.length); }else{ output.push([char, rest_text.slice(0, start+yomi.length)]); rest_text = rest_text.slice(start+yomi.length); } }else{ if(skipped_char != 0){ output.push([skipped_char, rest_text.slice(0,start)]); skipped_char = ""; rest_text = rest_text.slice(start); output.push([char, yomi]); rest_text = rest_text.slice(yomi.length); }else{ output[output.length-1][1]+= rest_text.slice(0, start); rest_text = rest_text.slice(start); output.push([char, yomi]); rest_text = rest_text.slice(yomi.length); } } }else{ output.push([char, yomi]); rest_text = rest_text.slice(yomi.length); } } //ループで処理しきれなかった文字列の処理 if(skipped_char != ""){ if(rest_text != ""){ console.log(skipped_char, rest_text); output.push([skipped_char, rest_text]); }else{ if(output.length == 0){ //たぶんほとんどないケース output.push([skipped_char, rest_text]); }else{ output[output.length-1][0]+=skipped_char; } } }else{ if(rest_text != ""){ if(output.length == 0){ //この分岐はたぶんない }else{ output[output.length-1][1] += rest_text; } } } output = output.map(function([surface, yomi]){ return charAllocate(surface, yomi); }); output = output.flat(); return output; } 取得したペアの各要素を均等に対応付ける 表層形surfaceに発音pronuncationを均等に対応付けます。 kana_correspondance.js function charAllocate (surface, pronunciation) { let id = {surface: "surface", pronunciation: "pronunciation"} let text = {surface: surface, pronunciation: pronunciation} let longer = id.surface; let shorter = id.pronunciation; if(surface.length <= pronunciation.length){ longer = id.pronunciation; shorter = id.surface; } let plusone = text[longer].length % text[shorter].length; let contentlen = Math.floor(text[longer].length/text[shorter].length); let output = []; let longer_pos = 0; for(let i = 0; i<text[shorter].length; i++){ let longer_content_len = contentlen; if(i<plusone) longer_content_len += 1; //pronunciationが長いときと短いときで処理を変える if(longer == id.pronunciation){//pronunciationが長いとき、pronunciation1文字ずつに重複する1文字のsurfaceを対応させ、in_order_posで区別する for(let j=0;j<longer_content_len; j++){ let info = {} info[longer]=text[longer][longer_pos+j]; info[shorter] = text[shorter][i]; info["in_surface_pos"] = j; output.push(info); } longer_pos += longer_content_len; }else{//pronunciationが短いとき、pronunciation1文字にsurface複数文字を対応させる let info = {} info[longer] = text[longer].slice(longer_pos, longer_pos + longer_content_len); info[shorter] = text[shorter][i]; info["in_surface_pos"] = 0; longer_pos += 1; console.log("info",info); output.push(info); } } return output; } 上記を組み合わせて読みがなと原文の対応を得る関数を作る これまでの関数を組み合わせて、漢字仮名交じり文surfaceとその発音pronunciationの対応を出力する関数を作ります。 pronunciationの1文字ずつに対し、当てはまるsurfaceの文字がなにか、その文字において何番目に発音されるか、surfaceの分類(ひらがな、カタカナ、記号、カナ以外)を出力します。 kana_correspondance.js function getCharCorrespondance(text, tokenizer, kanji_dict = {}){ //形態素解析 let tokens = tokenize(text, tokenizer); //カナ部分とカナ以外部分の対応を見つける let kana_correspondance = tokens.map(token=>{ let surface = token["surface_form"] let pronunciation = token["pronunciation"] let pos = token["pos"] //surfaceを解析 let separated = kanaTokenize(surface); //カナ、カナ以外の対応を見つける let correspondance = kanaAllocate(separated, pronunciation); //記号の場合はtypeに記号を設定する if(pos == "記号"){ correspondance = correspondance.map(v=>{ v["type"] = "sign"; return v; }); } return correspondance; }); kana_correspondance = kana_correspondance.flat(); //1重のリストにする //1文字ずつの対応を見つける let char_correspondance = kana_correspondance.map(token => { let surface = token["surface"] let type = token["type"] let pronunciation = token["pronunciation"] let correspondance = null; if(type == "nonkana"){ correspondance = kanjiAllocate(surface, pronunciation, kanji_dict); }else{ correspondance = charAllocate(surface, pronunciation); } correspondance = correspondance.map(v => { v["type"] = type; return v; }); return correspondance; }); char_correspondance = char_correspondance.flat(); return char_correspondance; } 参考にさせていただいた情報 kuromoji.js JavaScriptでカタカナをひらがなに変換する(その逆も) JavaScript: 正規表現/gと名前付きグループを併用する小技
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む