20210603のJavaScriptに関する記事は15件です。

JSの日時処理を理解する

現在日時を取得する new Date()を使用することで現在日時を取得できる var date = new Date(); console.log(date); Sun May 30 2021 13:44:33 GMT+0900 (JST) 上記のように現在日時が出力される 特定の日付、日時を取得する new Date()の引数に取得したい日付、日時の値を設定することで特定の日付や日時を取得できる 引数の設定方法は下記の3種類 ① new Date(dateString) dateStringにはDate.parse()で変換可能な文字列を指定する var date = new Date('2021/01/01 10:00:00'); var date2 = new Date('2021-01-01'); console.log(date); console.log(date2); Fri Jan 01 2021 10:00:00 GMT+0900 (JST) Fri Jan 01 2021 09:00:00 GMT+0900 (JST) ※日付のみを指定するした場合、日本時間の9:00で表示される ② new Date(value) 世界協定時 (UTC) 1970年1月1日午前0時0分0秒からのミリ秒数を整数値で表した値を設定する var date = new Date('2021/01/01 10:00:00'); console.log(date); var date2 = new Date(1609462800000); console.log(date2); Fri Jan 01 2021 10:00:00 GMT+0900 (JST) Fri Jan 01 2021 10:00:00 GMT+0900 (JST) ③ new Date(year, monthIndex [, day [, hours [, minutes [, seconds [, milliseconds]]]]]) カンマ区切りで年、月、日、時、分、秒を整数値で設定する。 var date = new Date(2021, 1, 1, 10, 10, 10); console.log(date); Mon Feb 01 2021 10:10:10 GMT+0900 (JST) 日付を編集して取得する 年を変更する var date = new Date('2021/01/01 10:00:00'); console.log(date); var year = date.getFullYear() + 1; date.setFullYear(year); console.log(date); Fri Jan 01 2021 10:00:00 GMT+0900 (JST) Sat Jan 01 2022 10:00:00 GMT+0900 (JST) 月を変更する var date = new Date('2021/01/01 10:00:00'); console.log(date); var month = date.getMonth() + 1; date.setMonth(month); console.log(date); Fri Jan 01 2021 10:00:00 GMT+0900 (JST) Mon Feb 01 2021 10:00:00 GMT+0900 (JST) 日を変更する var date = new Date('2021/01/01 10:00:00'); console.log(date); var day = date.getDate() + 1; date.setDate(day); console.log(date); Fri Jan 01 2021 10:00:00 GMT+0900 (JST) Sat Jan 02 2021 10:00:00 GMT+0900 (JST) ※注意点 getMonth()の取得値は取得値は0から始まるため、何月か確認するような判定を作成する場合はgetMonth() + 1をする必要がある 月 getMonthの取得値 1月 0 2月 1 3月 2 4月 3 5月 4 6月 5 7月 6 8月 7 9月 8 10月 9 11月 10 12月 11 コード例 var date = new Date('2021/01/01 10:00:00'); // 1月であることを判定する場合 if ((date.getMonth() + 1) == 1) { } 時間を変更する var date = new Date('2021/01/01 10:00:00'); console.log(date); var hour = date.getHours() + 1; date.setHours(hour); var minutes = date.getMinutes() + 1; date.setMinutes(minutes); var seconds = date.getSeconds() + 1; date.setSeconds(seconds) console.log(date); Fri Jan 01 2021 10:00:00 GMT+0900 (JST) Fri Jan 01 2021 11:01:01 GMT+0900 (JST) 参考サイト
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

react-splineを使って、自分で作った3Dモデルを動かしてみた。

ふと検索したら「react-spline」というライブラリがある事を知ったので、触ってみます。 また、これが qiita初投稿です。書き方が微妙かもしれませんがご了承下さい。 まずは作ったものを紹介 localで動いているのが分かると思います。 もともと3Dモデルが用意されていたのですが、 ちょっとだけ手を加えて右翼の支柱を2本取っておきました。 実装手順 2ステップで説明していきます。 「2」はモデル作成の方法と、作成したモデルを取り込む手順です。 私は3Dモデル作成は詳しく無いので、動画のとおり 棒を2本無くしただけです。 自作モデルは作らなくていいから、とりあえず動かしたい人 自作モデルを作成して、reactに組み込みたい人 1.自作モデルは作らなくていいから、とりあえず動かしたい人 早速説明していきます。でライブラリをクローンしてきます。 クローンしたらexampleフォルダに入って、npm iです。 git clone https://github.com/utkarshdubey/react-spline.git cd react-spline cd example npm install (または、 yarn install) あとは、npm install で完了です。 npm start (または、 yarn start) 動かすだけならこれで完了 うまくいけば 「localhost:3000」で以下の画面が表示されて、ぬるぬる動かせます。 ソース的にはこんな感じです。まんまreactですね。 「import { SPLINE_EXPORTED_SCENE } from './scene'」の部分でjson形式のモデルを読み込んでいるんですが、次の項目でその部分を作成していきます。 import React from 'react' import { SPLINE_EXPORTED_SCENE } from './scene' import { Spline } from 'react-spline' const App = () => { return ( <div> <div style={{ backgroundSize: 'cover', display: 'flex', justifyContent: 'center', alignItems: 'center' }} > <h1 style={{ position: 'absolute', zIndex: 1, color: '#fff' }}> If you see this, react-spline works. </h1> <Spline scene={SPLINE_EXPORTED_SCENE} /> </div> </div> ) } export default App 2.自作モデルを作成して、reactに組み込みたい人 モデルの作成 ここからは「自作モデルを作成して、reactに組み込みたい人」編に突入です。 公式HPから「Spline」をダウンロードします。「download now」のクリックだけで難しくなかったです。 ダウンロードしたらこんな感じの画面になります。 赤部分をクリックして、少しだけモデルに修正を加えていきます。 私は、3Dモデルをいじるスキルは低いので、とりあえず大事そうな支柱を2本抜いておきました。 作成した3Dモデルをexport これが簡単でびっくりしました。 以下はexportからブラウザで表示までの流れです。 作成された3Dモデル ※多分このモデルが見れるのは有効期限があると思います。 2021/6/3 作成したモデルをjson形式に変更する 「作成された3Dモデル」の最後に「scene.json」を付与します。 ・json出力用URL https://my.spline.design/airplane-d369decdf14902501f8c46fa4fa24536/scene.json サンプルのモデルを自作モデルと差し替える ダウンロードした「scene.json」を、元々ある「scense.js」と置き換えます。 この際に注意するのは、 「scene.json」と「scense.js」は拡張しが違う ダウンロードしてきた方は先頭に「export const SPLINE_EXPORTED_SCENE = 」が無いので付ける ダウンロードしてきた方は最後にセミコロン(;)が無いので付ける。 この状態でうまくいけば、自作の3Dモデルがローカルで表示されているはずです! 分からなかったこと 実際に動いている画面をみると一部モノクロになっており、色が反映されていない箇所があります。 その部分については、原因がわかりませんでした。 違うモデルだったら、大丈夫だったのかな・・ 最後に 最近ブログを作成しました。reactにも触れているので良かったら見てみてください。 ・いはかよ https://www.ihakayo.com/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TweenmaxのScrollTriggerをfullpage.jsみたいに使うメモ

HTML <section class="panel"> <p>Panel1</p> </section> <section class="panel"> <p>Panel2</p> </section> <section class="panel"> <p>Panel3</p> </section> <section class="panel"> <p>Panel4</p> </section> SCSS .panel{ display: block; height: 100vh; width: 100%; } JavaScript import gsap from "gsap"; import { ScrollTrigger, ScrollToPlugin } from "gsap/all"; gsap.registerPlugin(ScrollTrigger, ScrollToPlugin); const panels = document.querySelectorAll(".panel"); function goToPanel(panel) { gsap.to(window, { scrollTo: { y: panel, autoKill: false }, duration: 1.5, }); } panels.forEach((panel) => { ScrollTrigger.create({ trigger: panel, end: "bottom top+=1", markers: 1, onEnter: () => goToPanel(panel), onEnterBack: () => goToPanel(panel), }); }); ScrollTrigger.addEventListener("scrollEnd", () => console.log("scrollEnd"));
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GSAPのScrollTriggerをfullpage.jsみたいに使うメモ

