- 投稿日:2020-08-11T23:27:10+09:00
javascript 3分カウントダウンタイマー
要件は
1.カウントは03:00からスタートする
2.スタートボタンを押すと1秒ずつカウントが進む
3.カウントが00:00になったら「Time UP!」と表示する
4.ストップボタンを押すとカウントが止まる
5.リセットボタンを押すとカウントが03:00に戻り、カウントが止まる<!doctype html> <html lang="ja"> <head> <meta charset="utf-8"> <title>タイマー練習</title> <script> var to_timeup = 180; var max = 180; var intervalid; var start_flag = false; // var timer=document.getElementById("timer"); function count_start(){ console.log("count_start"); if(start_flag===false){ intervalid = setInterval(count_down,1000); start_flag = true; } } function count_down(){ console.log("count_down"); var timer = document.getElementById("timer"); if(to_timeup===0){ timer.innerHTML = 'Time up!' timer.style.color="red"; // clearInterval(intervalid); // to_timeup=10; count_stop(); } else{ to_timeup--; padding(); } } function padding(){ var timer=document.getElementById("timer"); var min = 0; var sec = 0; min = Math.floor(to_timeup/60); sec = (to_timeup%60); min = ("0"+min).slice(-2); sec = ("0"+sec).slice(-2); timer.innerHTML = min +":"+ sec; } function count_stop(){ console.log(count_stop); clearInterval(intervalid); start_flag = false; } function count_reset(){ console.log(count_reset); var timer = document.getElementById("timer"); clearInterval(intervalid); start_flag = false; to_timeup = max; padding(); timer.style.color = "black"; } window.onload = function(){ console.log("mumei"); padding(); var startbutton=document.getElementById("startbutton"); startbutton.addEventListener("click",count_start,false); var stopbutton=document.getElementById("stopbutton"); stopbutton.addEventListener("click",count_stop,false); var resetbutton=document.getElementById("resetbutton"); resetbutton.addEventListener("click",count_reset,false); } </script> </head> <body> <h1 id="timer"></h1> <button id="startbutton">start</button> <button id="stopbutton">stop</button> <button id="resetbutton">reset</button> </body> </html>最初に異なる変数に同じ値を入れるのはcount resetのところで180という数字を直接書くよりも、後でカウントが5分、10分に変更になったときに最初の変数の値を変更するだけで済ませるため。
- 投稿日:2020-08-11T22:15:14+09:00
【Rails】いいね機能を非同期実装
1. はじめに
以下のデモ動画の様に、ユーザーが投稿した内容に対して"いいね"が出来る機能を実装していきます。
2. 前提条件
既にユーザー登録機能と投稿機能は実装されている前提で、そこに"いいね機能"を追加実装する。という流れで進めていきます。
下記の様なデータベース構造をイメージしてもらえたら分かりやすいと思います。
3. いいね機能の実装
■実装するまでの流れ
ざっくり説明すると、以下の流れで実装していきます。
・モデルの作成
↓
・ルーティングの追加
↓
・コントローラーの作成
↓
・ビューの作成それでは、早速いってみましょー。
3-1. Likeモデルの作成
まずはLikeモデルを作成します。
ターミナルで以下のコマンドを実行してください。ターミナル$ rails g model Like新しくマイグレーションファイルが作成されるので、以下の通りに編集してください。
db>migrate>xxxxxx_create_likes.rbclass CreateLikes < ActiveRecord::Migration[6.0] def change create_table :likes do |t| # ===追記部分=== t.references :tweet, foreign_key: true, null: false t.references :user, foreign_key: true, null: false # ===追記部分=== t.timestamps end end end上記の様にreferences型で保存すると、
tweet_id
とuser_id
を外部キーとして指定することが出来ます。
それではマイグレーションファイルを実行して、likesテーブルを作成しましょう。ターミナル$ rails db:migrate上記のコマンドを実行した後、likesテーブルが作成されたかどうか確認して下さい。
無事作成されている事が確認出来たら、次はアソシエーションの設定です。3-2. アソシエーションの設定
アソシエーションとは、2つのモデル同士の関連付けのことを指します。
UserモデルとLikeモデル、TweetモデルとLikeモデル、それぞれのアソシエーションを設定していきます。UserモデルとLikeモデルのアソシエーションの設定
まずはUserモデルとLikeモデルのアソシエーションを設定していきます。
2つのモデルの関係性は以下の通りです。
・ ユーザーは複数のいいねが可能
・ いいねAをしたユーザーは1人しかいないつまり、UserモデルとLikeモデルは「1対多」の関係になります。
それでは、実際にコードを書いていきましょう。Userモデルに以下の通りコードを追記して下さい。
app>models>user.rbclass User < ApplicationRecord has_many :tweets, dependent: :destroy # この行を追加 has_many :likes end
has_many
は、他のモデルとの間に「1対多」の関係があることを示します。次はLikeモデルに以下の通りコードを追記して下さい。
app>models>like.rbclass Like < ApplicationRecord # この行を追加 belongs_to :user end
belongs_to
はhas_many
の逆で、他のモデルとの間に「多対1」の関係があることを示しています。これで、UserモデルとLikeモデルのアソシエーション設定が出来ました。
TweetモデルとLikeモデルのアソシエーションの設定
同じ要領でTweetモデルとLikeモデルのアソシエーションも設定していきます。
2つのモデルの関係性は以下の通りです。
・ 1つの投稿に対して、複数のいいねがつく
・ いいねAに紐づく投稿は1つしかないつまり、TweetモデルとLikeモデルも「1対多」の関係になります。
それでは、実際にコードを書いていきましょう。Tweetモデルに以下の通りコードを追記して下さい。
app>models>tweet.rbclass Tweet < ApplicationRecord belongs_to :user # この行を追加 has_many :likes, dependent: :destroy end
dependent: :destroy
をつける事で、投稿が削除された時に、その投稿に紐づくいいねも削除されます。次はLikeモデルです。
app>models>like.rbclass Like < ApplicationRecord belongs_to :user # この行を追加 belongs_to :tweet end以上で、全てのモデルのアソシエーションの設定が完了しました。
3-3. バリデーションの設定
投稿Aに対して1人のユーザーがいいねを押せる回数は1回にしたいので、1回以上は押せない様にバリデーションを設定します。
Likeモデルに以下の通り追記して下さい。
app>models>like.rbclass Like < ApplicationRecord belongs_to :user belongs_to :tweet # この行を追加 validates :user_id, uniqueness: { scope: :tweet_id } end上記の様に書く事で、
user_id
とtweet_id
が重複しない様にする事が出来ます。
以上で、バリデーションの設定は完了です。3-4. ルーティングの追加
いよいよ本格的にいいね機能を実装していきます。
まずは、いいね機能で使うルーティングを追加しましょう。
以下の通りコードを追記して下さい。config>routes.rbRails.application.routes.draw do devise_for :users, controllers: { registrations: 'registrations' } resources :tweets, only: [:index, :new, :create, :show, :destroy] do # この行を追加 resources :likes, only: [:create, :destroy] end endいいね情報の保存と削除のルーティングを追加する必要があるので、likesコントローラーの
create
アクションとdestroy
アクションを定義しています。ルーティングをネストにする事で、いいねがどの投稿に紐づくかを明示できます。
コードを追加したら
rails routes
コマンドで、ルーティングの設定が問題ないか忘れずに確認しておきましょう。3-5. likesコントローラーの作成
次にlikesコントローラーを作成していきます。
ターミナルで以下のコマンドを実行してください。ターミナル$ rails g controller likes上記のコマンドを実行すると、likesコントローラーが作成できます。
それでは作成したlikesコントローラーに
create
アクションとdestroy
アクションを作成していきます。
以下の通りコードを追記して下さい。app>controllers>likes_controller.rbclass LikesController < ApplicationController # ===追記部分=== def create @like = current_user.likes.build(like_params) @tweet = @like.tweet if @like.save respond_to :js end end def destroy @like = Like.find_by(id: params[:id]) @tweet = @like.tweet if @like.destroy respond_to :js end end private def like_params params.permit(:tweet_id) end # ===追記部分=== endprivateメソッドやparamsは理解できているものとして、追加したコードについて簡単に説明していきます。
createアクション
まず
@like
には投稿に"いいね"をしたユーザーのuser_id
と、"いいね"された投稿のtweet_id
の情報が入っています。
このコードはbuildメソッドを使って、インスタンスを作成しています。次に
@tweet
には@like
に紐づく投稿の情報、つまり"いいね"された投稿の情報が入ります。
@tweet
はどの投稿に"いいね"を押したのかを判断するために、ビューを作成するところで使います。最後の
if @like.save
の部分は、"いいね"が押された時に返すレスポンスのフォーマットをrespond_to
メソッドで切り替えています。
"いいね"が押されたらリアルタイムでビューを反映させるために、JS形式のフォーマットでレスポンスを返すようにしています。destroyアクション
createアクションのところで説明した内容と重複している部分が多いので簡単に説明すると、受け取ったHTTPリクエストから
id
を判別し、@like
に指定のレコードの情報を入れています。こちらもリアルタイムでビューを反映させるために、JS形式のフォーマットでレスポンスを返すようにしています。
3-5. ビューの作成
いよいよビューの作成です。
まずは投稿一覧のビュー画面を編集していきましょう・・・と言いたいところですが、ビューで使うためのメソッドを先に定義しておきます。
Tweetモデルに以下の通りコードを追記して下さい。
app>models>tweet.rbclass Tweet < ApplicationRecord belongs_to :user has_many :likes, dependent: :destroy # 追加部分(liked_byメソッド) def liked_by(user) Like.find_by(user_id: user.id, tweet_id: id) end # 追加部分 end上記で追加した
liked_by
メソッドは、user_id
とtweet_id
が一致するlikeを探して、無ければnillを返します。それでは、
app/views/tweets/index.html.erb
に以下のコードを追記して下さい。app>views>tweets>index.html.erb<% @tweets.each do |tweet| %> # いいねボタンを表示したい部分に追加 <div class="content-like"> <ul class="content-like__icons"> <li id="<%= tweet.id.to_s %>"> <% if tweet.liked_by(current_user).present? %> <%= link_to (tweet_like_path(tweet.id, tweet.liked_by(current_user)), method: :DELETE, remote: true, class: "liked") do %> <i class="far fa-thumbs-up"></i> <% end %> <% else %> <%= link_to (tweet_likes_path(tweet), method: :POST, remote: true, class: "like") do %> <i class="far fa-thumbs-up"></i> <% end %> <% end %> </li> </ul> </div> # 追加部分はここまで <% end %>
liked_by
に引数としてcurrent_user
を渡すことで、現在ログインしているユーザーが投稿に"いいね"をしているかどうか判断しています。これでユーザーが"いいね"をしていない時に"いいねボタン"をクリックすると、先ほど作成した
create
アクションを実行、ユーザーが"いいね"をしている時はdestroy
アクションを実行と、条件分岐させる事ができました。リンクが押された時に
.js.erb
ファイルを呼び出す必要があるので、link_to
にremote: true
オプションを追加することを忘れないでください。なお"いいねボタン"のアイコンについては、Font Awesome を利用しています。
導入方法については、以下のqiita記事などが参考になるかと思います。
rails font-awesome-sass導入方法次は、createアクションが実行された時に出力するファイルを作成します。
app/views
フォルダ直下にlikes
フォルダを作成し、その中にcreate.js.erb
ファイルを作成してください。ファイルの作成ができたら、以下の通りコードを追記してください。
app>views>likes>create.js.erb$('#<%= @tweet.id.to_s %>'). html('<%= j render "tweets/liked", { tweet: @tweet } %>');上記のコードで、createアクションが実行されたら
tweets
フォルダ内の_liked.html.erb
ファイルを呼び出しています。
tweets
フォルダの中に_liked.html.erb
ファイルを作成し、以下のコードを追加してください。app>views>tweets>_liked.html.erb<%= link_to (tweet_like_path(tweet.id, tweet.liked_by(current_user)), method: :DELETE, remote: true, class: "liked") do %> <i class="far fa-thumbs-up"></i> <% end %>上記のコードで、"いいねボタン"を押したら"いいね"を取り消すHTMLを表示するようにしています。
同じ流れで、destroyアクションが実行された時に呼び出されるファイルも作っていきましょう。
app/views>likes
フォルダの中にdestroy.js.erb
ファイルを作成してください。ファイルの作成ができたら、以下の通りコードを追記してください。
app>views>likes>destroy.js.erb$('#<%= @tweet.id.to_s %>'). html('<%= j render "tweets/like", { tweet: @tweet } %>');
tweets
フォルダの中に_like.html.erb
ファイルを作成し、以下のコードを追加してください。app>views>tweets>_like.html.erb<%= link_to (tweet_likes_path(tweet), method: :POST, remote: true, class: "like") do %> <i class="far fa-thumbs-up"></i> <% end %>以上で非同期でいいね機能を実装する事ができました。
あとは見た目ですが、クラス名を、いいねされている時は
liked
されていない時はlike
としていますので、CSSで自分好みにカスタマイズしてみてください。私の場合は以下の様にして、いいねがされた時はレッド、されていない時はグレーにアイコンの色を変えています。
app>assets>stylesheets>tweets>_tweet.css.like { color: gray; } .liked { color: red; }4. さいごに
今回がはじめての投稿になりますが、記事を書くのって想像していたよりも大変ですね。
もし分かりにくい部分や、間違っている部分がある場合はご指摘いただけると嬉しいです。最後まで読んでいただき、ありがとうございました☺️
- 投稿日:2020-08-11T21:45:39+09:00
【javascript】新しい配列メソッド
こちらの記事は以下の書籍を参考にアウトプットとして執筆しました。
Array.fromで配列を作成する
argumentsオブジェクトは配列ではない
arugumentsは配列ではなく、配列風のオブジェクトである。
配列風とは、配列のようにlengthプロパティを持っていて、展示アクセスができ、forループによる処理もできるオブジェクトのこと
以下のコードはargumentsが配列でないためにエラーになる
function avg(){ const sum=arguments.reduce(function(a,b){ return a+b; }) return sum/arguments.length; }reduce
構文arr.reduce(callback( accumulator, currentValue[, index[, array]] )[, initialValue])
callback引数 説明 accumulator callback値を蓄積する currentValue 現在処理されている配列の要素 index 現在処理中の配列要素のインデックス。initialValueが指定されている場合は0から。そうじゃないなら1から array reduce()が呼び出された配列
引数 説明 initialValue callbackの最初の呼び出しの最初の引数として使用する値 配列風を配列に変換する
Array.fromの目的は配列風を配列に変換することで、配列風とはlengthプロパティを持つオブジェクトのこと
0からlengthまで適切なインデックス位置におかれ、文字列ではlengthプロパティに加えて、各文字のインデックスを示す数値のプロパティがある。このため文字列でArray.fromを呼び出すと文字の配列が返される。
これを使ってargumentsも配列にする。NodeListを配列にする
document.querySelectorAllなどのDOMノードリストを返すようなオブジェクトでも配列に変換できる。
というかlengthプロパティを持つオブジェクトであれば何でもうまくいく参考
- 投稿日:2020-08-11T21:41:19+09:00
自分用メモ javascript 演算子
- 投稿日:2020-08-11T21:19:09+09:00
[react]nextjs関連のメモ
nextjs関連で学んだことをメモ
現在nextjsの勉強をしているので覚えておいた方が良さそうなところや、
仕組みが不明なとこなどをメモがわりに残しておく。ページのルーティング
nextjsをなんとなく使っていたが、どういうルールでルーティングができてるのかわからなかった。
nextjsのディレクトリはpages/ - index.js - sample/ - index.js - show.jsこのような構成になっているが
pages/index.jsはlocalhost:3000/
,
pages/sample/index.jsはlocalhost:3000/sample/
,
pages/sample/show.jsはlocalhost:3000/sample/show/
という風にpagesディレクトリに存在するファイルからエクスポートされたコンポーネントがルーティングになるらしい。sample/index.js
```
export default function Sample() {
returnsample
}localhost:3000/sample/にアクセスすると確認できた。
```プリレンダリングとは
あらかじめhtmlを生成しておき、後からjavascriptコードが読み込まれる。
(*詳しい方いたら補足して頂けると助かります。)プリレンダリングの2つの形式
静的生成(Static Generation)
本番にビルドしたタイミングでhtmlを生成しておき、ユーザーのリクエストに応じて生成されたhtmlを再利用して表示する。
サーバサイドレンダリング(SSR)
ユーザーからのリクエスト毎にhtmlが生成される。
- 投稿日:2020-08-11T20:11:42+09:00
JavaScript / 直積を生成する
目的
- 総当たりは直積 (デカルト積) で表現できるらしい
- 入れ子の反復処理を無くす方法で検索していたら出てきた
- 標準ライブラリに直積の生成は用意されていないので書いてみる
直積
これだと
[0, 1, 2] [3, 4, 5]こういうこと
[0, 3] [0, 4] [0, 5] [1, 3] [1, 4] [1, 5] [2, 3] [2, 4] [2, 5]コード
制御文
for...of 文の例
const array_a = [0, 1, 2] const array_b = [3, 4, 5] for (const x of array_a) { for (const y of array_b) { console.log([x,y]) } }関数化
高階関数だと短く書ける
const product = (...args) => args.reduce((acc, cur) => acc.flatMap(x => cur.map(y => x.concat([y]))), [[]])使い方
const array_a = [0, 1, 2] const array_b = [3, 4, 5] for (const xy of product(array_a, array_b)) { console.log(xy) }何をしているのかは次のコードが分かりやすいかもしれない
高階関数は遅い、for 文は速いというイメージがあったので試しに書いたものconst product = (...args) => { const args_size = args.length let result = [[]] for (let a = 0; a < args_size; a++) { const arg = args[a] const arg_size = arg.length const result_size = result.length const halfway = [] for (let b = 0; b < result_size; b++) { const x = result[b] for (let c = 0; c < arg_size; c++) { halfway.push(x.concat([arg[c]])) } } result = halfway } return result }最後に
入れ子の反復処理を無くす方法というよりは関数化して隠す方法ですね
- 投稿日:2020-08-11T19:00:29+09:00
【33大特典付】JavaScript関数ドリル一般公開しました!
概要
詳細は以下の記事で解説しております^^
↓
https://bit.ly/3abWMmX対象
- JavaScript初学者
- 関数の実装に苦手意識を持っている方
- 関数の演習問題をたくさん解きたい方
- プログラミング学習に挫折しそうな方
- 効率の良い学習方法を知りたい方
- Webエンジニアを目指すための学習ロードマップの作り方を知りたい方
- フロントエンドエンジニアを目指す人向けの学習ロードマップを観たい方
- 無料・有料のメンターを見つける方法を知りたい方
- 投稿日:2020-08-11T18:04:04+09:00
初めてアドオン(WebExtensions)を作ってみたのでハマった点をまとめてみる
アドオン(WebExtensions)を初めて作ってみました。その過程で、いろいろハマったので、まとめたいと思います。
作ったもの
Qiine というアドオンを作りました。1 2 いまのところ、Firefox にのみ対応しています。
主な機能は以下のとおりです。
LGTM 表示
- Qiita の記事を開きながらツールバーのアイコンをクリックすると、ポップアップが開きます
- ポップアップでは、その記事の LGTM を最大 100 件まで表示します
- 100 件以上表示したい場合や別タブで表示したい場合は、一番下の More をクリックしてください3
アクセストークン追加
- Qiita で発行されたアクセストークンを設定画面に追加すると、API の利用制限を緩和することができます4
MDN
今回、MDN の ブラウザー拡張機能 にはかなり助けられました。
初めての拡張機能 はあまりにも簡単なので、ざっと眺める程度にして、 2つめの拡張機能 から作り始めるのが良いように感じました。
リファレンス の海にいきなり飛び込むのもアリだと思いますが、個人的には 逆引きリファレンス を先に見ておけば良かったと思うこともありました(後述)。
web-ext
web-ext
は「WebExtensions のビルド・実行・テストを支援するコマンドラインツール」5 です。2つめの拡張機能 では最後のほうに小さく書かれているだけだったり、入門記事が 英語 しかなかったりと敷居が高いですが、使ってみるとかなり便利だったので、オススメです。
インストール
web-ext
は Node.js ベースなので、あらかじめ Node.js をインストールしておく必要があります。Node.js をインストールしたら、以下のコマンドを実行して
web-ext
をインストールします。npm install --global web-ext実行
アドオンを開発しているディレクトリへ移動し、
web-ext run
を実行します。$ cd アドオンの開発ディレクトリ $ web-ext run
web-ext run
はディレクトリに格納されているアドオンを読み込んで、Firefox を起動します。about:debugging
を開くと、 一時的な拡張機能 としてアドオンが読み込まれていることを確認できます。
web-ext run
は、デフォルトでは一時プロファイルを作って Firefox を起動しますが、既存のプロファイルを指定することもできます。例えば、develop
というプロファイルを使いたい場合は、以下のように指定します。$ web-ext run --firefox-profile=develop反映
web-ext
を使わない場合、コードを変更するたびに、about:debugging
を開き、「再読み込み」ボタンを押す必要があります。実際に何回かやってみると分かると思いますが、コードを変更するたびにボタンを押すのは面倒ですし、押し忘れていたときのイライラ感は耐えがたいものです。
web-ext
を使う場合、「再読み込み」ボタンを押す必要はありません。コードが変更されたことをweb-ext run
が検知して、自動的に再読み込みを行ってくれるからです。6なお、
web-ext run
を実行しているときに誤って「再読み込み」ボタンを押すと、同じアドオンが重複して読み込まれてしまうので注意が必要です。コードチェック
web-ext lint
を実行すると、コードをチェックしてくれます。例えば、サニタイズせずにinnerHTML
に代入しようとしている場合は、以下のような警告が表示されます。7$ cd アドオンの開発ディレクトリ $ web-ext lint{ "count": 1, "summary": { "errors": 0, "notices": 0, "warnings": 1 }, (中略) "errors": [], "notices": [], "warnings": [ { "_type": "warning", "code": "UNSAFE_VAR_ASSIGNMENT", "message": "Unsafe assignment to innerHTML", "description": "Due to both security and performance concerns, this may not be set using dynamic values which have not been adequately sanitized. This can lead to security issues or fairly serious performance degradation.", "column": 9, "file": "src/controller/OptionsController.js", "line": 102 } ] }ビルド
アドオンが完成して addons.mozilla.org へ登録する際は、zip ファイルで提出することになります。
web-ext build
を使うと、.git
など通常パッケージ化する上で不要なファイルを無視して zip 化してくれます。$ cd アドオンの開発ディレクトリ $ web-ext build開発ツール
アドオンといっても、WebExtensions API を除けば、普通の HTML / CSS / JavaScript です。通常の Web 開発と同様に、インスペクターやコンソール、デバッガーなどの 開発ツール を使うことができます。
アドオンで開発ツールを使うには、
about:debugging
の「調査」ボタンを押します。ただし、ポップアップを表示する場合は、開発ツールの設定で「ポップアップを自動で隠さない」にチェックを入れておいたほうが良いでしょう( 参考 )。画面の他の場所をクリックしてもポップアップが開いたままになります(ポップアップを閉じたいときは
Esc
キーを押します)。WebExtensions API
アクティブなタブの情報を取得するには
現在開いているタブの URL を取得する場合のように、アクティブなタブの情報を取得したいことがあると思います。そうした場合は
tabs.query()
( MDN )を使います。const tabs = await browser.tabs.query({active: true, currentWindow: true}); const url = tabs[0].url;
tabs.query()
の戻り値は、tabs.Tab
型( MDN )の配列です。tabs.Tab
のurl
プロパティに URL が格納されています。なお、メソッドをコールせずに
tabs.Tab
型を直接参照することはできません。また、メソッド名が紛らわしいですが、tabs.getCurrent()
( MDN )も使えません。バックグラウンドスクリプトやポップアップで使うとundefined
が返ってくるからです。MDN にも以下のように注意書きがあります。Note: This function is only useful in contexts where there is a browser tab, such as an options page.
If you call it from a background script or a popup, it will return undefined.
Tabs API については 逆引きリファレンス に情報がまとめられているので、リファレンスを見る前に、そちらに目を通したほうが良いでしょう。
ポップアップからタブへメッセージを送るには
もしかすると、ここが一番苦戦したところかもしれません。Qiine では、ポップアップを開いた際に Qiita API を使って、以下の情報を取得します。
- GET /api/v2/items/:item_id
- 記事に関する情報(タイトルなど)
- GET /api/v2/items/:item_id/likes
- 記事につけられた LGTM に関する情報(最大 100 件まで取得)
ポップアップ最下部の More ボタンを押した場合、新しくタブを開きますが、そこにも同じ情報を表示します。すでに取得済みのデータをもう一度 API で取得するのは API を消費しすぎるので避けたいところです。
そこで、ポップアップからタブへデータを受け渡す方法を探しました。コンテンツスクリプトの解説に バックグラウンドスクリプトとの通信 が載っています。どうやらバックグラウンドスクリプトとコンテンツスクリプトのあいだでメッセージを送り合うことで、データを受け渡すことができるようです。
ポップアップはそのどちらでもありませんが、
tabs.sendMessage()
( MDN )の概要を見ると、ポップアップでも使えるように読める記載がありました。Sends a single message from the extension's background scripts (or other privileged scripts, such as popup scripts or options page scripts) to any content scripts or extension pages/iframes that belong to the extension and are running in the specified tab.
まず、メッセージの送信元であるポップアップ側を作ります。
tabs.sendMessage()
をコールして、指定したタブにメッセージを送信します。第1引数には送信先のタブ ID を、第2引数にはメッセージを指定します。メッセージには、JSON でシリアライズできるオブジェクトなら何でも指定できるようです。await browser.tabs.sendMessage(tabId, { item: this._item, lgtms: this._lgtmList, lastPageNo: this._lastPageNo });次に、メッセージの送信先では
runtime.onMessage
イベント( MDN )を使って受信します。addListener()
の第1引数に、イベントが発生したときに実行される関数を指定します。送られてきたメッセージは、関数の第1引数(下記のコードではrequest
)に格納されています。browser.runtime.onMessage.addListener((request) => { const controller = new MoreController(); controller.init(request.item, request.lgtms, request.lastPageNo); });これでうまくいくと思ったのですが... コンソールに
Could not establish connection. Receiving end does not exist.
と表示されて、うまく動いてくれません。検索すると、同じ事象に遭遇した方々を見つけました。
- WebExtensionsのtabs.sendMessageが動かない
- chrome extension の Unchecked lastError value: Error: Could not establish connection. Receiving end does not exist.の対処法
どうやら、メッセージ送信時に、送信先のタブの読み込みが完了していないことが原因のようです。タブの読み込み状況は
tabs.Tab.status
で確認でき、読み込み中の場合はloading
を、読み込みが完了した場合はcomplete
を返します。実際に試してみると、たしかにloading
のままになっていました。そこで、teratail の解決策を参考に
tabs.onUpdated
( MDN )を使うことにしました。これはタブが何らか更新されたときに発火するイベントです。その中にはstatus
がloading
からcomplete
へ更新された場合も含まれています。このイベントにリスナー関数を追加すれば、「送信先の読み込みが完了しているときにのみメッセージを送信する」こともできそうです。最終的に書いたコードは以下のとおりです。
tabs.onUpdated
にリスナー関数を登録し、送信先のタブの読み込みが完了したときにのみsendMessage()
をコールしています。async displayMorePage() { // 別タブを作成する const tab = await browser.tabs.create({ url: "/page/more/more.html" }); // onUpdated で実行されるリスナー関数 const listener = async (tabId, changeInfo, tab) => { // 読み込み中の場合は何もしない if (tab.status !== "complete") { return; } // メッセージを送信する await browser.tabs.sendMessage(tabId, { item: this._item, lgtms: this._lgtmList, lastPageNo: this._lastPageNo }); }; // onUpdated にリスナー関数を追加する browser.tabs.onUpdated.addListener(listener, { properties: ["status"], tabId: tab.id }); }なお、
tabs.onUpdated.addListener()
の第2引数はtabId
(タブ ID)だけでも問題なさそうですが、念の為、properties
にstatus
を指定しています。こうすることで、status
(タブの読み込み状況)が更新されたときにのみ、リスナー関数が実行されるようにしています。Qiita API
ここからは、Qiita API に特化した話題を書いていきます。
OAuth
Qiita API には、以下のような 利用制限 があります。
認証している状態ではユーザごとに1時間に1000回まで、認証していない状態ではIPアドレスごとに1時間に60回までリクエストを受け付けます。
Qiine では、Qiita API で一度に取得できる上限の100件単位で LGTM を取得していますが、それでもかなりの量の API を消費することが考えられ、認証認可はぜひ実装したいと思いました。8
もともと、Qiita API には OAuth を使った認可の仕組みが用意されています( API ドキュメント )。また、WebExtensions API でも identity API が用意されており、簡単に OAuth を利用することができます。
そこで、それらを組み合わせて、当初、以下のようなコードを書いていました。最終的には破棄しましたが、参考までに記載しておきます。
document.querySelector("#login").addEventListener("click", () => { // Qiita で発行された Client ID、Client Secret const clientId = "****************************************"; const clientSecret = "******************"; let authUrl = "https://qiita.com/api/v2/oauth/authorize"; authUrl += "?client_id=" + clientId; authUrl += "&scope=read_qiita"; browser.identity.launchWebAuthFlow({ interactive: true, url: authUrl, }).then(authResult => { // リダイレクト先の URL から code を抜き出す const code = (new URL(authResult)).searchParams.get("code"); const body = { "client_id": clientId, "client_secret": clientSecret, "code": code }; // アクセストークンを取得する fetch("https://qiita.com/api/v2/access_tokens", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), mode: "cors" }).then(response => { if (!response.ok) { throw new Error("Fail to Authorize."); } return response.json(); }).then(data => { const token = data.token; // ストレージへトークンを保存する browser.storage.local.set({ token: token }); }); }) });このコードを破棄した理由は「
client_secret
を配布物に含めたり GitHub に載せるのは良くないのでは?」と思ったからです。配布物にOAuth2で利用するclient_secretを含むことのリスク なども見て、やっぱり違うよなと。Qiita API が他のフローも用意してくれているのであれば、それを使ったのですが、OAuth を使ったフローは
client_secret
を使うフローしか無いようでした。他にアクセストークンを発行する方法は、ユーザが自分で Qiita の管理画面から発行する方法しか無いようでした。
アクセストークンは、後述するOAuthを利用した認可フローか、ユーザの管理画面から発行できます。
そこで、ユーザが自分でアクセストークンを発行し、それをアドオンの設定画面で追加できるようにすることで、お茶を濁すことにしました。
user.organization
GET /api/v2/items/:item_id/likes で LGTM を取得したところ、
user.organization
(ユーザの属する組織名)に 空文字 と null が混在していました。9以下のように ドキュメント にも
null
が返ってくる可能性があることは示されているのですが、空文字しか想定していなかったので、危うくバグになるところでした。organization
所属している組織
Example: "Increments Inc"
Type: null, string感想
実際に作ってみると、アドオンも、中身は普通の HTML / CSS / JavaScript なんだなと思いました。正直、WebExtensions API で躓いたところよりも、CSS や JavaScript で躓いたところのほうが多かった気がします(あまりにも低レベル過ぎて、あえて記事には書きませんでしたが)。
火狐教徒なので Firefox を優先しましたが、いずれは Chrome にも対応したいですね。同じ WebExtensions API なので、そのまま動くのかと思いきや、どうやら Polyfill を使わないといけないらしく... リリースを優先して、そちらは一旦先送りにしました。10 何かありましたら、GitHub のほうへ Issue を立てていただければ幸いです。
転職活動とは全く関係なく11、純粋に個人的に欲しかったので作りはじめたのですが、addons.mozilla.org の審査もパスしたし、これをポートフォリオにしようかなと思いました。PHP の経験を PR しているのに、ポートフォリオが JavaScript ってどうなん?とは自分でも思いますが(苦笑)
きっかけは、はてなブックマークの 公式アドオン を重宝していて、その Qiita 版のようなものがあったらいいのになーと思ったことです。半年ぐらい前からアイディアだけあったのですが、ある日曜日(8/2)の夜に、酒を飲みながら MDN を眺めていたら、意外と簡単そうで、これなら1週間ぐらいで作れそうな気がした。なので作ってみました。 ↩
「Qiine」の語源は「Qiita」+「いいね」です。思いつきで名付けました。 ↩
Qiita API の ページネーション の仕様上、最大 10,000 件まで表示することができます。(1 ページ最大 100 件 × 最大 100 ページ = 10,000 件) ↩
GitHub の以下の文章の和訳。 "This is a command line tool to help build, run, and test WebExtensions." ↩
create-react-app では、
npm start
を実行しているあいだにコードを変更すると、自動で再コンパイルが行われますが、それに近いものを感じました。 ↩デフォルトではテキスト形式で出力されますが、横に長くてコピペしにくかったので JSON 形式で出力しました。使用したコマンドは
web-ext lint -o json --pretty
です。 ↩そういえば、Qiita API のドキュメントには、利用制限に到達した際に、どのくらいの時間が経過したら利用制限が解除されるかが明記されていないように見えます。 ↩
ちなみに、一度も組織名を登録したことがない私の
user.organization
は空文字でした。一度組織名を登録して削除した場合とかにnull
になるのでしょうか?(試していないですが...) ↩こだわりはじめると、芋づる式にこだわって永遠に完成しないという悪癖があるので... 「1週間ぐらいで」と期限を決めたのもそのためです。 ↩
現在、無職3ヶ月目。そろそろどこかに転職しなければと思いつつ、なかなか良いところが見つからず。 ↩
- 投稿日:2020-08-11T18:03:48+09:00
【Nuxt.js】Nuxt実践編:ユーザーアイコン表示の切り替えをしよう
? この記事はWP専用です
https://wp.me/pc9NHC-ye前置き
今回は簡単な実践編です??
作るものはこちら!マイページのあるサイトで
⬆️ユーザーアイコンの初期状態と
⬇️画像を設定した時の
表示の切り替えをやってみましょう??コンポーネント間のやりとりや
演算子の復習になるのでぜひチャレンジしてください??今回のような簡単な実践編も
今後増やしていこうと思ってます?Let's try!
ディレクトリ
filecomponents/ --| atom/ ----| icons/ ------| IconUser.vue --| molecule/ ----| items/ ------| ItemUserIconName.vue pages/ --| user.vueアトミックデザインに基づき作成します?
atomはアイコン部分。
他のmoleculeにも使い回す前提です。
moleculeはアイコンとユーザーの名前を追加したものです。アドミックデザイン
要素の大きさごとにファイルを分けます?♀️
コンポーネントがどこにあるのか
分かりやすくなります?
大きさの分類はここが参考になります!
Atomic Designとはatom
構成
IconUser.vueを作成しましょう?♀️
初期アイコンと、
アイコンが設定された場合の
表示を切り替えます。
アイコン画像は親で設定します。背景は縦横64px
アイコンは縦横24pxアイコンはiconmonstrのsvgを使用しています?
https://iconmonstr.com/user-5-svg/それではやってみましょう!!!
ticktack…
まずは切り替えをするので
v-ifとv-elseが使えますね…
親で画像を設定するのでpropsをstringで渡します。
stringを真偽値にしてv-ifとv-elseにしたい…それではコードを見てみましょう✨?
解説
? 続きはWPでご覧ください?
https://wp.me/pc9NHC-ye
- 投稿日:2020-08-11T17:40:15+09:00
スムーススクロール(ページ内移動)
//ページ内リンクのURLが "#"から始まる時(ex. "#service"や"#about")
script.js$('a[href^="#"]').click(function () { // スクロールの速度 var speed = 400; // ミリ秒で記述 var href = $(this).attr('href'); var target = $(href === '#' || href === "" ? 'html' : href); var position = target.offset().top; $('body,html').animate({ scrollTop: position }, speed, 'swing'); return false; });//ページ内リンクのURLが "/#"から始まる時(ex. "/#service"や"/#about")
script.js$(document).on('click', 'a[href*="#"]', function() { let time = 400; let target = $(this.hash); if (!target.length) return; let targetY = target.offset().top; $('html,body').animate({scrollTop: targetY}, time, 'swing'); return false; });
- 投稿日:2020-08-11T17:01:38+09:00
React初心者のための Material-UI 【Dialogの使い方編】
React + Material-UI を勉強しています。今日は「ダイアログ」「モーダル」などと呼ばれる機能の使い方をまとめます。
React のチュートリアルは一通り終えていざ自分で作ろうとするとつまずくことがあるかと思います。なかでも Material-UI の公式ページは便利だけど理解し辛いなあと思うので「使い方」の点からまとめます。ちなみに Dialog ってのはこんな奴ですね。GIFはMaterial-UI のサンプルページです。
大雑把なプロセスは
- Dialog のデザインを決める
- Dialog を含むコンポーネントを作る SampleDialog
- SampleDialog を開くトリガーを作る
- state のリフトアップ
となります。
ダイアログのデザインを決める
Dialog を含むコンポーネントのデザインを決めます。直接
<Dialog />
に props を渡せるなら一手間省けますが、今回はひとまわりラッピングされたコンポーネントを想定します。コンポーネントを作る
ダイアログとトリガーのコンポーネントを作ります。ここでは仮に SampleDialog, SampleCard と名付けます。トリガーのコンポーネントは Card にするつもりなので SampleCard と名付けました。 SampleCard のなかの Button をクリックしてダイアログを開くようにしたいと思います。
図のような構造になります。
SampleDialog を開くトリガーを作る
SampleDialog を開くトリガーを作ります。つまり、 SampleCard のなかに Button を配置します。
class SampleCard extends React.Component { render() { return ( <Card> <CardContent>Lorem ipsum dolor sit amet</CardContent> <CardActions> <Button variant="contained" color="primary"> Open Dialog! </Button> </CardActions> </Card> ); } }state のリフトアップ
このセクションがいちばん大変ですね笑。
React では2つ以上のコンポーネントで状態のやりとりをするために共通の親コンポーネントを作り state を一元化するという手法を取ります。これを state のリフトアップと呼んでいます。今回の場合は SampleDialog と SampleCard の間で open という bool 値を共有する必要があります。それを仮に Parent と名付けます。 Parent の state に open を用意します。
ダイアログを開く
- SampleCard 内部の Button がクリックされる
- Parent の open が true になる
- SampleDialog の props.open が true になりダイアログが開く
というステップがあります。 では、1 から 2 を繋ぐにはどうすればいいのでしょうか?子コンポーネントから親コンポーネントの state をいじるのはできません。そのため、親コンポーネントから渡す props に親コンポーネントの state を変更するメソッドを渡します。たとえば、
handleOpen
というメソッドを作ります。中身はthis.setState({open: true})
です。つまり、 Parent の state を変更するメソッドです。そして、 constructor でメソッドを.bind(this)
しておきます(ここは説明不十分だと思います。後日追記する予定)。
そして、このメソッドを SampleCard に渡します。
Card の props にonClick: handleOpen
が入っていますね。これを Button への props に渡すことで 1 から 2 へのステップが実現します。
2から3へのステップは簡単です。Parent から SampleDialog に渡された props.open を
<Dialog>
に渡します。これでダイアログを開けるようになりました。ダイアログを閉じる
Dialog は Backdrop(ダイアログの背景、つまり本体より外側の部分)をクリックすると
onClose
がコールバックされます。onClose
で Parent の open を false にすればダイアログも閉じられます。「ダイアログを開く」でやったのと同じように
- Parent で メソッド
handleClose
を定義する- Parent から SampleDialog へ props で
onClose={handleClose}
を渡すというステップです。
わかりにくかった点
Material-UI のサンプルは Hooks を使ったコードです。それを知らずに読んでいると混乱してしまいました。「俺の知ってる React じゃないぞ???」となりました笑。
おわり
途中で力尽きたので、コードサンプルは載せていません。気が向けば書きます笑。
自分を含めた初心者のための備忘録として書いています。間違い、もっとよい方法などございましたらぜひ教えてください。
参考
https://material-ui.com/api/dialog/
Dialog の props を調べられます。いろいろカスタムできます。
- 投稿日:2020-08-11T16:40:17+09:00
一から作るモダンフロントエンド環境【Webpack & Babel編】
どうも、ディベロッパーの羽田です。
最近業務でモダンJSフレームワークやwebpackを使うことが多くなってきたので開発環境の構築手順をまとめてみます。概要
ウェブ開発のデファクトスタンダードとなりつつあるWebpackとモダンJSフレームワークを使用したフロント開発環境構築の手順をまとめました。
自分用のメモも兼ねてるので雑な部分や端折ったりしてる部分があります。対象者
- ある程度JSの知識がある人
- モダンJSフレームワークに興味がある人
- Webpackを使用した開発環境構築に興味ある人
章節
- 【基本構築編】
- 【Webpack & Babel編】←イマココ
- 【css & sass編】(執筆中)
- 【React & React Router編】(執筆中)
- 【Redux編】(執筆中)
Webpack周辺を構築
パッケージ名 説明 webpack 複数のファイルを1つにバンドルして出力してくれるツール。JSファイルを起点にローダーを読み込ませることであらゆるファイルをバンドルできる。 webpack-cli コマンドラインでwebpackを実行するために必要なパッケージ。 webpack-dev-server webpackを使用した開発環境向けのwebサーバー。様々なオプションを設定可能。 html-webpack-plugin webpackから生成したJavaScriptを埋め込んだHTMLテンプレートを生成できる。 html-loader webpackにHTMLを読み込む。 webpack周りの構築を行います。
以下のコマンドを実行してパッケージをダウンロードしてください。$ npm i -D webpack webpack-cli webpack-dev-server html-webpack-plugin html-loader次にwebpackの設定を行うために
webpack.config.js
を作成します。webpackにはプラグインやローダー(JSファイル以外のファイルをバンドルするための拡張プログラム)を追加することができますがその際の設定もここに記述できます。
webpack.config.jsconst path = require('path'); //node.js上でファイルパスを操作できるモジュール const HtmlWebpackPlugin = require('html-webpack-plugin'); //html-webpack-pluginモジュール const htmlWebpackPlugin = new HtmlWebpackPlugin({ template: "./src/index.html", filename: "./index.html" }); module.exports = { mode: 'development', //ビルド時の出力ファイルを圧縮するかしないかの設定。今回はdevelopment。 entry: './src/index.js', //ビルド時に起点となるJSファイルを指定。 output: { filename: 'bundle.js', //ビルド時に出力されるファイル名を設定 path: path.resolve(__dirname, 'dist') //出力先のパスを指定(ここで絶対パスを指定するためにpathモジュールを使用) }, devServer: { port: 8000, //ローカルサーバーのポート番号を指定 open: true //webpack-dev-server起動時に自動でブラウザを開く設定 }, plugins: [ htmlWebpackPlugin //html-webpack-pluginをwebpackに追加 ] }Babelの設定
Babelとは最新バージョンのECMAScript(Ecma Internationalによって策定されたJavaScriptの標準仕様)を古い型へとコンパイルするツールです。ブラウザによっては最新のECMAScriptをサポートしていない場合があるのでReactなどのモダンJSフレームワークを使用する場合は必須となります。
パッケージ名 説明 @babel/core Babel本体のモジュール @babel/cli コマンドラインでBabelを実行するために必要なパッケージ。 @babel/preset-env 自動で実行環境に合わせたバージョンでコードをコンパイルする。 babel-loader webpackにBabelを読み込む。 導入するには以下のコマンドを実行
$ npm i -D @babel/core @babel/cli @babel/preset-env babel-loaderBabelの導入に合わせて
webpack.config.js
に設定を追記します。webpack.config.jsconst path = require('path'); //node.js上でファイルパスを操作できるモジュール const HtmlWebpackPlugin = require('html-webpack-plugin'); //html-webpack-pluginモジュール const htmlWebpackPlugin = new HtmlWebpackPlugin({ template: "./src/index.html", filename: "./index.html" }); module.exports = { mode: 'development', //ビルド時の出力ファイルを圧縮するかしないかの設定。今回はdevelopment。 entry: './src/index.js', //ビルド時に起点となるJSファイルを指定。 output: { filename: 'bundle.js', //ビルド時に出力されるファイル名を設定 path: path.resolve(__dirname, 'dist') //出力先のパスを指定(ここで絶対パスを指定するためにpathモジュールを使用) }, devServer: { port: 8000, //ローカルサーバーのポート番号を指定 open: true //webpack-dev-server起動時に自動でブラウザを開く設定 }, //Babel追記箇所 loader: { //ここに各種ローダーを追加する rules: [ { test: /\.js$/, exclude: /node_modules/, //コンパイル対象からnode_modulesを除外 loader: "babel-loader", //ローダーの指定 query:{ presets: ["@babel/preset-env"] } }, ] }, plugins: [ htmlWebpackPlugin //html-webpack-pluginをwebpackに追加 ] }ビルド用のファイルを作成
Webpackを実行してアプリケーションファイルを出力するためには元となるファイルが必要です。
myApp
フォルダのディレクトリ下にsrc
フォルダを作成、src
フォルダ内部にindex.html
ファイルとindex.js
ファイルを作成して下記のコードをコピペしてください。index.html<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>myapp</title> </head> <body> <h1 id="text"></h1> </body> </html>index.jsconst text = document.getElementById('text'); text.innerHTML = "Hello Webpack!"実際にwebpackを実行してみる
webpackを実行するためのスクリプトを
package.json
に追記します。
内容は前回の続きからです。package.json{ "name": "myapp", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "build": "webpack", "start": "webpack-dev-server" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "@babel/cli": "^7.10.5", "@babel/core": "^7.11.1", "@babel/preset-env": "^7.11.0", "babel-loader": "^8.1.0", "html-loader": "^1.1.0", "html-webpack-plugin": "^4.3.0", "webpack": "^4.44.1", "webpack-cli": "^3.3.12", "webpack-dev-server": "^3.11.0" } }追記箇所はscriptsプロパティの値です。
これで以下コマンドが使えるようになりました。$ npm run build $ npm run start
npm run build
がwebpackを実行してアプリケーションをビルドするコマンド。
npm run start
がwebpack-dev-serverを実行してローカルサーバー上でファイルを実行するコマンドになります。実際にビルドしてみるとディレクトリ内に
dist
フォルダが生成され、その中にアプリケーションファイルがあるのが確認できるはずです。確認のためwebpack-dev-serverを実行して確認してみましょう。
npm run start
を実行してください。ブラウザ上にHello Webpack!と表示されれば成功しています。
ここまで出来たら一旦webpackの基礎構築は完了です。
次はcssや画像周りを構築していきます。
続く...
- 投稿日:2020-08-11T15:08:44+09:00
『GAFAコーディング面接こんな感じでした』を読んで
GAFAコーディング面接こんな感じでした - yambe2002’s diary を読んで、課題のコードを書いてみる気に。
元記事だと「URL の履歴を保存するデータ構造」がひとつだけど、ふたつ (進む用 + 戻る用) に分割した方が良さそう。
あと、今更 React のチュートリアルを読んだので、練習を兼ねて。
ついでに Bootstrap 5 alpha もチラ見しよう。
Web ブラウザの履歴を管理するようなコード
比較用に 2 種類をつくる。
感想
Bootstrap 5 alpha
- v4 からの破壊的変更は少なめ
- v4 と v5 の差異 で気になった点は以下
- Custom forms 廃止 (通常のフォームに統合)
- フォームのレイアウト周りの廃統合
.form-row
->.row
.form-group
->.mb-3
.form-inline
->.col-auto
など- フォームラベルに
.form-label
が必要に- cf. https://v5.getbootstrap.com/docs/5.0/forms/layout/
.form-control-file
->.form-file
.form-control-range
->.form-range
- IE 11 などのサポート終了
- jQuery 廃止
- DOM API (Vanilla JS) が高速なのは分かるし使うけど、書き易くはない。。
React
- state と props の違い
state
- コンポーネント内部で使うプライベート変数のようなオブジェクト
- 変更は必ず
setState()
メソッドを使うことprops
- 別のコンポーネントから渡される、読み取り専用のオブジェクト
- React と HTML における属性名の差異
class
->className
for
->htmlFor
readOnly
->readOnly
など- jQuery との比較
- JSX とコンポーネントにより、JS 上での CSS セレクターによる要素の指定がほぼ不要に。
sate
オブジェクトで状態を保持して、それを更新するだけで自動的に DOM 側にも反映できるので簡潔に記述できる。- TODO
- コンポーネントをもっと分割できるのか?
- フォームの検証はどう書くのが良いのか?
- 良いコードをもっと読まないと。
- 投稿日:2020-08-11T14:13:55+09:00
ReactのRedux内でaxiosを使用した通信をする
はじめに
前々回のReactを使用してWeb画面を作成するでは、reactの簡単なサンプルと共に説明をまとめました。
前回のReduxとReduxToolkitを使用してReact内でデータを管理するでは、reduxのの簡単なサンプルと共に説明をまとめました。
今回はaxiosを使用してREST APIから情報を取得して画面に表示させるサンプルと説明をまとめようと思います。環境
- node.js: v12.18.2
- webpack: 4.44.1
- React: 16.13.1
- Redux: 7.2.1
- axios: 0.19.2
環境作成
前回のReactを使用してWeb画面を作成すると前回のReduxとReduxToolkitを使用してReact内でデータを管理するの続きとなります。
環境構築などはそちらを見てください。axios
REST APIへ通信するためにaxiosというライブラリを使用します。
axiosのインストール
npm install axiosRedux Thunk
axiosは非同期で通信を実行します。しかし、Reduxは非同期の処理をするのは若干手間です。それらを楽にするためにRedux Thunkというミドルウェアを使用します。
Redux Thunkのインストール
npm install redux-thunkBabel/polyfill
axiosを使用するにあたってpolyfillが必要になります。
Babel/polyfillのインストール
npm install @babel/polyfill --save-devReactでaxiosを使用する
Reactのソースの作成
前回のReduxとReduxToolkitを使用してReact内でデータを管理するの続きなので、ソースも前回のに対して変更していく形にします。
sliceファイルにREST API呼び出しの処理を追加
REST APIの呼び出しと呼び出した後の処理をsliceファイルに追加する。
今回呼び出すREST APIはhttp://localhost:3000/mess_api
、ボディは{["message":"testMessage"]}
を想定して、このtestMessageをstateのmessに入れて、画面に表示させます。messageSlice.jsimport { createSlice } from '@reduxjs/toolkit'; import axios from 'axios'; export const messageSlice = createSlice({ name: 'message', ~~~ 前回と同じ ~~~ reducers: { ~~~ 前回と同じ ~~~ // action内にREST APIから取ってきたデータが入っている getRequest: (state, action) => { // body内の最初のmessageキーの値をstateに入れる state.mess = action.payload[0].message; } }, }); // 今回追加したgetRequestもエクスポートする export const { sayhello, sayAmount, getRequest} = messageSlice.actions; // RESTAPIの発行とgetRequestの呼び出しをする export const getRequestAsync = amount => async dispatch => { // ここでREST APIの呼び出しをする const response = await axios.get('http://localhost:3000/mess_api').then(response => { // 呼び出した結果のボディだけgetRequestに与える(与えるものを返れば上のaction引数に入る内容も変わる) dispatch(getRequest(response.data)); }); }; export default messageSlice.reducer;storeファイルにthunkを追加
store.jsimport { configureStore } from '@reduxjs/toolkit'; import thunk from 'redux-thunk'; import messageReducer from './slice/messageSlice'; export default configureStore({ reducer: { message: messageReducer, }, middlewares: thunk });slice経由でstoreを使用する
コンポーネントでREST APIを発行する際には、sliceで作成したactionを呼び出してstoreを操作します。
App.jsimport React, { useState } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { sayhello, sayAmount, getRequestAsync } from '../store/slice/messageSlice'; export function Message() { const message = useSelector(state => state.message.mess); const dispatch = useDispatch(); const [messsageAmount, setMesssageAmount] = useState('2'); ~~~ 前回と同じ ~~~ {/* sliceで作成したgetRequestAsyncアクションを使用する */} <button aria-label="rest get" onClick={() => dispatch(getRequestAsync())}> 通信 </button> {/* テキストボックスとボタンにインポートしたものを適用する */} <input aria-label="set amount" value={messsageAmount} onChange={e => setMesssageAmount(e.target.value)} /> <button onClick={() => dispatch(sayAmount(messsageAmount))}> テキスト変更 </button> </div> </div> ); }Reactの実行
前回同様にReactを開発用のサーバで起動してブラウザからアクセスしてください。
"./node_modules/.bin/webpack-dev-server"通信ボタンを押すとREST APIを発行して、そのボディをstoreに格納することでWeb画面上のテキストも変わります。
REST APIサーバを立てる
REST APIさえ発行できれば何を使用しても良いのですが、手ごろな環境がない場合に簡単なREST APIサーバを立てる方法もまとめます。
node.jsがある環境なのでJSON Serverというライブラリを使用してREST APIを立てます。JSON Serverのインストール
npm install -g json-serverJSON用ファイルの作成
JSONのエンドポイントとボディを定義したファイルをJSON形式で作成します。
今回はReactからエンドポイント:mess_api
、ボディのキー:message
から情報を取っているためそのように記載します。db.json{ "mess_api": [ { "id": 1, "message": "testMessage" } ] }JSON Serverの起動
先ほど作成したJSON用ファイルを指定してJSON Serverを起動します。
json-server --watch db.jsonコンソールに起動した旨が表示されればREST APIサーバの完成です。
終わりに
ReactのWeb画面作成に関しては、ここで一旦終わりにしようかと思います。React Routerなど他のライブラリもReactにありますが、特に難しくなかったのでまとめなくて良いかなと思います。
REST APIに通信できるようになるとWeb画面で出来ることが増えるため、とても便利だと思います。しかし、Reactでのaxiosの使い方とReduxでのaxiosの使い方が結構違うので苦労しました。
次回はreact-testing-libraryの使い方でもまとめてみようと思います。
- 投稿日:2020-08-11T13:37:33+09:00
コールバック関数について
はじめに
コールバック関数について全く理解していなかったのでここで自分用にアウトプット、メモしておきたいと思います!!
何か至らない点などあれば教えていただけると幸いです!!コールバック関数とは
別の関数に渡す関数のこと
関数内では実際に処理を行わず、関数を引数として引き渡しす関数のこと
簡単に言うとこんなところ!!使い方
//コールバック関数を実行する関数 const parentsCallback = (callback) => { console.log('I call callback'); callback(); } //渡すコールバック関数 const callback = () => { console.log('This is my callback'); } //コールバック関数を引数で渡して実行!! parentsCallback(callback);このように関数を引数として扱う!!
なぜ使うのか
1.非同期処理などのときに行って欲しい順番で処理を実行したいとき
2.関数実行後の処理を自由に決めるため(関数を渡すことでその後の処理)コールバック関数を使うと非同期での処理を管理できるようになる!!
const showHoge = () => { var hoge = ""; setTimeout(function(){ hoge = "hoge"; console.log(hoge); }); } showHoge();これだと再代入した "hoge"は入ってこない!!なぜならsetTimeoutと言う関数が非同期処理により飛ばされてしまい、そのままconsole.log(hoge)に移ってしまうからである!!
そのためsetTimeoutと言う外部通信での非同期処理を制御するためコールバック関数を使う!!
const logOut = (param) => { console.log(param) } function WithCallback(e){ var hoge; setTimeout(function(){ hoge = 'hoge'; callback(hoge); //logOutにhogeを渡している!! }); } WithCallback(logOut); //こうすることで再代入後の値がlogに入ってくる!! console.log(withCallbackこのように非同期などの処理の後に行って欲しい処理を行う場合、関数をそこで呼び出すのではなく、引数として渡して呼び出す!!
最後に
複雑なことはまだ理解できていないのでコールバック関数の簡単な説明としてここではアウトプットしています!!
簡単すぎた、またもっと理解を深めたい方はこちらからどうぞ!!参考
- 投稿日:2020-08-11T13:37:33+09:00
自分用メモ コールバック関数について
はじめに
コールバック関数について全く理解していなかったのでここで自分用にアウトプット、メモしておきたいと思います!!
何か至らない点などあれば教えていただけると幸いです!!コールバック関数とは
別の関数に渡す関数のこと
関数内では実際に処理を行わず、関数を引数として引き渡しす関数のこと
簡単に言うとこんなところ!!使い方
//コールバック関数を実行する関数 const parentsCallback = (callback) => { console.log('I call callback'); callback(); } //渡すコールバック関数 const callback = () => { console.log('This is my callback'); } //コールバック関数を引数で渡して実行!! parentsCallback(callback);このように関数を引数として扱う!!
なぜ使うのか
1.非同期処理などのときに行って欲しい順番で処理を実行したいとき
2.関数実行後の処理を自由に決めるため(関数を渡すことでその後の処理)コールバック関数を使うと非同期での処理を管理できるようになる!!
const showHoge = () => { var hoge = ""; setTimeout(function(){ hoge = "hoge"; console.log(hoge); }); } showHoge();これだと再代入した "hoge"は入ってこない!!なぜならsetTimeoutと言う関数が非同期処理により飛ばされてしまい、そのままconsole.log(hoge)に移ってしまうからである!!
そのためsetTimeoutと言う外部通信での非同期処理を制御するためコールバック関数を使う!!
const logOut = (param) => { console.log(param) } function WithCallback(e){ var hoge; setTimeout(function(){ hoge = 'hoge'; callback(hoge); //logOutにhogeを渡している!! }); } WithCallback(logOut); //こうすることで再代入後の値がlogに入ってくる!! console.log(withCallbackこのように非同期などの処理の後に行って欲しい処理を行う場合、関数をそこで呼び出すのではなく、引数として渡して呼び出す!!
最後に
複雑なことはまだ理解できていないのでコールバック関数の簡単な説明としてここではアウトプットしています!!
簡単すぎた、またもっと理解を深めたい方はこちらからどうぞ!!参考
- 投稿日:2020-08-11T13:28:47+09:00
一から作るモダンフロントエンド環境【基本構築編】
どうも、ディベロッパーの羽田です。
最近業務でモダンJSフレームワークやwebpackを使うことが多くなってきたので開発環境の構築手順をまとめてみます。概要
ウェブ開発のデファクトスタンダードとなりつつあるWebpackとモダンJSフレームワークを使用したフロント開発環境構築の手順をまとめました。
自分用のメモも兼ねてるので雑な部分や端折ったりしてる部分があります。対象者
- ある程度JSの知識がある人
- モダンJSフレームワークに興味がある人
- Webpackを使用した開発環境構築に興味ある人
章節
- 【基本構築編】←イマココ
- 【Webpack & Babel編】
- 【css & 画像バンドル編】(執筆中)
- 【React & React Router編】(執筆中)
- 【Redux編】(執筆中)
前提
実際のウェブ開発を想定して構築します
事前に導入しておくもの
- Node.js
- npm
上記ソフトウェアを使用するので環境に無ければインストールしておきます
$ node -v v12.16.1 $npm -v v6.13.4執筆時点のバージョン情報。
作業環境と下準備
ターミナル or コマンドラインを開き、任意のディレクトリ内で以下コマンドを実行。
mkdir myapp cd myappmyAppディレクトリ内で、下記コマンドを実行。
$ npm init -yすると以下のjsonファイルが生成されます。
packege.json{ "name": "myapp", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }ここまでが下準備。
続きは以下リンクから。
- 投稿日:2020-08-11T12:45:33+09:00
三項演算子の世界へようこそ
はじめに
三項演算子、使ってますか?
先日とうとう後輩の @miriwo 氏がめでたく三項演算子に目覚めたということで、これはめでたいということで筆を執りました。まあ、タイトルだけ見るとエルビス演算子に目覚めたのかなって気になってましたけど。
それはそれで大変便利なのです。三項演算子とは
例を見てみましょう。
転載元: https://chiicomi.com/smp/r_news_detail?id=19e92c3c-0b86-11e6-8bd0-a0369f7b8e6eこちらは我らがジェフユナイテッド市原千葉のマスコット、ジェフィとユニティです。
(千葉なのに)秋田犬の双子のわんこなんですが、キャラクターデザイン上の取り計らいで、大きさと背番号以外は区別がつかないようになっています。
ある程度のレベルのジェフサポになると 「キリッとしてるほうがジェフィ、やんちゃな方がユニティ」 とか 「耳がシャキッとしてるほうがジェフィ、丸っこい耳がユニティ」 とか言い出したりするんですが、他サポからは 「ジェフサポは(いろいろな意味で)あたまがおかしい」 と思われているので聞き入れてもらえません。なので、犬の背番号が2だったらジェフィ、2じゃなかったユニティを
name
に代入するコードをJavaScriptで書いてみました。const name = (dog.uniform_number === 2) ? 'Jefy' : 'Unity';
?
の左に判定条件、その右に判定がtrue
のときの値、:
を挟んでその右がfalse
のときの値です。
これをたまにいる三項演算子否定派が書くとこんな感じになります。const name = ''; if(dog.uniform_number === 2){ name = 'Jefy'; } else { name = 'Unity'; }長い……
要はこういうのをif文ではなく式で簡便に表現できるというのが三項演算子です。
実は本名は「条件演算子」といいます。https://ja.wikipedia.org/wiki/%E6%9D%A1%E4%BB%B6%E6%BC%94%E7%AE%97%E5%AD%90
三項演算子は結構どの言語にも実装されています。
入れ子の三項演算子
あんまりやってはいけないのですが、三項演算子はネスト(入れ子)して書くことができます。
例を見てみましょう。
実はジェフのマスコットはジェフィとユニティのオス犬の双子だけではなく、どこからともなく現れた みなちゃん という白い雑種のメス犬もいます。写真の一番右の子ですね。
ちなみにジェフィとユニティは毎年シーズンシートを買って入っている一般サポーターらしいのですが、みなちゃんはれっきとしたジェフユナイテッド株式会社の社員です。そんな設定の話は置いといて、みなちゃんの背番号はサポーターナンバーと呼ばれる12番です。つまりこんな感じになります。
const name = (dog.uniform_number === 2) ? 'Jefy' : (dog.uniform_number === 9) ? 'Unity' : 'Mina-chang';どうですか? 見づらくてハゲそうですよね。
見やすくするテクニックとして、記号の前で改行するというのがあります。
外人が書くコードでよく見かけますよね。const name = (dog.uniform_number === 2) ? 'Jefy' : (dog.uniform_number === 9) ? 'Unity' : 'Mina-chang';ただ、この入れ子の三項演算子、PHPだけなぜか動作が違います。あいつはなぜか 右から評価 するので、こんな感じに書かないと思ったとおりになりません。
$name = (dog.uniform_number === 2) ? 'Jefy' : ( (dog.uniform_number === 9) ? 'Unity' : 'Mina-chang');不毛感がすごい……
おわりに
三項演算子の特殊な書き方として、エルビス演算子とかnull合体演算子の話は次回書きます。
それではお元気で! Enjoy, Summer!!
- 投稿日:2020-08-11T12:45:33+09:00
(ジェフユナイテッドで覚える)三項演算子の世界へようこそ
はじめに
三項演算子、使ってますか?
先日とうとう後輩の @miriwo 氏がめでたく三項演算子に目覚めたということで、これはめでたいということで筆を執りました。まあ、タイトルだけ見るとエルビス演算子に目覚めたのかなって気になってましたけど。
それはそれで大変便利なのです。三項演算子とは
例を見てみましょう。
転載元: https://chiicomi.com/smp/r_news_detail?id=19e92c3c-0b86-11e6-8bd0-a0369f7b8e6eこちらは我らがジェフユナイテッド市原千葉のマスコット、ジェフィとユニティです。
(千葉なのに)秋田犬の双子のわんこなんですが、キャラクターデザイン上の取り計らいで、大きさと背番号以外は区別がつかないようになっています。
ある程度のレベルのジェフサポになると 「キリッとしてるほうがジェフィ、やんちゃな方がユニティ」 とか 「耳がシャキッとしてるほうがジェフィ、丸っこい耳がユニティ」 とか言い出したりするんですが、他サポからは 「ジェフサポは(いろいろな意味で)あたまがおかしい」 と思われているので聞き入れてもらえません。なので、犬の背番号が2だったらジェフィ、2じゃなかったユニティを
name
に代入するコードをJavaScriptで書いてみました。const name = (dog.uniform_number === 2) ? 'Jefy' : 'Unity';
?
の左に判定条件、その右に判定がtrue
のときの値、:
を挟んでその右がfalse
のときの値です。
これをたまにいる三項演算子否定派が書くとこんな感じになります。const name = ''; if(dog.uniform_number === 2){ name = 'Jefy'; } else { name = 'Unity'; }長い……
要はこういうのをif文ではなく式で簡便に表現できるというのが三項演算子です。
実は本名は「条件演算子」といいます。https://ja.wikipedia.org/wiki/%E6%9D%A1%E4%BB%B6%E6%BC%94%E7%AE%97%E5%AD%90
三項演算子は結構どの言語にも実装されています。
入れ子の三項演算子
あんまりやってはいけないのですが、三項演算子はネスト(入れ子)して書くことができます。
例を見てみましょう。
実はジェフのマスコットはジェフィとユニティのオス犬の双子だけではなく、どこからともなく現れた みなちゃん という白い雑種のメス犬もいます。写真の一番右の子ですね。
ちなみにジェフィとユニティは毎年シーズンシートを買って入っている一般サポーターらしいのですが、みなちゃんはれっきとしたジェフユナイテッド株式会社の社員です。そんな設定の話は置いといて、みなちゃんの背番号はサポーターナンバーと呼ばれる12番です。つまりこんな感じになります。
const name = (dog.uniform_number === 2) ? 'Jefy' : (dog.uniform_number === 9) ? 'Unity' : 'Mina-chang';どうですか? 見づらくてハゲそうですよね。
見やすくするテクニックとして、記号の前で改行するというのがあります。
外人が書くコードでよく見かけますよね。const name = (dog.uniform_number === 2) ? 'Jefy' : (dog.uniform_number === 9) ? 'Unity' : 'Mina-chang';ただ、この入れ子の三項演算子、PHPだけなぜか動作が違います。あいつはなぜか 右から評価 するので、こんな感じに書かないと思ったとおりになりません。
$name = (dog.uniform_number === 2) ? 'Jefy' : ( (dog.uniform_number === 9) ? 'Unity' : 'Mina-chang');不毛感がすごい……
おわりに
三項演算子の特殊な書き方として、エルビス演算子とかnull合体演算子の話は次回書きます。
それではお元気で! Enjoy, Summer!!
- 投稿日:2020-08-11T12:34:08+09:00
【Rails5】gem gonの使い方 ~RailsからJSに変数を渡す方法~
実装した機能
開発環境
ruby > 2.6.5 rails > 5.2.4.2 gon > 6.3.2今回はシンプルに、RailsのControllerで定義した変数をJSに渡してhtmlに表示します。
Railsに標準搭載されているhoge.js.erb
形式やcoffeescript
で書いたほうがスムーズだったりしますが、うまく表示できないときの回避策としても使えるGemかなと思ったのでご紹介します。※注意
Gemの仕様上、htmlソース内に変数自体が表示されるので、セキュリティ的に隠す必要があるものには使わないほうが良いです。導入方法
まずは、Gemfileに追加
Gemfilegem 'gon'そして、インストール
Terminal$ bundle installRailsのView内にコードを記述
今回は全ページで使う想定ですが、必要なviewに記述されていればOKです。
JSを読み込むのと同じく、読み込みのタイミングによって挙動が異なるので読み込み順には注意しましょう。application.html.erb・・・ <%= include_gon %> <%= javascript_include_tag "application" %> ・・・ </body>これで準備完了です。
変数を渡す
RailsのControllerでGon用の変数を定義します。
といっても、変数の頭にgon.
をつけるだけです。Users.erbdef show @user = User.find(1) gon.username = @user.name #これをJSに渡します endJSに変数を渡してhtmlに表示します。
index.html.erb・・・ <p id="name"></p> ・・・application.js・・・ let name = gon.username $('#name').html(name); ・・・このように表示されればOKです。
index.html・・・ <p id="name">こうへい</p> ・・・html内の表示について
冒頭で「html内に変数が見えちゃう」という話をしましたが
実際にはこのように読み込まれています。
良くも悪くもわかりやすい構造になっていますね笑index.html・・・ <script> //<![CDATA[ window.gon={};gon.username="こうへい"; //]]> </script> </body> ・・・その他機能
単純に変数を渡す以外にも
- 配列、ハッシュを渡す
- 変数をすべて読み込む
などの使い方もできるようです。
むしろ、Gemの役割としてはこっちのほうが大きいのかもしれませんね。
詳しくは公式ページをご確認ください。
- 投稿日:2020-08-11T12:25:28+09:00
react-hook-formの基本的な使い方
Reactで入力フォームを開発する際に便利なライブラリであるreact-hook-formの基本的な使い方についてまとめておきます。
公式ドキュメントが丁寧に用意されていますので、網羅的に機能を知りたい方はそちらをご覧ください。
実装手順
基本
以下のようなフォームに値を入力させて、submitする想定で改修を加えていきます。
import React from 'react'; function App() { return ( <form> <input name="title" /> <textarea name="content" /> </form> ); } export default App;ライブラリのインストール
ライブラリをインストールします。
npm install react-hook-form or yarn add react-hook-form
refの登録
次にライブラリが各フィールドを参照できるよう、registerメソッドをそれぞれのrefに追加します。
TypeScriptの場合は予め、useFormに対して各フィールドを持った型を渡しておきましょう。import React from 'react'; import { useForm } from 'react-hook-form'; type Post = { title: string; content: string; }; function App() { const { register } = useForm<Post>(); return ( <form> <input name="title" ref={register({ required: true, maxLength: 30 })} /> <textarea name="content" ref={register} /> </form> ); } export default App;react-hook-formには基本的なバリデーションも用意されており、このregisterを追加する際に設定することが可能です。
利用できるバリデーションについては以下を参照ください。
https://react-hook-form.com/get-started/#Applyvalidation
バリデーションエラーについて
設定したバリデーションでエラーが発生した場合はreact-hook-formが用意しているerrorオブジェクトを利用してハンドリングすることができます。
import React from 'react'; import { useForm } from 'react-hook-form'; type Post = { title: string; content: string; }; function App() { const { register, errors } = useForm<Post>(); return ( <form> <input name="title" ref={register({ required: true, maxLength: 30 })} /> {errors.title && <span>タイトルは必須です。</span>} <textarea name="content" ref={register} /> </form> ); } export default App;submit
そして、formタグに対して、コールバックを渡したhandleSubmitを設定します。
import React from 'react'; import { useForm } from 'react-hook-form'; type Post = { title: string; content: string; }; function App() { const { register, handleSubmit, errors } = useForm<Post>(); const onSubmit = (data: Post) => { console.log(data); }; return ( <form onSubmit={handleSubmit(onSubmit)}> <input name="title" ref={register({ required: true, maxLength: 30 })} /> {errors.title && <span>タイトルは必須です。</span>} <textarea name="content" ref={register} /> </form> ); } export default App;これでフォームが送信アクションが発生した際に、入力した値がonSubmitに渡されるようになります。
- 投稿日:2020-08-11T12:20:35+09:00
JavaScriptの制御構文(1)〜if命令とswitch命令〜
はじめに
こんにちは!
今日から時間をかけて、1度ある程度読んだJavaScriptのテキストの復習をしていこうと計画したので、ついでにこちらに投稿して備忘録として残してみようと思います!
まず、最初のテーマは、制御構文です!制御構文
制御構文って色々ありますよね!
- if命令
- switch命令
- while/do...while命令
- for命令
(for命令の中でも、配列なのかオブジェクトなのかによって使い分けもあります。)この中で、今回は、if命令とswitch命令について投稿します。
この2つの共通点としては、処理を「分岐する」ことです!if命令
処理を分岐するための命令には、if命令とswitch命令の2つがあります。
if (条件式) { trueの場合の処理 } else { falseの場合の処理 }let x = 75; if (x >= 60) { console.log('合格'); } else { console.log('不合格'); } /* 変数xの値は、60以上であるため、条件式はtrueになるので、consoleには、 「合格」が表示される。*/上記のような構文を用いて、if命令を実行し、処理の分岐を実現できます。
また、else以降のfalseの場合の処理を省略して、trueの場合のみ、処理を指定することも可能です。また、trueかfalseの2つに処理を分岐させるのではなく、複数分岐させることもできます。
if (条件式1) { 条件式1がtrueの場合の処理 } else if (条件式2) { 条件式2がtrueの場合の処理 } else { 全ての条件式がfalseの場合の処理 }let x = 77; if (x >= 90) { console.log('Aです。'); } else if (x >= 75) { console.log('Bです。'); } else if (x >= 60) { console.log('Cです。'); } else { console.log('不合格です。'); } /* 変数xの値は、77であり、75以上、90未満である。したがって、consoleには、 「Bです。」が表示される。*/else ifを必要な数だけ書き、分岐させます。
しかし、条件式を適切な順番で記述しないと、思わぬところで条件が一致し、処理が終了してしまうので、注意が必要です。switch命令
if命令は、条件によっては、同じような形式で条件が並ぶので、冗長な(長ったらしい)記述になってしまいます。。
例)
if (rank === 'A') { console.log('Aランクです。'); } else if (rank === 'B') { console.log('Bランクです。'); } else if (rank === 'C') { console.log('Cランクです。'); } else { console.log('ランク外です。'); }上記のような、コードが存在したとすします。条件式をそれぞれ見てみると、rank === ‘A’や、rank === ‘b’というように、「rank ===」という形式の条件が複数連なっています。
しかし、このような、長ったらしい、同じような条件の分岐命令も、switch命令を利用すれば、見やすく改善できます。switch (式) { case 値1: 式 = 値1の場合の処理 break; case 値2: 式 = 値2の場合の処理 break; case 値3: 式 = 値3の場合の処理 break; default: 式の値が全て一致しなかった場合の処理 break; }このswitch命令には1つ注意点があり、それぞれのcaseの処理の最後には、break命令の記述を忘れないことです。switch命令は、条件式に合致するcaseに移動するだけで、そのcaseの処理を終えたとしても、switch命令自体が終了するわけではないため、そのまま、合致したcase以降のcaseの処理も次々と実行してしまうのです。そうならないために、breakを忘れないように気をつけましょう。
意図的にbreak命令を記述しない時はある。(フォールスルー)
let rank = 'B'; switch (rank) { case 'A': case 'B': case 'C': console.log('合格'); break; case 'D': console.log('不合格'); break; default: console.log('ランク外'); break; } /* 変数rankの値は、Bであるため、consoleには、 「合格」が表示される。*/上記では、変数rankの値が、Aの場合、Bの場合、Cの場合では、全て同じ処理をしようとしています。この場合、case ‘A’とcase ‘B’、case ‘C’の間にbreak命令を挟んで、終わらせる必要はありません。
仮にbreak命令を記述してしまうと、そこでswitch命令自体が終了してしまうため、適切な処理が行われなくなってしまいます。(行うべき処理があるにも関わらず、case 'A'やcase 'B'の場合に、何も処理が行われない)
ただし、処理内容の詳細を記述している、case ‘C’やcase ‘D’、defaultの後には、break命令が必要になってきます。以上!!今日のif命令・switch命令のまとめでした!
理解不足による不備等ございましたら、コメントをお願い致します。参考文献
山田祥寛著『[改訂新版]JavaScript本格入門 ~モダンスタイルによる基礎から現場での応用まで』(2018)
※第1刷は、2016年。
- 投稿日:2020-08-11T10:41:15+09:00
JavaScriptで全角のひらがなカタカナ小文字を対応する大文字に変換する
例えば「ぁ」は「あ」、「っ」は「つ」、「ァ」なら「ア」みたいに、小文字を大文字に変換する。
Unicode上でぁはU+3041であはU+3042、他の文字も同様に小文字が来て、次に大文字が来るといったように並んでいるので、小文字のUnicode(16進数値)に1足した値が大文字になる。unescape("%u" + (parseInt(escape("ァ").substr(2,4), 16) + 1).toString(16))無理やりやればこれでできたけど釈然としない。。。
- 投稿日:2020-08-11T05:48:41+09:00
jsでHTML要素を追加する方法はこれを見てくれ。
欲しいのはこれだろう??
jqueryは使わない。
insertAdjacentHTMLしか載せない。
けどそれでいいだろ?① <div id="BASE"> ② <p>ぺぽー</p> ③ </div> ④① ここには「beforebegin」
var BASE = document.getElementById('BASE'); BASE.insertAdjacentHTML('beforebegin','<p>①に入るよん</p>');② ここには「afterbegin」
var BASE = document.getElementById('BASE'); BASE.insertAdjacentHTML('afterbegin','<p>②に入るよん</p>');③ ここには「beforeend」
var BASE = document.getElementById('BASE'); BASE.insertAdjacentHTML('beforeend','<p>③に入るよん</p>');④ ここには「afterend」
var BASE = document.getElementById('BASE'); BASE.insertAdjacentHTML('afterend','<p>④に入るよん</p>');以上。
一瞬でわかるやつが欲しかった、、
「js 要素 追加」でググって3秒で答えが欲しかった。
ありがとう、たくさんの記事がヒットしてどれも丁寧に書かれてました。
が、丁寧すぎた!内容量が多くて欲しい情報を探すのに時間がかかる。
実際、コーダーにはこれくらいで十分だ。
- 投稿日:2020-08-11T05:48:41+09:00
【一番速い】jsでHTML要素を追加する方法はこれを見てくれ。
欲しいのはこれだろう??
jqueryは使わない。
insertAdjacentHTMLしか載せない。
けどそれでいいだろ?① <div id="BASE"> ② <p>ぺぽー</p> ③ </div> ④① ここには「beforebegin」
var BASE = document.getElementById('BASE'); BASE.insertAdjacentHTML('beforebegin','<p>①に入るよん</p>');② ここには「afterbegin」
var BASE = document.getElementById('BASE'); BASE.insertAdjacentHTML('afterbegin','<p>②に入るよん</p>');③ ここには「beforeend」
var BASE = document.getElementById('BASE'); BASE.insertAdjacentHTML('beforeend','<p>③に入るよん</p>');④ ここには「afterend」
var BASE = document.getElementById('BASE'); BASE.insertAdjacentHTML('afterend','<p>④に入るよん</p>');以上。
一瞬でわかるやつが欲しかった、、
「js 要素 追加」でググって3秒で答えが欲しかった。
ありがとう、たくさんの記事がヒットしてどれも丁寧に書かれてました。
が、丁寧すぎた!内容量が多くて欲しい情報を探すのに時間がかかる。
実際、コーダーにはこれくらいで十分だろう?
- 投稿日:2020-08-11T05:31:34+09:00
二度と忘れるな。swiperで見切れさせる方法はこれだって言ってんだろ!!
俺は、バカなのか...
swiperでのカルーセル案件、今までに何度もやってきただろ...
なんで見切れさせるのに毎回苦戦しているんだよ...!二度とこいつに時間をかけなくてもいいように、ここに私の奮闘記を残します。
やりたいのはこういうカルーセル。
端っこのコンテンツがちょっと見切れちゃってるようなのを実装したい時のお話です。slidesPerViewは小数が使えるんだ!!
つまりこう。
var myswiper = new Swiper ('.swiper-container', { centeredSlides: true, slidesPerView: 2.2, spaceBetween: 16, pagination: { el: '.swiper-pagination', clickable: true, }, navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev', }, });この小数が使えるってことと、
「centeredSlides: true」で中央寄せにしてあげるってことがわかってれば一瞬。
なんてことはない。なんだこれはぁぁっぁあ!
中央寄せじゃなくなったとき(偶数個見せたいとき)。
これが微妙にめんどくさかった。こうしなさい。
var myswiper = new Swiper ('.swiper-container', { slidesPerView: 2.2, spaceBetween: 16, pagination: { el: '.swiper-pagination', clickable: true, }, navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev', }, });js側ではやることはあまりない。
さっきのと比べると中央寄せの「centeredSlides: true」を消したくらい。大事なのはcssで、「.swiper-container」に対してpadding-leftを当ててあげること。
.swiper-container { padding-left: 125px; }みたいに。
ちゃんとそれっぽい位置になるように微調整が必要だったり、
SPとかだとpx→vwにして、、とか必要になるけど、
やり方を検討するとこはもう終わってるから簡単だよね☆
- 投稿日:2020-08-11T05:30:55+09:00
swiperの雛形にはこれを使え!!
※公式のフォーマットとは異なります!
※使えなくなるオプションがあるかもしれません!完全に個人用です。
よくあれこれどうやったっけと忘れて、その度時間けかちゃってるのでメモ残しておきます。これを、使え!!
<div class="mycontainer"> <div class="swiper-container"> <div class="swiper-wrapper"> <div class="item swiper-slide">1</div> <div class="item swiper-slide">2</div> <div class="item swiper-slide">3</div> <div class="item swiper-slide">4</div> <div class="item swiper-slide">5</div> </div> </div> <!-- ページネーション --> <div class="swiper-pagination"></div> <!-- ナビゲーションボタン --> <div class="swiper-button-prev"></div> <div class="swiper-button-next"></div> </div>公式と違うところは、
マルポチやボタンが「.swiper-container」の外に出てることです。ここから下では、その理由とか経緯を説明していきます。
なんでそんなことを
swiperで次へ/戻るボタンや、マルポチを公式通りの方法でhtmlを書くと、
<div class="swiper-container"> <div class="swiper-wrapper"> <div class="item swiper-slide">1</div> <div class="item swiper-slide">2</div> <div class="item swiper-slide">3</div> <div class="item swiper-slide">4</div> <div class="item swiper-slide">5</div> </div> <!-- マルポチ --> <div class="swiper-pagination"></div> <!-- 次へ/戻るボタン --> <div class="swiper-button-prev"></div> <div class="swiper-button-next"></div> </div>ですね。
緑で囲った部分が赤枠のカルーセルエリアから飛び出していることがお分かりいただけるかと思います。
これ、公式のフォーマット通りの形で実現しようとするとかなりめんどくさい。なぜか。
理由はswiperの一番外側のクラスにあたる「.swiper-container」に対して公式cssでoverflow: hiddenが当たってしまっているからです。
なので素直にフォーマットに従いつつcssでマルポチなんかのbottomを下げたとしてもこうなっちゃうわけです。
いやん!だから俺は外に出した
cssで公式のoverflow:hiddenを上書きして、、
とかもありかもしれませんが一番外に出しちゃうのが楽かなという結論に至りました。つまりこうです。
<div class="mycontainer"> <div class="swiper-container"> <div class="swiper-wrapper"> <div class="item swiper-slide">1</div> <div class="item swiper-slide">2</div> <div class="item swiper-slide">3</div> <div class="item swiper-slide">4</div> <div class="item swiper-slide">5</div> </div> </div> <!-- ページネーション --> <div class="swiper-pagination"></div> <!-- ナビゲーションボタン --> <div class="swiper-button-prev"></div> <div class="swiper-button-next"></div> </div>これで公式cssのoverflow:hiddenから解き放たれました。
代償として、マルポチのスタイルが死んでしまうようですが...
ただ、案件でswiper公式のスタイルをそのまま使うことなんてないでしょう。
どうせ何かしらで上書きするのだからこんなの屁でもありません。一応、それっぽくする修正css置いてはおきますが。
.swiper-pagination { left: 50%; bottom: -40px !important; transform: translateX(-50%); } .swiper-pagination-bullet { margin: 0 4px; }完璧ですね☆
※使用したswiperのバージョンは5.4.5でした。
- 投稿日:2020-08-11T05:18:27+09:00
addClassで追加したクラスのstyleが反映されない件についての質問
jQueryのaddClass,removeClassを利用して、クリックしたらプロフィールが右からスライドしてくるアニメーションを作っています。
ハンバーガーメニューをクリックした際にtranslateX(100vw)が0%となってほしいのですが、デベロッパーツールで確認したところcssが上書きされていないようです。(クラスの付与は行われています)
!importantを使用しても同じ結果でした。よろしくお願い致します。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="stylesheet.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css" integrity="sha256-UzFD2WYH2U1dQpKDjjZK72VtPeWP50NoJjd26rnAdUI=" crossorigin="anonymous" /> <title></title> </head> <body> <script src="https://code.jquery.com/jquery-3.3.1.js"></script> <script> $(document).ready(function() { $('.menu').on('click', function() { if ($('.profile').hasClass('.is-active')) { $('.profile').removeClass('.is-active'); } else { $('.profile').addClass('.is-active'); } }); }); </script> <header> <div class="menu"> <i class="fas fa-bars"></i> </div> <div class="profile"> <p>texttexttexttext</p> <p>texttexttexttext</p> <div class="mail"> <p><i class="far fa-envelope"></i></p> </div> </div> </header> </body> </html>.menu { display: block; cursor: pointer; } /* アニメーション前の状態 */ .profile { transform: translateX(100vw); transition: all 0.3s linear; z-index: 2; top: 0; right: 0; } /* アニメーション後の状態 */ .profile.is-active { transform: translateX(0%); }
- 投稿日:2020-08-11T02:51:33+09:00
Webの表示速度を意識する1(idle速度の改善)
今読んでいる本。
気になったところをまとめていきたいと思い書きます。その1。
ーーーーーーーーーーーーーーーーーーー
いくつかのポートフォリオサイトを検証ツールでみてみたところ、あるサイトだけ数値上「idle」のところが非常に時間がかかっておりました。
idleとは
responseやanimation、Loadが終わった後に、ユーザーのアクションを待っている状態
のことらしい。では、「ユーザーのリアクションを待つ」状態がなぜ特定のサイトだけ長くなるのか?というと
最近のブラウザのレンダリングエンジンは、jsの処理とユーザーからのアクションを同じメインスレッドで実行しているため、
webページが何も処理を行っていないように見えても、裏側でjsが実行されているとユーザーからの入力が遅延される。。。とのこと。(前述の本より)
それを踏まえてサイトを見てみると、確かに、最初にJqueryのsettimeoutで「アニメーションでボワっ」と文字を出して、その後に操作を受け付けるようにしています。
このタイミングが少し長いのが原因かも、、?と思い半分にしてみたところやっぱりここが原因で、速度が2200ミリ秒弱短くなりました。例えば逆に5倍くらいの長さにすると、もちろんidolの値はグッと伸びました。し、体感ももちろん長く感じました
ーーーーーーーーーーーーーーーーーーーー
じゃあもっと短くしてもいいんじゃない?
確かにそれも一つの選択肢ですが、
・サイト訪問時に間を持たせた動きをつけて目をひきたい
・そもそもどれくらいの長さが妥当なのかは主観という考え方や、そもそもコンテンツの読み込みデータ量が多くて、
最初にアニメーションをわざと表示させてる間に読み込ませている、、、といったケースもあると思います。(ローディングアニメーションなど。読み込み中で何も表示されないより個人的には良いと思います。)そんな感じで必ずしも一瞬で終わるアニメーションが良いとも限らないんじゃないかなと思ったりしています。
そして速度を遅いと感じるかどうかは主観であるところが大きいので、
感覚ではなく、数値で見ようというのが今回使った検証ツールのPerformanceでした。
- 投稿日:2020-08-11T01:34:04+09:00
$('input[value="aaa"]') は「"aaa"と入力されたinput要素」ではない
かなり驚いたので初めて記事を書きます。
だめな例
"aaa"と入力されたinput要素のvalue属性を表示するプログラムのつもり。(つまり"aaa"が返ってくるはず)
HTML<input type="text" name="phrase">JavaScriptconst check = () =>{ const target = $('input[value="aaa"]'); console.log(target.val()); }
"aaa"と入力して、consoleでcheck();
を呼び出すと当然"aaa"...ではなくundefined
原因
どうやらこちらによると、
If you are changing value dynamically it wouldn't get selected by attribute selector.
この属性セレクタはユーザーによって動的に変更された値に対しては使えないそうです。1
Attribute selector will not check the dom node's value property it only targets the element's attribute
この属性セレクタは、その要素の属性のみを対象とし、ノードのvalueプロパティはチェックされません。1
解決策
filterを使いましょう。2
HTML<input type="text" name="phrase">JavaScriptconst check = () =>{ const target = $('input').filter(function() { return this.value == "aaa" }); console.log(target.val()); }
"aaa"と入力して、consoleでcheck();
を呼び出すと..."aaa"!!参考
javascript - Why Jquery selector by 'value' not work in case of dynamic change - Stack Overflow