snapを使うと画面半分までスクロールしないと自動スクロールしてくれないのを すぐスクロールされるようにしたものです。 HTML <section class="panel"> <p>Panel1</p> </section> <section class="panel"> <p>Panel2</p> </section> <section class="panel"> <p>Panel3</p> </section> <section class="panel"> <p>Panel4</p> </section> SCSS .panel{ display: block; height: 100vh; width: 100%; } JavaScript import gsap from "gsap"; import { ScrollTrigger, ScrollToPlugin } from "gsap/all"; gsap.registerPlugin(ScrollTrigger, ScrollToPlugin); const panels = document.querySelectorAll(".panel"); function goToPanel(panel) { gsap.to(window, { scrollTo: { y: panel, autoKill: false }, duration: 1.5, }); } panels.forEach((panel) => { ScrollTrigger.create({ trigger: panel, end: "bottom top+=1", markers: 1, onEnter: () => goToPanel(panel), onEnterBack: () => goToPanel(panel), }); }); ScrollTrigger.addEventListener("scrollEnd", () => console.log("scrollEnd"));
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】非同期通信入門

目標 今回は、ボタンを押すとjsonで取得したリストを10件ずつ表示していく機能を非同期処理で実装していきます。 はじめにXMLHttpRequestを使用した非同期処理の方法を見ていき、 そのあとでasync / awaitを利用した非同期処理の方法を見ていきます。 jsonデータ 今回使用させていただくデータはjsonplaceholderのuser情報です。 完成コード 要所ごとに詳細に説明していきます。 <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>非同期処理でユーザーのリストを追加してみよう</title> </head> <body> <ol id="lists"></ol> <button id="btn">もっと見る</button> </body> <script src="index.js"></script> </html> let xhr = new XMLHttpRequest(); xhr.open('GET', 'https://jsonplaceholder.typicode.com/users', true) xhr.responseType = 'json' xhr.send(null) xhr.onload = function(e) { if (xhr.readyState == 4) { if (xhr.status == 200 ) { let btn = document.getElementById('btn'); let users = xhr.response; let lists = document.getElementById('lists'); btn.addEventListener('click', function() { users.forEach(function (user) { let li = document.createElement('li'); li.innerText = user.name lists.appendChild(li) }); }); } } } 基本このフォーマットなので、定型文として覚えて大丈夫です。 各処理が実際に何をしているかフローを見ていきましょう。 new演算子を利用してオブジェクトを生成します。 open( )メソッドを利用して、メソッド、URL、非同期処理の有無を引数に渡します。第3引数をfalseにすると、同期処理になります。 responseTypeでjsonを指定します。 send( )メソッドでAPIを呼び出して通信を開始します。nullは送るものがないことを意味します。open( )のメソッドがPOSTの場合、send( )に処理したいデータを格納できます。 リクエストに対して返ってきたレスポンスの処理をxhr.onload=function(e){}に格納します。 readyStateは現在の通信状況を示しています。4は通信完了という意味になります。 statusで200は正常に通信出来ていることを示しています。もしファイルが存在しなかったりすると404が返ってきます。 xhr.responseでレスポンスで返ってきたデータをコンソールに表示します。 let xhr = new XMLHttpRequest(); // 今回はデータを取得するだけなのでGETを渡します。 // 非同期処理なのでtrueを渡します。 xhr.open('GET', 'https://jsonplaceholder.typicode.com/users', true) xhr.responseType = 'json' xhr.send(null) xhr.onload = function(e) { if (xhr.readyState == 4) { if (xhr.status == 200 ) { console.log(xhr.response) // データが返る } } } イベント処理です。 今回ここで行われている処理の説明は割愛させていただきます。 // 省略 let btn = document.getElementById('btn'); let lists = document.getElementById('lists'); let users = xhr.response; // 変数usersにデータを代入 btn.addEventListener('click', function() { users.forEach(function (user) { // 新しく生成したliにuser情報を入れます let li = document.createElement('li'); li.innerText = user.name lists.appendChild(li) }); }); // 省略 async await 今度はasync / awaitを使って同じ処理を書き換えていきます。 let lists = document.getElementById('lists'); let btn = document.getElementById('btn'); btn.addEventListener('click', async function () { // URLを格納します。fetchを使うと外部との連携ができるようにになります。 const res = await fetch("https://jsonplaceholder.typicode.com/users"); // レスポンスで返ってきたデータをjson形式で格納します。 const users = await res.json(); // forEach文で新しく生成したli要素にuser情報をいれます。 users.forEach(function (user) { const list = document.createElement('list'); list.innerText = user.name lists.appendChild(list); }); }) これで書き換え終了です。無事に表示できたましたでしょうか。 追加で初回のロードした時に既に10件表表示されている実装も行いましょう。 また記述が上々になったり分割できる所が見受けられるのでリファクタリングも行いましょう。 let lists = document.getElementById('lists'); let btn = document.getElementById('btn'); async function getUsers() { const res = await fetch("https://jsonplaceholder.typicode.com/users"); const users = await res.json(); return users } async function listUsers() { const users = await getUsers(); users.forEach(addList); } function addList(user) { const list = document.createElement('list'); list.innerText = user.name list.style.display = 'block' lists.appendChild(list); } window.addEventListener('load', listUsers) btn.addEventListener('click', listUsers) 以上
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】正規表現ってそんなに難しくないよ

正規表現の勉強をしたのでまとめました。 正規表現とは 正規表現とは、文字列のパターンマッチングに特化した言語です。 正規表現の用語 正規表現の用語を整理します。 パターン 探したい規則をパターン(pattern)と呼びます。 シーケンス パターンを探す対象文字列を入力シーケンスと呼びます。 マッチ 入力シーケンスの中でパターンに一致する文字列が見つかることをマッチ(match)すると言います。 パターンとシーケンスを噛み砕いて説明すると、 「lで始まりeで終わる単語」がパターンで、"I love JavaScript"が入力シーケンスです。 正規表現の文法 正規表現は言語の一種なので文法を持ちます。 しかし、JavaScriptから想像するような文法とは異なります。 あくまでパターンマッチングのための記法の一つと見なす方が理解しやすいと思います。 正規表現のメタ文字 メタ文字 意味 . 任意の一文字 \s 空白文字 \S 非空白文字 \w 単語構成文字(英数字とアンダースコア文字) \W 非単語構成文字 \d 数字 \D 数字以外の文字 \b 単語境界 \B 非単語境界 ^ 行頭 $ 行末 X? Xの0文字または1文字の繰り返し X?? Xの0文字または1文字の繰り返し(非貪欲) X* Xの0文字以上の繰り返し X*? Xの0文字以上の繰り返し(非貪欲) X+ Xの1文字以上の繰り返し X+? Xの0文字以上の繰り返し(非貪欲) X{n} Xのn回の繰り返し X{n}? Xのn回の繰り返し(非貪欲) X{n,} Xのn回以上の繰り返し X{n,}? Xのn回以上の繰り返し(非貪欲) X{n,m} Xのn回からm回の繰り返し X{n,m}? Xのn回からm回の繰り返し(非貪欲) X Y [XYZ] XまたはYまたはZの1文字 [^XYZ] XでもYでもZでもない1文字 (X) グループ化(参照可能) \数字 マッチグループの参照 (?:X) グループ化のみ(参照しない) X(?=Y) Xの後にYが続く場合にマッチ X(?!Y) Xの後にYが続かない場合にマッチ メタ文字を使う記号をメタ記号扱いしたく無い場合はバックスラッシュ文字でエスケープします。 例えば、ドット文字(.)にマッチさせたいパターンには\.と書きます。 バックスラッシュ文字にマッチするパターンを書く時は\\と書きます。 その他にもJavaScript文字列と似たようなエスケープシーケンスが使えます。 正規表現のエスケープシーケンス 特殊文字(エスケープシーケンス) 意味 \n 改行(LF) \t タブ \r 改行(CR) \f フィード \f 垂直タブ 正規表現のフラグ フラグ 説明 g グローバルマッチ i 英字の大文字小文字を区別しない m マルチライン。^と$がそれぞれの改行の先頭や末尾にもマッチするようになる 正規表現 JavaScriptで正規表現を扱うには正規表現オブジェクトを使います。 正規表現オブジェクトはRegExpクラスののインスタンスオブジェクトです。 また、リテラル表記でも生成可能です。 var reg = new RegExp('^[0-9]$'); // 正規表現 ^[0-9]$ のパターンRegExpインスタンス生成 var reg = /^[0-9]$/; // リテラル表記でも生成できます。 matchメソッド var test = 'abcdefg' // 変数testに入力シーケンスを代入 var result = test.match(/[a-z]/gi); // 変数resultにマッチした結果を配列で返す console.log(result); (7) ["a", "b", "c", "d", "e", "f", "g"] matchメソッドは、パターンにマッチした部分文字列を要素に持つ配列を返します。 グローバルフラグの有無で返り値の意味が変わります。 グローバルフラグが存在してる場合は、入力シーケンスの中でマッチした全ての部分文字列を持つ配列を返します。 execメソッド var test = "abcdefg" var result = /(.+)de(fg)/.exec(test); console.log(result); (3) ["abcdefg", "abc", "fg", index: 0, input: "abcdefg", groups: undefined] execメソッドは、パターンにマッチした部分文字列を要素に持つ配列を返します。 返した配列の[0]には最後にマッチした文字が返ります。 [1]以降は、カッコ( )で囲まれた部分の文字列のマッチがセットされます。 マッチしない場合は、nullを返します。 testメソッド var test = '0123456789' // 変数testに入力シーケンスを代入 var result = /[0-9]{6}/.test(test) // testメソッドの引数に変数testを渡す console.log(result); true // 入力シーケンス'0123456789'に対してマッチ => 結果はtrue 入力シーケンスを検索し、結果はブーリアン形式で返す。 searchメソッド var test = "abcdefg"; var result = test.search(/fg/); console.log(result); 3 searchメソッドは、指定された正規表現で最初にマッチした文字位置を返す。 0からカウントして数字を返すが、マッチした文字列が存在しない場合は、戻り値として-1を返す。 replaceメソッド var test = "abc def ghi jkl"; var result = test.replace(/\s/g, ','); // 変数testに格納された文字列の空白を「,」と置換 console.log(result); abc,def,ghi,jkl var test = "Tanaka Taro"; var result = test.replace(/(\w+)\s(\w+)/, "$2, $1"); // $1にはTanaka $2にはTaro console.log(result); Taro,Tanaka replaceメソッドは、正規表現でマッチした文字列を置換することができます。 splitメソッド console.log('2021/5/30'.split(/[\/\.\-]/)); // 「/」,「-」,「.」を区切って年月日に分割するコード ["2021", "5", "30"] console.log('2021-5-30'.split(/[\/\.\-]/)); ["2021", "5", "30"] console.log('2021.5.30'.split(/[\/\.\-]/)); ["2021", "5", "30"] splitメソッドは、正規表現で文字列を分割することができる。 splitメソッドの第二引数に分割回数の上限値を渡すこともできます。 以上です。 ちょっとずつ使いこなして正規表現マスターになります!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】正規表現を好きになろう

正規表現の勉強をしたのでまとめました。 正規表現とは 正規表現とは、文字列のパターンマッチングに特化した言語です。 正規表現の用語 正規表現の用語を整理します。 パターン 探したい規則をパターン(pattern)と呼びます。 シーケンス パターンを探す対象文字列を入力シーケンスと呼びます。 マッチ 入力シーケンスの中でパターンに一致する文字列が見つかることをマッチ(match)すると言います。 パターンとシーケンスを噛み砕いて説明すると、 「lで始まりeで終わる単語」がパターンで、"I love JavaScript"が入力シーケンスです。 正規表現の文法 正規表現は言語の一種なので文法を持ちます。 しかし、JavaScriptから想像するような文法とは異なります。 あくまでパターンマッチングのための記法の一つと見なす方が理解しやすいと思います。 正規表現のメタ文字 メタ文字 意味 . 任意の一文字 \s 空白文字 \S 非空白文字 \w 単語構成文字(英数字とアンダースコア文字) \W 非単語構成文字 \d 数字 \D 数字以外の文字 \b 単語境界 \B 非単語境界 ^ 行頭 $ 行末 X? Xの0文字または1文字の繰り返し X?? Xの0文字または1文字の繰り返し(非貪欲) X* Xの0文字以上の繰り返し X*? Xの0文字以上の繰り返し(非貪欲) X+ Xの1文字以上の繰り返し X+? Xの0文字以上の繰り返し(非貪欲) X{n} Xのn回の繰り返し X{n}? Xのn回の繰り返し(非貪欲) X{n,} Xのn回以上の繰り返し X{n,}? Xのn回以上の繰り返し(非貪欲) X{n,m} Xのn回からm回の繰り返し X{n,m}? Xのn回からm回の繰り返し(非貪欲) X Y [XYZ] XまたはYまたはZの1文字 [^XYZ] XでもYでもZでもない1文字 (X) グループ化(参照可能) \数字 マッチグループの参照 (?:X) グループ化のみ(参照しない) X(?=Y) Xの後にYが続く場合にマッチ X(?!Y) Xの後にYが続かない場合にマッチ メタ文字を使う記号をメタ記号扱いしたく無い場合はバックスラッシュ文字でエスケープします。 例えば、ドット文字(.)にマッチさせたいパターンには\.と書きます。 バックスラッシュ文字にマッチするパターンを書く時は\\と書きます。 その他にもJavaScript文字列と似たようなエスケープシーケンスが使えます。 正規表現のエスケープシーケンス 特殊文字(エスケープシーケンス) 意味 \n 改行(LF) \t タブ \r 改行(CR) \f フィード \f 垂直タブ 正規表現のフラグ フラグ 説明 g グローバルマッチ i 英字の大文字小文字を区別しない m マルチライン。^と$がそれぞれの改行の先頭や末尾にもマッチするようになる 正規表現 JavaScriptで正規表現を扱うには正規表現オブジェクトを使います。 正規表現オブジェクトはRegExpクラスののインスタンスオブジェクトです。 また、リテラル表記でも生成可能です。 var reg = new RegExp('^[0-9]$'); // 正規表現 ^[0-9]$ のパターンRegExpインスタンス生成 var reg = /^[0-9]$/; // リテラル表記でも生成できます。 matchメソッド var test = 'abcdefg' // 変数testに入力シーケンスを代入 var result = test.match(/[a-z]/gi); // 変数resultにマッチした結果を配列で返す console.log(result); (7) ["a", "b", "c", "d", "e", "f", "g"] matchメソッドは、パターンにマッチした部分文字列を要素に持つ配列を返します。 グローバルフラグの有無で返り値の意味が変わります。 グローバルフラグが存在してる場合は、入力シーケンスの中でマッチした全ての部分文字列を持つ配列を返します。 execメソッド var test = "abcdefg" var result = /(.+)de(fg)/.exec(test); console.log(result); (3) ["abcdefg", "abc", "fg", index: 0, input: "abcdefg", groups: undefined] execメソッドは、パターンにマッチした部分文字列を要素に持つ配列を返します。 返した配列の[0]にはマッチした範囲の文字列全体が返ります。 [1]以降は、カッコ( )で囲まれた部分の文字列のマッチがセットされます。 マッチしない場合は、nullを返します。 testメソッド var test = '0123456789' // 変数testに入力シーケンスを代入 var result = /[0-9]{6}/.test(test) // testメソッドの引数に変数testを渡す console.log(result); true // 入力シーケンス'0123456789'に対してマッチ => 結果はtrue 入力シーケンスを検索し、結果はブーリアン形式で返す。 searchメソッド var test = "abcdefg"; var result = test.search(/fg/); console.log(result); 5 searchメソッドは、指定された正規表現で最初にマッチした文字位置を返す。 0からカウントして数字を返すが、マッチした文字列が存在しない場合は、戻り値として-1を返す。 replaceメソッド var test = "abc def ghi jkl"; var result = test.replace(/\s/g, ','); // 変数testに格納された文字列の空白を「,」と置換 console.log(result); abc,def,ghi,jkl var test = "Tanaka Taro"; var result = test.replace(/(\w+)\s(\w+)/, "$2, $1"); // $1にはTanaka $2にはTaro console.log(result); Taro,Tanaka replaceメソッドは、正規表現でマッチした文字列を置換することができます。 splitメソッド console.log('2021/5/30'.split(/[\/\.\-]/)); // 「/」,「-」,「.」を区切って年月日に分割するコード ["2021", "5", "30"] console.log('2021-5-30'.split(/[\/\.\-]/)); ["2021", "5", "30"] console.log('2021.5.30'.split(/[\/\.\-]/)); ["2021", "5", "30"] splitメソッドは、正規表現で文字列を分割することができる。 splitメソッドの第二引数に分割回数の上限値を渡すこともできます。 以上です。 ちょっとずつ使いこなして正規表現マスターになります!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

indesign スクリプト 属性の値を置換(構造内の選択された)

構造内の選択された属性の値を置換するスクリプトはこれで良いのかな・・・? /* 構造内の選択された属性の値を置換する 更新 2021/06/04 */ // アプリ指定 #target "indesign"; // スクリプト名 var scriptName = "属性の値を置換(構造内の選択された)"; //スクリプト動作指定(一つのアンドゥ履歴にする、及び、アンドゥ名の指定) app.doScript(function () { // ダイアログ var dialogueFlg = confirm("「検索と置換」の正規表現の検索文字列と置換文字列の設定でJavaScriptの正規表現で置換します\n" + "\n" + "JavaScriptの正規表現はIndesignの「検索と置換」の正規表現とは違う部分があります\n" + "例:$0(見つかったテキスト)は無い、$のエスケープは$$", "", scriptName); // Noの場合 if (dialogueFlg == false) { // スクリプトを終了 exit(); } // 正規表現の検索プロパティのフラグ var findGrepPreferencesPropertyFlg = false; // 正規表現の検索のプロパティ名の数だけ変数に入れて繰り返す for(var findGrepPreferencesProperty in app.findGrepPreferences.properties){ // 正規表現の検索のプロパティ名が「parent」、「bulletChar」、「numberingRestartPolicies」では無い場合 if(findGrepPreferencesProperty != "parent" && findGrepPreferencesProperty != "bulletChar" && findGrepPreferencesProperty != "numberingRestartPolicies"){ // 正規表現の検索のプロパティの内容が「NothingEnum.NOTHING」、「」、「null」ではない場合 if(app.findGrepPreferences[findGrepPreferencesProperty] != NothingEnum.NOTHING && app.findGrepPreferences[findGrepPreferencesProperty] != "" && app.findGrepPreferences[findGrepPreferencesProperty] != null){ // 正規表現の検索プロパティのフラグにtrueを入れる findGrepPreferencesPropertyFlg = true; // 繰り返しを抜ける break; } } } // 正規表現の検索プロパティのフラグがfalseの場合 if(findGrepPreferencesPropertyFlg == false){ // 警告 alert("「検索と置換」の正規表現の検索に値がありません"); // スクリプトを終了 exit(); } // 正規表現の検索文字に正規表現の検索形式の検索文字を入れる findGrepText = app.findGrepPreferences.findWhat; // エラーが発生した場合の処理 try{ // 正規表現オブジェクトを作り正規表現の検索文字に入れる findGrepText = new RegExp(findGrepText,"g"); // エラーの場合 }catch(e){ // 正規表現の検索文字にnullを入れる findGrepText = null; } // 選択されているオブジェクト記憶用 var selectObjects = app.activeDocument.selection; // すべての選択を解除 app.activeDocument.selection = null; // 置換数 var replaceNumber = 0; // 選択の数だけ繰り返す for (var i = 0; i < selectObjects.length; i++){ // 選択がXml要素の場合 if (selectObjects[i].constructor.name == "XMLAttribute") { // 正規表現での検索結果にXml要素の値を正規表現で検索した結果を入れる var findGrepResult = selectObjects[i].value.match(findGrepText); // 正規表現での検索結果がnullではない場合 if(findGrepResult != null){ // Xml要素の値にXml要素の値を正規表現で置換した結果を入れる selectObjects[i].value = selectObjects[i].value.replace(findGrepText,app.changeGrepPreferences.changeTo); // 選択に追加 selectObjects[i].select(SelectionOptions.addTo); // 置換数に検索された数を追加 replaceNumber += findGrepResult.length; } } } // 置換数が0より多い場合 if(replaceNumber > 0){ // 結果表示 alert("置換された属性を選択しました" + "\n" + "置換数 " + replaceNumber,scriptName); // 以外の場合 }else{ // 選択状態を元に戻す app.activeDocument.selection = selectObjects; // 結果表示 alert("置換数 " + replaceNumber,scriptName); } //スクリプト動作指定(一つのアンドゥ履歴にする、及び、アンドゥ名の指定)の続き }, ScriptLanguage.JAVASCRIPT, [scriptName], UndoModes.ENTIRE_SCRIPT, scriptName);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

node.jsで、A処理が終わったらB処理を実行するアレを今更書いてみる

node.jsで、A処理が終わったらB処理を実行するアレを今更書いてみる | ハイテク好きが楽しめるウェブメディア off.tokyo node.jsに限らず、非同期処理を書いてると、上から順番に処理が走らずに変数が未定義のまま次の処理が走ってしまうことがあります。 これを解決するためには、コールバックみたいなものを書いて、処理Aがリターンされたら、処理Bを実行するみたいなことを書かないといけません。 まあ、この手の詳しいことはいくらでも詳しく書いてる情報があるので、今回の記事は備忘録みたいな感じで書くだけです。 書いたコードはこんな感じです。 async function A() { try { // この中でawaitなりで非同期処理でapiとか叩く return "返り値"; } catch (err) { console.log("エラー発生"); console.log(err); } } こんな感じですね。tryの中でapiなりを叩いて、それを受け取ったらリターンする。 A().then(result => { console.log("Aの処理がリターンされたらBを発動させる"); B(result) }); resultはA()を実行して返ってきた値です。 非同期処理で普通に書いてると未定義で進んでしまうのを防ぎます。 node.jsで、A処理が終わったらB処理を実行するアレを今更書いてみる | ハイテク好きが楽しめるウェブメディア off.tokyo
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScropt [初心者の備忘録] テキストボックスに入力した数字で加減するカウンター

[やりたかったこと] テキストボックスを作成し、そこに入力された数値に対して、 プラスまたはマイナスボタンをクリックすると、その分だけ加算または減算されるだけのもの。 <div class="spinner_area"> <p id="counter" type="number">0</p> <input type="button" value="+" class="btnspinner" data-cal="1" data-target=".counter1" id="up" hovercolor="red"> <input type="number" value="1" class="counter1" data-max="500" data-min="0" id="textbox"> <input type="button" value="-" class="btnspinner" data-cal="-1" data-target=".counter1" id="down" hovercolor="red"> </div> window.addEventListener('DOMContentLoaded', ()=>{ const downbutton = document.querySelector('#down'); const upbutton = document.querySelector('#up'); const text = document.querySelector('#textbox'); const counter = document.querySelector('#counter'); downbutton.addEventListener('click', (event) => { counter.textContent=parseInt(counter.textContent)-parseInt(text.value); }); upbutton.addEventListener('click', (event) => { counter.textContent=parseInt(counter.textContent)+parseInt(text.value);; }); });
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javaにrailsの変数を持たせるには

今回オリジナルアプリの作成で、railsの変数をjavaで使えないかと調べて実装できたのでここに記録。 結論から言うとgemを入れたら簡単に実装できた。 まず、使用するのはgonと言うgem gemfileに gem"gon"と記載 ターミナルにbundle install その後application.heml.erbのhead内に<%= include_gon %>と書くことでgonを読み取れるように 次に使用したいcontrollerの変数をjavaで使えるように   @movie = Movie.find(params[:id]) gon.movie = @movie 変数@movieにgonを加える あとはjsで変数を使えるように 今回はアラートを用いてアラートの内容に変数を用いたいため以下の記述 alert(正解は[${gon.movie.phrase}]です);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Javascript】Mapオブジェクト(iteration)

初めに Mapオブジェクトについて学習した内容のoutput用記事です。 ※内容に間違いなどがある場合はご指摘をよろしくお願いします。 ※こちらの記事はあくまでも個人で学習した内容のoutputとしての記事になります。 前回の記事: https://qiita.com/redrabbit1104/items/cc0d851da2a533cda694 Mapオブジェクトの生成 setメソッドを使わずにMapオブジェクトは以下のような方法で作れます。 const bookStore = new Map([ ["mon", { open: 10, close: 20 }], ["tue", { open: 11, close: 19 }], ["sun", { oepn: 0, close: 20 }], ]); console.log(bookStore); //Map(3) {"mon" => {…}, "tue" => {…}, "sun" => {…}} ObjectからMapオブジェクトへ Mapの引数は配列の中に配列が入っている構造ですので、Objectのentriesメソッドで生成したものを丸ごとMapの引数にすればObjectからMapオブジェクトに簡単に変換することができます。 const bookStore = { mon: { open: 10, close: 20 }, tue: { open: 11, close: 19 }, sun: { open: 0, close: 20 }, }; const entries = Object.entries(bookStore); console.log(entries); //(3) [Array(2), Array(2), Array(2)] const newMap = new Map(entries); console.log(newMap); //Map(3) {"mon" => {…}, "tue" => {…}, "sun" => {…}} for of文を使ったloop また、for ofを使ってkeyの値に該当するvalueだけをコンソールに表示することもできます。 const items = new Map([ ["name", "John"], [1, "school"], [2, "home"], [true, "Yes"], [false, "No"], ]); for (const [key, value] of items) { if (typeof key === "number") console.log(`Guess ${key}: ${value}`); }] //Guess 1: school // Guess 2: home typeofでキーの値が数字だけの場合に値を出力することができました。 Mapオブジェクトを配列にする Spread構文を使えば、Mapオブジェクトを配列にすることもできます。        console.log([...items]); //(6) [Array(2), Array(2), Array(2), Array(2), Array(2), Array(2)] console.log([...items.entries()]); //(6) [Array(2), Array(2), Array(2), Array(2), Array(2), Array(2)] console.log([...items.keys()]); //(6) ["name", 1, 2, "正解", true, false] console.log([...items.values()]); //(6) ["John", "school", "home", 2, "Yes", "No"] 参考サイト
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript初学者が現場で活躍するフロントエンドエンジニアにレビューしていただいた内容【もりけん塾】

前置き 私が所属している「もりけん塾」で受けたコードレビューについてまとめていきます。 「もりけん塾」では、先生がマークアップエンジニアからフロントエンドエンジニアになるための課題を作成してくださっており、塾生はその課題を通してJavaScriptの基礎を学んでいきます。 本当に1段1段階段を登っていくように作られており、課題を終える頃にはある程度自走しながらコードが書けるようになります。 私もこの課題に挑戦し、先日課題を終えることができました。だいたい2~3ヶ月くらいかかったと思います。JavaScriptが全然わからない状態から、ここまで書けるようになるとは思っていなかったです。 JS課題はこちら 成果物 ? README.mdに置いてます。 先を見越したコードを書く編 仕様が増えたときを想定する バリデーションを実装する課題でのレビューです。 名前のバリデーションでは、変数名を nameLength としており、 例えば名前の正規表現を実装するとなれば nameLength という変数名では追加したい仕様に沿った変数名ではないです。 また、メールアドレスのバリデーションでは正規表現を変数だけで持つようにしていたのですが、これも他の仕様が追加されることを想定して、オブジェクトで管理することを教えていただきました。 先を見据えて変数名やオブジェクトで管理するといった方法を学ぶことが出来た、とても勉強になった課題でした。 try-catch-finally try-catch-finallyを使ってローディングを実装しました。その際のコードの一部がこちらです。 注目していただきたいのはfinallyの記述です。 async function outputFn() { try { const result = await promiseObj(); createElements(result); }catch(e) { console.error(e); }finally { console.log('処理が終わりました。'); } }; outputFn(); ここでは、outputFn の処理が終わったということで、コンソールに「処理が終わりました。」と出力されるようにしていたのですが、先生からのレビューは 確かに。resolveされたときにはこれでも良いかもしれませんが、rejectされると処理はcatch節に移るので、その状態では「処理は終わっていない。または失敗している」ので、この書き方は間違えているということになります。 修正後はこちら。 async function outputFn() { try { const result = await promiseObj(); createElements(result); }catch(e) { console.error(e); }finally { loading.remove(); // この部分を修正 } }; outputFn(); 今回の課題の場合、処理が終わるまではローディング画像を出していたので、処理が終わったタイミングで loading.remove()とするように記述を変更しました。 どこでも使えるようなロジックは抽象的に書く これはユーザが持っているIDでソートをする関数です。ここで先生から。 この後の課題は年齢でもソートを行うといったものでした。もし上記のコードのまま年齢のソートをしようと思うと同じようなコードを書く必要がありました。 // idでソート function sortIdDesc(a, b) { if (a.id > b.id) return -1 if (a.id < b.id) return 1 return 0 } // ageでソート function sortIdDesc(a, b) { if (a.age > b.age) return -1 if (a.age < b.age) return 1 return 0 } 違うのは .id か .age のみです。これはスマートではありません。修正後はこちら。 // keyに渡す情報でソートする function sortDesc(key) { return (a, b) => { if (a[key] > b[key]) return -1 if (a[key] < b[key]) return 1 return 0 } } このように汎用性を持たせてコードを書くことで、余計なコードを書く必要がなくなります。 命名編 処理が分かる関数名をつける async function fetchData() { try { const response = await fetch('https://jsondata.okiba.me/v1/json/9omPz210202144336'); const json = await response.json(); loading.remove(); createElements(json.data); } catch(e) { console.error(e); } finally { console.log('outputFunc run'); } } setTimeout(outputFunc, 3000); 関数 fetchDataですが、関数名の通りに処理を考えると「fetchしてdataを返すだけの関数」になるはず。 しかし、この関数内では createElementsも実行しており、 fetchDataをしているだけの関数ではなくなってしまっています。この場合、関数名を変更するか、 createElementsは別の処理として切り出す必要がありました。 今回の場合は関数名を outputFuncに変更しましたが、これでは「outputって何処に?何を?ていうのが分からないのとFuncは隣のfunctionで自明なので全体的に抽象的すぎる気がしています。」と再度レビューをいただくことになりました。 今考えるとtry節で return json.dataとして createElementsは別の場所に切り出す方法が自然かなと思いました。 Boolean値を扱う時の関数名・変数名 JSONを作成し、最初に表示する画像を決める際に値に trueを持つプロパティを作成していました。 (文字列のtrueになっているのは後々修正しました。) このプロパティ名が firstViewとなっており、先生から とレビューを受けました。 今回のように trueを持つプロパティ名や、値があるかないかを判断する関数名には is**や has**を使うようにしています。 例えば以下のようなコード // is**の例 const content = document.getElementById('content') content.classList.add('active') const isActive = (target) => { return target.classList.contains('active') } if(isActive(content)) { console.log(`content is Active!!`) } else { console.log(`content is not Active...`) } // has**の例 const comments = ['美味しかった', '早かった', '安い'] const hasComment = (comments) => { return comments.length } if(hasComment(comments)) { console.log(`コメントあったで`) } else { console.log(`コメントなかったわ`) } みたいな感じで使っています。 イベントを付与する関数名の付け方 英語力の問題かもしれませんが、 僕はスライドショーの戻る・次へボタン(◀︎ ▶︎ みたいな矢印)に addEventListener('click')を付与したかったのですが、レビュー前は、clickArrowという関数名をつけていました。 先生からは とレビューをいただきました。 結局自分では答えを出せずにいたのですが、先生が答えを教えてくださいました。 少し関数名は長いですが、 attachClickEventForArrow 確かにこちらは関数名から何をしているのかが伝わる関数名になっていると思います。 リーダブルなコードを書くためには編 関数の肥大化を避ける 約3ヶ月前、恐ろしいくらい長いコードを書いていました。 これが1つの関数の処理だと思うと気がおかしくなりそうです。こんな冗長なコードをレビューしろと言われた先生の気持ちたるや...。 async function createElement () { const articleList = await fetchArticleData() const tab_list = new Set() for(const article of articleList) { // タブの生成 const tab = document.createElement('li') tab.textContent = article.category tab.classList.add('tab__item') tabs.appendChild(tab) tab_list.add(tab) // どのタブを初期表示にするか if(article.firstView === 'true') { tab.classList.add('active') } if(article.category === 'ニュース') { tab.dataset.id = 'js-news' } else if(article.category === '経済') { tab.dataset.id = 'js-economy' } else if(article.category === 'エンタメ') { tab.dataset.id = 'js-entertainment' } else if(article.category === 'スポーツ') { tab.dataset.id = 'js-sports' } else if(article.category === '国内') { tab.dataset.id = 'js-japan' } // firstViewがtrueのコンテンツを初期表示にする(ニュースカテゴリーがtrueを保持している) if(article.category === 'ニュース' && article.firstView === 'true') { newsContents.classList.add('active') } else if(article.category === '経済' && article.firstView === 'true') { economyContents.classList.add('active') } else if(article.category === 'エンタメ' && article.firstView === 'true') { entertainmentContents.classList.add('active') } else if(article.category === 'スポーツ' && article.firstView === 'true') { sportsContents.classList.add('active') } else if(article.category === '国内' && article.firstView === 'true') { japanContents.classList.add('active') } // タブの生成ここまで // タイトルの生成 for(const info of article.articleInfo) { const title = document.createElement('li') const title_link = document.createElement('a') const comment = document.createElement('span') const comment_icon = document.createElement('img') title.classList.add('title') comment.classList.add('comment') title_link.innerHTML = info.title comment_icon.src = 'img/comment.png' comment_icon.classList.add('comment_icon') title_link.appendChild(comment) title.appendChild(title_link) // 投稿日と今日との日差を取得 const postDay = new Date(info.postDay) const ms = today.getTime() - postDay.getTime() const days = Math.floor(ms / (1000*60*60*24)) // 14日以内の投稿であればnew_iconを追加する if(days <= 14) { const new_icon = document.createElement('img') new_icon.src = 'img/new_icon.png' new_icon.classList.add('new_icon') if(article.category === 'ニュース') { title.appendChild(new_icon) } else if(article.category === '経済') { title.appendChild(new_icon) } else if(article.category === 'エンタメ') { title.appendChild(new_icon) } else if(article.category === 'スポーツ') { title.appendChild(new_icon) } else if(article.category === '国内') { title.appendChild(new_icon) } } // コメントがあればアイコンを追加 if(info.comment > 0) { comment.textContent = info.comment comment.appendChild(comment_icon) } if(article.category === 'ニュース') { newsTitleWrap.appendChild(title) newsContentsInner.appendChild(newsTitleWrap) newsContents.appendChild(newsContentsInner) } else if(article.category === '経済') { economyTitleWrap.appendChild(title) economyContentsInner.appendChild(economyTitleWrap) economyContents.appendChild(economyContentsInner) } else if(article.category === 'エンタメ') { entertainmentTitleWrap.appendChild(title) entertainmentContentsInner.appendChild(entertainmentTitleWrap) entertainmentContents.appendChild(entertainmentContentsInner) } else if(article.category === 'スポーツ') { sportsTitleWrap.appendChild(title) sportsContentsInner.appendChild(sportsTitleWrap) sportsContents.appendChild(sportsContentsInner) } else if(article.category === '国内') { japanTitleWrap.appendChild(title) japanContentsInner.appendChild(japanTitleWrap) japanContents.appendChild(japanContentsInner) } } // タイトルの生成ここまで // 画像の生成 const img = document.createElement('img') img.src = article.img img.classList.add('img') if(article.category === 'ニュース') { newsContentsInner.appendChild(img) newsContents.appendChild(newsContentsInner) } else if(article.category === '経済') { economyContentsInner.appendChild(img) economyContents.appendChild(economyContentsInner) } else if(article.category === 'エンタメ') { entertainmentContentsInner.appendChild(img) entertainmentContents.appendChild(entertainmentContentsInner) } else if(article.category === 'スポーツ') { sportsContentsInner.appendChild(img) sportsContents.appendChild(sportsContentsInner) } else if(article.category === '国内') { japanContentsInner.appendChild(img) japanContents.appendChild(japanContentsInner) } // 画像の生成ここまで } tabClickAction(tab_list) } だいたい150行くらいありました。クソくらえですね。 とレビューをいただきました。 というわけで関数内部で行なっていた処理を関数化し切り出したコードがこちらです。 async function createElements () { let articles try { articles = await fetchArticle() } catch(e) { console.error(e); } finally { loading.remove(); } createTabs(articles) createTitles(articles) createImages(articles) checkContentsIsInit(articles) } なんと14行になりました。同じ関数とは思えないですね。 ここで関数の切り出しをめちゃくちゃ練習したおかげで、引数の扱い方が少しわかった気がします。 切り分けたことで関数が肥大化せず、引数に何を渡しているのか、関数名からその関数が何をしている関数なのかということも分かるようになりました。 また、テスト?(まだやったことないのでわからない)をする際にも処理を切り出すことは大切なんだとか。 むやみに関数化しない 処理を切り分けることや、関数化することに楽しさを覚えてしまったころ、先生からレビューが。 tokenがlocalStorageに存在するか を確認する処理を関数化して切り分けたのですが、見事に修正されました。 理由として - 繰り返し使うものではないと判断した - localStorage.getItem('token')で処理を説明できている - 切り分けると冗長 と解説していただきました。 なんでも関数化したり切り分けたりすると、それはそれでリーダブルでなくなってしまうという。 マジックナンバーに気を付けろ こちらは同じ塾生のもなかさんにレビューしていただきました。もりけん塾では塾生も積極的にコードレビューをすることが特徴です。 コード書いているときには気にならなかったのですが、確かに後で見返すとなんの1かわかりません。 const nextNum = 1 changeImage(nextNum) changePaginationIncrement(nextNum) とすることで、1に意味を持たせることができました。 関連する値をオブジェクトとしてまとめる ...この currentNumは一体どこで、どのように使われるのでしょうか。 答えはスライドが今何番目が表示されているかを管理するための値でした。これだと何のために存在するのかをコードを見ただけでは予測できません。 そのため、関連する値はまとめてオブジェクトにして管理してしまうという手があります。 修正後はこちら const slideState = { currentNum: 0, images: [] } これでスライドに関する値であることが伝わるコードになったかと思います。 さらに僕はここで「分割代入」を知っていたので、使ってやろうと意気込んで使って見ました。 const slideState = { currentNum: 0, images: [] } let { currentNum } = slideState const { images } = slideState すると先生から 僕的には何度も記述する値だったので、毎回 slideState.currentNumと記述するのが億劫になっていたので分割代入を使っていたのですが、ここで分割代入をしてしまうと結局 currentNumはどこの・何の currentNumなのかがわからなくなってしまいますね。 if-elseをswitchにしてリータブルに。そして保守性も大切に。 こちらは、コメントに書いてある通り「ユーザが持っているkey情報を元に、表示したいカラム名に変換して返却する」関数です。 if-elseを使うと、「なんの条件の時にどういった処理が行われるか」「あ、いろんな条件でreturnする値を変えているな」というのがわかりやすくなるのがメリットかなと思っています。 このコードに対して先生から というコメントとともに function changeColumnName(column) { switch (column) { case "id": return "ID"; case "name": return "名前"; case "age": return "年齢"; case "sex": return "性別"; default: console.error(`not provided ${column}`); } } このコードが返ってきました。switch文、初めて使うところを見ました。今までは if-elseがあったので、別に覚えなくてもいいわ...くらいに思っていたのですが、 「読みやすさ」が全然違う。邪魔な記述がないコードを見ると感動すら覚えます。 そして、先生からのレビューにあった「どこにも当てはまらない時の処理」が保守性に関わってくる部分です。(恐らく。) 今は自分が作成したJSONを使ってフロントを作っているので意図しない値が渡ってくることはありませんが、先生は「ここで要素が増えたら」「もしこんな値が渡ってきたら」など、今後のことも考えながらレビューをしてくれます。 こんなツイートもされてました。 [HTML tips] contenteditable属性をtrueにするとその要素は編集可能になる- 文字の増減による表示崩れ確認に便利- 非推奨動画は開発ツールでbodyにcontenteditable="true"を与え色々いじっている様子 pic.twitter.com/AtUR8UgHSe— フロントエンドエンジニア (@terrace_tech) May 3, 2021 switch文をオブジェクトを使ってさらにリーダブルに function changeColumnName(column) { switch (column) { case "id": return "ID"; case "name": return "名前"; case "age": return "年齢"; case "sex": return "性別"; default: console.error(`not provided ${column}`); } } function changeColumnName(key) { const mapColumns = { "id": "ID", "name": "名前", "age": "年齢", "sex": "性別", } return mapColumns[key] ?? console.error(`not provided ${key}`); } なんともいえないすっきり感。 [JavaScript]if elseで一つ一つ評価していく方法もありますが条件が多い場合見栄えがよくないですこれを...switch文やオブジェクトを使うと少し見やすくなります pic.twitter.com/iTAO3MutwM— フロントエンドエンジニア (@terrace_tech) May 3, 2021 オプショナルチェイニング(optional chaining)演算子ですっきりと書く オプショナルチェイニング:https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Optional_chaining if(result) { passwordErrorMessage && passwordErrorMessage.remove() } passwordErrorMessageが trueであれば passwordErrorMessage.remove()を実行する処理です。 これを if(result) { passwordErrorMessage?.remove?.() } と記述することができます。すっきり。 あらかじめboolean以外のfalsyな値がわかっている時はその値と比較する const argErrorMessage = document.getElementById('errorMessage') if(argErrorMessage) { ... } これを const argErrorMessage = document.getElementById('errorMessage') if(argErrorMessage !== null) { // この部分 ... } このようにすることで null以外の falsyな値が渡ってこないのが明確になり、リーダブルになるとのことでした。 falsyな値として false, null, undefined, 0, "" (空文字), NaNなどがありますが、今回の場合には document.getElementByIdを使っているので、返却される値としては 指定された ID に一致する DOM 要素オブジェクトを記述した Element オブジェクト、または文書内に一致する要素がなければ null が返ってきます。 そのため、あえて他のfalsyな値が存在するということを示さないために、 if(argErrorMessage !== null) としています。 [JavaScript] あらかじめboolean以外のfalsyな値がわかっている時はその値と比較する※あくまでIMO pic.twitter.com/WAz5BNwcpI— フロントエンドエンジニア (@terrace_tech) May 14, 2021 パフォーマンス編 document.createDocumentFragmentを使う 初めて聞いたときには、なんですかそれは?状態でした。 例えばこんな記述をしていたとします。 const body = document.querySelector('body') const ul = document.createElement('ul') const books = ['webを支える技術', 'Web技術の基本', 'JavaScript本格入門'] books.forEach(book => { const li = document.createElement('li') li.textContent = book ul.appendChild(li) // ここで毎ループごとにulへliを追加している }) body.appendChild(ul) 注目していただきたいのはforEachの中でulに対してliを追加している部分です。 これはパフォーマンスの観点から望ましいコードとはいえません。これはulにliを追加した時点で一度コンテンツが再描画されるからです。この程度であれば問題ありませんが、もっと大量のデータを扱うことになると大変なことになります。 要するに、なかなか負荷が高いことをプログラムにさせているということになります。 そこで document.createDocumentFragmentの出番です。 DocumentFragmentオブジェクトは「組み立てたノードを一時的にストックするための容器」です。実際にコードを見ていきます。 const body = document.querySelector('body') const ul = document.createElement('ul') const books = ['webを支える技術', 'Web技術の基本', 'JavaScript本格入門'] let listFragment = document.createDocumentFragment() books.forEach(book => { const li = document.createElement('li') li.textContent = book listFragment.appendChild(li) // ここでDocumentFragmentオブジェクトに追加している }) ul.appendChild(listFragment) // ulに対してDocumentFragmentオブジェクトを追加 body.appendChild(ul) このように document.createDocumentFragmentを使うことでパフォーマンスを最適化できます。 MDN : https://developer.mozilla.org/ja/docs/Web/API/Document/createDocumentFragment 配列操作・メソッド編 .map .forEach などの配列操作をするメソッドを使い分ける slideImages.images.map((image, index) => { const li = document.createElement('li') imageListFragment.appendChild(li) slideState.images.push(img) }) このように .mapを使って配列操作を行なっていたのですが、本来 .mapは与えられた関数の処理をすべての要素に対して呼び出して、 新しい配列を生成するためのメソッドです。 そのため、 .mapを知っている人からすると「新しい配列は?」となってしまい、リーダブルではありませんし、使い方を間違っています。 このコードの場合、特に新しい配列を生成する必要はないので、 .forEachが適切でした。 目的にあったメソッドを使う const users = [ { name: 'aaaa', password: 'Yamamoto0000' }, { name: 'bbbb', password: 'Yamamoto1111' } ] function checkSubmitData(inputUserData) { const result = users.some(user => { user.name === inputUserData.name && user.password === inputUserData.password }) return result } フォームに入力された name と passwordが存在するかを見つける処理です。 このとき、 .someを使って実装していました。 とレビューをいただき、ハッとしました。 ユーザが存在するかどうかを確認したかった。のであれば .findのほうが適切ですよね。 .someは true を返す要素が見つかるとtrue を返すのに対し、 .findは true を返した要素の値を返す。という違いがあります。 some : https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/some find : https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/find その他 引数の順番や数を意識する これはIMOだったのですが、自分が気になったので質問してみたものです。 先生の場合は 、 必須な値・必須な関数・オプション の順で引数を渡しているそうです。 ちなみに僕の場合は、処理内で出てくる順に引数に渡すようにしていました。 また、引数の数は3つまでが望ましく、それ以上引数がある場合には ・関数内での処理が多いので、処理を切り出すか別の方法で書けないか考える ・引数をオブジェクトにしてまとめる が必要だと教えていただきました。 まとめ ここに書いたこと以外にももっと細かいレビューも頂いていますが、書ききれないのでよく見るレビューをまとめてみました。 ただ記事をみて学ぶのと、自分で書いて、レビューしてもらって、修正して、またレビューもらって...を繰り返すのとでは、定着率が全然違うなということがよくわかりました。やっぱり手を動かしたほうが勉強になりますね。 紹介 私が所属している「もりけん塾」は、もりけん先生が運営するJavaScriptに特化したコミュニティーです。(とは言ってもいろんな方がいらっしゃいます。) フロントエンドエンジニアになりたい方にむけて、先生が道標となって初学者が迷わないように導いてくれます。コードの書き方、自走力を身に着けるにはとても良い環境です。 毎月1日に募集をかけていたのですが、ここ最近は募集かけなくても入塾したいとDMがくるそうです...!!(大人気) 先生のブログ 先生のTwitter
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

お気に入り登録を実装(非同期通信)

現在RubyとJavaScriptを用いて記事投稿サイトを作成しています。 今回は非同期通信のお気に入り登録を実装します。 お気に入り記事一覧ページの実装までを書いていきたいと思います。 ■仕様等 ・お気に入りアイコンをクリックで登録/解除 ・jQueryを使用しない ■完成後の動作 ■お気に入り機能 ①Userモデルにお気に入り操作に関するメソッドを記述 bookmarkモデル、コントローラーを作成した後、 お気に入り操作に関するメソッドをuserモデルに追記していきます。 ※bookmarksテーブルのカラムはuser_idとarticle_idです。 user.rb has_many :bookmarks_articles, through: :bookmarks, source: :article def bookmark(article) bookmarks_articles << article end def unbookmark(article) bookmarks_articles.delete(article) end def bookmark?(article) bookmarks_articles.include?(article) end 1行目 has_many :bookmarks_articles, through: :bookmarks, source: :article ユーザーがお気に入り登録した記事を全て取得します。 2行目〜4行目 bookmark(article) 引数に渡された記事情報を1行目のbookmarks_articlesに追加します。 6行目〜8行目 unbookmark(article) 引数に渡された記事情報を1行目のbookmarks_articlesから削除します。 10行目〜12行目 bookmark?(article) 引数に渡された記事情報が1行目のbookmarks_articlesに存在するかを判定します。 ②ルーティングの設定 routes.rb Rails.application.routes.draw do devise_for :users root to: "articles#index" resources :bookmarks, only: [:create, :destroy] end 3行目 resources :bookmarks, only: [:create, :destroy] お気に入りの登録と解除を実装するので、createとdestroyを指定します。 ③コントローラーの設定 bookmarks_controller.rb class BookmarksController < ApplicationController def create @article = Article.find(params[:article_id]) current_user.bookmark(@article) end def destroy @article = current_user.bookmarks.find_by(id: params[:id]).article current_user.unbookmark(@article) end end 4行目/9行目 current_user.bookmark(@article) current_user.unbookmark(@article) userモデルで設定した各メソッドの引数に、対象の記事情報を渡しています。 ④ビューの記述 index.html <% @articles.each do |article| %> <div class="article-content" id="article_<%= article.id %>"> <%= render 'shared/articles', article: article %> </div> <% end %> _articles.html <% if user_signed_in? %> <% if current_user.bookmark?(article) %> <%= render 'bookmarks/unbookmark', article: article %> <% else %> <%= render 'bookmarks/bookmark', article: article %> <% end %> <% end %> ■非同期処理の設定 _bookmark.html <%= link_to bookmarks_path(article_id: article.id), class: "bookmark", id: "js-bookmark-button-for-article-#{article.id}", method: :post, remote: true do %> <% end %> _unbookmark.html <%= link_to bookmark_path(current_user.bookmarks.find_by(article_id: article.id)), class: "bookmark", id: "js-bookmark-button-for-article-#{article.id}", method: :delete, remote: true do %> <% end %> 各1行目 <%= link_to bookmarks_path(article_id: article.id), class: "bookmark", id: "js-bookmark-button-for-article-#{article.id}", method: :post, remote: true do %> <%= link_to bookmark_path(current_user.bookmarks.find_by(article_id: article.id)), class: "bookmark", id: "js-bookmark-button-for-article-#{article.id}", method: :delete, remote: true do %> 各末尾のオプションに注目してください。 remote:trueを設定することでJavaScript形式のリクエストが送信されます。 送信後、コントローラーのcreate/destroyアクションが実行されると、 create.js.erb/destroy.js.erbに遷移します。 create.js.erb document.getElementById('article_<%= @article.id %>').innerHTML = '<%= escape_javascript( render 'shared/articles', article: @article ) %>' destroy.js.erb document.getElementById('article_<%= @article.id %>').innerHTML = '<%= escape_javascript( render 'shared/articles', article: @article ) %>' 対象の記事を取得して、index.htmlにもあるrenderの部分を挿入しています。 escape_javascriptは改行のエスケープ処理を行います。 以上でお気に入り登録を実装することができました。 ■お気に入り一覧表示ページの実装 ①ルーティングの設定 routes.rb Rails.application.routes.draw do devise_for :users root to: "articles#index" resources :articles do collection do get :bookmarks end end resources :bookmarks, only: [:create, :destroy] end 4行目〜8行目 :articlesにネストする形でcollection do :bookmarks endを記述します。 ②コントローラーの設定 articles_controller.rb def bookmarks @bookmarks_articles = current_user.bookmarks_articles.includes(:user).order('created_at DESC') end current_user.bookmarks_articles 現在のユーザーの全てのお気に入り記事を取得しています。(冒頭のUserモデルにて設定) ③ビューを記述 bookmark.html.erb <% @bookmarks_articles.each do |article| %> <div class="article-content" id="article_<%= article.id %>"> <%= render 'shared/articles', article: article %> </div> <% end %> インスタンス変数を受け取って一覧を表示しています。 以上でお気に入り記事一覧表示ページの完成です。 参考記事 https://miiina01220.hatenablog.com/entry/2020/11/20/172739 https://techtechmedia.com/favorite-function-rails/ https://qiita.com/naberina/items/c6b5c8d7756cb882fb20
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaSctipt,TypeScript】論理演算子の基本と応用

基本 どちらの演算子も両方がTrueだったらTrue、FalseだったらFalseになる JavaScriptの仕様上判定は左から右へ行われる。 ||演算子 main.js console.log( true || true ) //true console.log( true || false ) //true console.log( false || true ) //true console.log( false || false ) //false 数学的な意味では「または」 記号であらわすと∪ a || b であればaまたはbのどちらかを満たす範囲 プログラミングであればどちらかがTrueであればTrueになる &&演算子 main.js console.log( true && true ) //true console.log( true && false ) //false console.log( false && true ) //false console.log( true && false ) //false 数学的な意味では「かつ」 記号であらわすと∩ a && b であればaまたはbの両方を満たす範囲 プログラミングであれば両方TrueでないとTrueにならない。 JavaScriptでの演算子の法則 ||演算子 ||の場合、左式がfalseもしくはfalseとみなすことができる(falsy)値の場合右式を返す これら全て出力は "右式を返すよ" になります。 main.js console.log( false || "右式を返すよ") console.log( 0 || "右式を返すよ") console.log( null || "右式を返すよ") console.log( undefined || "右式を返すよ") console.log( NaN || "右式を返すよ") console.log( "" || "右式を返すよ") &&演算子 &&の場合、左式がtrueもしくはtrueとみなすことができる(truthy)値の場合右式を返す これら全て出力は "右式を返すよ" になります。 main.js console.log( true && "右式を返すよ") console.log( 1 && "右式を返すよ") console.log( "左式だよ" && "右式を返すよ") これらの法則はほかのプログラミング言語でも使用できたりもします。 JavaScriptでの応用 Reactでの応用 ||の左式がtrueの場合右式を返却する仕組みを利用してこのような書き方が出来ます。 App.jsx import { useState, useEffect } from 'react'; export const App = () => { const [admin , setAdmin] = useState('false') return( <div> {admin && <AdminPage />} <div> ) } ①Admin(管理者)かどうか判定するStateを作成 ②管理者であればAdminPage(管理者画面)を出力する。 ※本来であれば管理者かどうかとかはhook化して汎用的にするのがベスト まとめ ||演算子 左式がfalseとみなすことができる値の場合、右式に設定されている値を返却 左式がtrueとみなせる場合はそのまま左式を返却 &&演算子 左式がtrueとみなすことができる値の場合、右式に設定されている値を返却 左式がfalseとみなせる場合はそのまま左式を返却 特にフロントエンド開発で上手いこと使いこなせればコードの質が上がりますが、 使いすぎて可読性を落とさないように注意しましょう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む