- 投稿日:2020-10-28T23:26:03+09:00
JavaScript スタイルシートの操作
前回の ノードを追加・置換・削除
においての、おまけの続きスタイルシートの操作
インラインスタイルにアクセス styleプロパティ
HTMLのStyle属性にアクセスする方法。最も簡単にできるが、構造と装飾を分離するために、使いどころは考えなければいけない。
elem.style.prop = valueelem:要素オブジェクト prop:スタイルプロパティ value:設定値
例elem.addEventListener('mouseover',function(){ this.style.backgroundColor = 'Red'; },false);これは、<div style="background-color:red;">にしたと言うこと。
※スタイルプロパティはCSSと同じ書き方ではなく、「-」を取り除き、2文字目を大文字にする。(キャメルケース)
background-color → backgroundColor主なスタイルプロパティ(多すぎるため少数の例)
プロパティ 概要 border 枠線全体 borderColor 枠線全体の色 backgroundColor 背景色 textAlign 行揃え などなど
外部スタイルシートを適用する classNameプロパティ
外部スタイルシートに定義されたスタイルにアクセスするにはclassNameプロパティを使う。
elem.className = clazzelem:要素オブジェクト clazz:スタイルクラス
例
.highlight { background-color: red; }elem.addEventListener('mouseover',function(){ this.className = 'highlight'; },false);cssで定義された.highlightをJSファイルで読み込んで、スタイルを変更している。
これにて ー完ー
- 投稿日:2020-10-28T21:24:14+09:00
Gatsbyのチュートリアルを試してみた style編
はじめに
前回に引き続きgatsbyのチュートリアルを試してみた話を書こうと思います
今回はスタイリングについてです
・・・netlifyへのデプロイは特に問題も無くスイスイできたので記事にするのはやめました環境
MacBook Pro 2018
macOS Mojave 10.14.5
node.js v12.18.3
Gatsby 2.12.95
react 16.13.1目次
1.グローバルスタイル
2.コンポーネントスコープグローバルスタイル
gatsbyでcssを全体に適用する場合は任意のディレクトリにcssファイルを配置し
プロジェクトのルートディレクトリにgatsby-browser.jsを作成しますgatsby-browser.js src └── styles └── global.cssこんな感じ
そしてgatsby-browser.jsのなかでcssをインポートしますgatsby-browser.jsimport "./src/styles/global.css"これだけで各コンポーネントへcssが適用されます
コンポーネントスコープ
スタイルをコンポーネント単位で適用する場合はcssモジュールを使用します
src ├── components │ ├── container.js │ └── container.module.css ├── pages │ ├── about-css-module.jsこの中にあるxxx.module.cssのファイルがcssモジュールとなり、
各コンポーネントからインポートして使用しますcontainer.module.css.container { margin: 3rem auto; max-width: 600px; }container.jsimport React from "react" import conainerStyles from "./container.module.css" export default function Container({ children }) { return <div className={conainerStyles.container}>{children}</div> }about-css-module.jsimport React from "react" import Container from "../components/container" export default function About(){ return( <Container> <h1>Hello Style</h1> </Container> ) }cssモジュールを使用した場合自動でユニークなクラス名を生成してくれるため
衝突を気にせず使用することができるらしいですまたcssコンポーネント以外にCSS-in-JSが使えるようです
今回はEmotionを試してみますまず下記でgatsby用のemotionモジュールをインストールします
$ npm install gatsby-plugin-emotion @emotion/core @emotion/styledそしてプロジェクトのルートディレクトリにあるgatsby-config.jsを編集し下記を追加します
gatsby-config.jsmodule.exports = { plugins: [`gatsby-plugin-emotion`], }複数のプラグインを使用している場合はこんな感じ
gatsby-config.jsmodule.exports = { plugins: [ { resolve: `gatsby-plugin-typography`, options: { pathToConfigModule: `src/utils/typography`, }, }, `gatsby-plugin-emotion`, ], }これでEmotionを使用する準備はできたのでJSXにこんな感じでスタイルを記載していきます
index.jsimport React from "react" import Layout from "../components/layout" import styled from "@emotion/styled" /** @jsx jsx */ import { css, jsx } from '@emotion/core' const style = css` color: red; ` const HeaderStyle = styled.h1` color: blue; &:hover { color: red; } ` export default function Home() { return ( <Layout> <HeaderStyle>test a </HeaderStyle> <h1 css={style}>Hi! I'm building a fake Gatsby site as part of a tutorial!</h1> <p> What do I like to do? Lots of course but definitely enjoy building websites. </p> </Layout> ) }emotion/coreを使用するとcssを変数などに格納することができます
そして使用したい部分で展開すればcssを適用することができますemotion/styledを使用するとスタイルを持ったコンポーネントを作成できます
上の例だとstyled.h1でh1タグに相当するコンポーネントを作成し
その中にスタイルを記載していますここでちょっとハマったのがコンポーネントの名前をheaderStyleのような
キャメルケースにしてしまった場合は組み込みコンポーネントと解釈されて
自分で定義したコンポーネントが使用されない状態となります
Reactの基本なのに一瞬忘れててスタイルが反映されず焦りましたまたstyledの方は下記のようにプロパティも受け取ることができます
index.jsconst HeaderStyle = styled.h1` color: blue; background-color: ${ props => props.dark ? 'black' : 'white'}; &:hover { color: red; } ` export default function Home() { return ( <Layout> <HeaderStyle dark>test a </HeaderStyle>書いていて思ったのはvscodeのintellisenseが効かないのでプラグインを探さないと
ちょっと効率が悪い気がしました今回のスタイルについてはここまでにします
まだまだチュートリアルは続くので引き続き進めていこうと思います
- 投稿日:2020-10-28T19:16:43+09:00
【Rails】星★の評価を実装する(入力、保存、表示)
はじめに
開発しているメモアプリにランク付けをしていきたいと思います。
今回は三段階で評価していこうと思います。
それに伴い、星★による評価の入力・保存・表示について学習していきます。参考
マイグレーションファイルにカラムを追加
notesテーブル(メモテーブル)のrateカラム(型:float)に評価値を保存する
すでに、テーブルとカラムは作成されていることを前提に進めます。カラムは、半分の星による評価を行う場合、「0.5」や「1.5」という値を保存することになるため、float型にしておく必要があります。
もし、integerやstringの型として作成してしまっていた場合は、データベースによって変更方法は異なりますが、型の変更が必要になります。下記の記事でこの型の変更の沼にハマってしまった時の解決策を書いています。
class CreateNotes < ActiveRecord::Migration[5.2] def change create_table :notes do |t| t.text :title t.integer :user_id t.integer :category_id t.text :explanation t.float :rate t.timestamps end end endrails db:migrate:resetjavascriptの読み込み、★画像の読み込み
上記参考リンクを確認しながら、javascriptの読み込みと星の画像の読み込みを行います。
やり方(2種類)
①jQuery Ratyを使用するには、https://github.com/wbotelhos/raty からjquery.raty.jsをダウンロードする。
ダウンロードしたスクリプトをウェブサイトの任意の場所に配置する。
app/javascriptsのフォルダの中にダウンロードしたjquery.raty.jsファイルを配置する
②jQuery Ratyを使用するHTMLの中でJavaScriptを読み込む。RatyはjQueryのプラグインなので、jQueryのスクリプトも必要である。
<script src="/js/jquery.min.js"></script> <script src="/js/jquery.raty.js"></script>今回は①のやり方で実装していきます。
*ここでJavaScriptも定義していきます。
javascripts/application.js
ファイルに追加する//= require rails-ujs //= require activestorage //= require turbolinks //= require jquery #追加 //= require jquery_ujs #追加 //= require_tree .Gemファイル追加
gem 'jquery-rails'budle installモデルの記述(紐付け)
note.rbclass Note < ApplicationRecord # ユーザーとの紐付け belongs_to :user,optional: true # バリデーション validates :title, presence: true validates :explanation, presence: true # カテゴリーと紐付け belongs_to :category validates :rate, presence: true validates :rate, numericality: { # only_integer: true, less_than_or_equal_to: 3, greater_than_or_equal_to: 1, } # numericality=空を許可する numericalityは、デフォルトでは小数も許容してしまいます。rateカラムでは整数のみ許可したいので、 only_integerを。 例 validates :param3, :numericality => { :less_than_or_equal_to => 3} # 数字が3以下であるか validates :param3, :numericality => { :greater_than_or_equal_to => 1 } # 数字が1以上であるか endコントローラーの記述
notes_controller.rbclass NotesController < ApplicationController before_action :authenticate_user! def new @note = Note.new end def create # @note = Note.new(note_params) @note = current_user.notes.build(note_params) @note.save redirect_to notes_path end def index # is_validがマッチするレコードを全て取得 @categorys = Category.where(is_valid: true) @q = Note.all.ransack(params[:q]) @notes = @q.result(distinct: true) end def show @note = Note.find(params[:id]) end def edit @note = Note.find(params[:id]) end def update @note = Note.find(params[:id]) @note.update(note_params) redirect_to note_path end def destroy @note = Note.find(params[:id]) @note.destroy redirect_to notes_path end def search @categorys = Category.where(is_valid: true) @category = Category.find(params[:id]) @q = @category.notes.all.ransack(params[:q]) @notes = @q.result(distinct: true).page(params[:page]) @title = @category.name render 'notes/index' end private def note_params params.require(:note).permit(:title, :category_id, :explanation,:user_id,:rate) end endここでは
note_params
メソッドにrate
カラムを追加します。メモ登録フォームを作る
_form.html.erb<%= form_with model:note, local: true do |f| %> <div class='form-group'> <%= f.label :タイトル %> <%= f.text_field :title, class: 'form-control', id: 'note_title' %> </div> <div class='form-group'> <%= f.label :カテゴリー %> <%= f.collection_select :category_id, Category.all, :id, :name %> </div> <!-- 評価 --> <div class="form-group row" id="star"> <%= f.label :rate,'重要度 ', class:'col-md-1 col-form-label' %> <%= f.hidden_field :rate, id: :review_star %> </div> <div class='form-group'> <div id='editor'> <%= f.label :内容 %> <%= f.text_area :explanation, rows: 10, class: 'form-control', id: 'note_explanation', "v-model" => "input", name: "note[explanation]" %> <h2><i class="fas fa-eye"></i> Preview</h2> <div id="preview-field" v-html='input | marked'> </div> <div ></div> </div> <%= f.submit '登録', class: 'btn btn-success' %> </div> <% end %> <!-- リアルタイムプレビュー --> <script type="text/javascript"> window.onload = function() { new Vue({ el: '#editor', data: { input: '<%== j @note.explanation %>', }, filters: { marked: marked, }, }); }; <!-- 評価 --> $('#star').raty({ size : 36, starOff: '<%= asset_path('star-off.png') %>', starOn : '<%= asset_path('star-on.png') %>', starHalf: '<%= asset_path('star-half.png') %>', scoreName: 'note[rate]', half: true, }); </script>ポイントは下記
# ★の半分の入力ができるようにするため half: true,星による評価の表示
メモ一覧にて、★を表示したい。
上記の「★の入力、保存」と異なる点は、1.入力はせず表示する 、 2.繰り返し処理をするということになります_notes_index.html.erb<div class='row'> <table class='table'> <thead> <tr> <th>タイトル</th> <th>カテゴリー</th> <th>重要度</th> </tr> </thead> <tbody> <% @notes.each do |note| %> <% if user_signed_in? && current_user.id == note.user_id %> <tr> <td> <%= link_to note_path(note) do %> <%= note.title %> <% end %> </td> <td><%= note.category.name %></td> <!-- 評価 --> <td> <div id="star-rate-<%= note.id %>"></div> <script> // 繰り返し処理でもidをidを一意にできるようにnote_idを入れる $('#star-rate-<%= note.id %>').raty({ size: 36, starOff: '<%= asset_path('star-off.png') %>', starOn : '<%= asset_path('star-on.png') %>', starHalf: '<%= asset_path('star-half.png') %>', half: true, // 読み込みだけで入力できない readOnly: true, score: <%= note.rate %>, }); </script> </td> </tr> <% end %> <% end %> </tbody> </table> </div>ポイントは下記
# 繰り返し処理を実行してもidを一意に保てるようにreview.idを埋め込む <div id="note-rate-<%= note.id %>"></div> # 読み取り専用(入力できない) readOnly: true, # 星の入力値を読み込む score: <%= note.rate %>,これで完成!
最後に
説明が分かりづらかったりすると思いますがご了承ください。
また、間違っているところがあればご教授いただけると幸いです。
- 投稿日:2020-10-28T18:51:20+09:00
【Javascript】for文でハマったこと
■ for文の意味
下記構文は変数linesに対して加算をしていくfor文。
【例】// linesにはNumber型の値が入っている var length = lines[0]; var sumNumber = 0; for(let i = 0;i <= length; i++ ){ sumNumber += i; } console.log(sumNumber);■for文を分解
// 変数を宣言。sumNumber変数は加算した値を入れる箱。 // これが定義されていなくてハマった。 var length = lines[0]; var sumNumber = 0;for(let i=0;i<=length; i++ ) let i=0 // 変数iが宣言され、0が代入される。 i<=length // 作ったfor文の条件。length以下になったらfalseとなるためfor文が止まる。 i++ // 変数iに加算1を加算させていく。 // 書き換えると下記になる // i = i + 1// for文の外で定義したsumNumber変数に変数iを加算させる構文 sumNumber += i; // 書き換えると下記になる // sumNumber = sumNumber + i;ポイントはfor文は繰り返し処理を行っていることを理解すること
for文の中で定義された変数に代入や計算などの処理を行っても上書きされるだけ。
- 投稿日:2020-10-28T18:42:30+09:00
ノードを追加・置換・削除
前回の
属性値やテキストの取得
の続きあるHTMLのdivタグの文章を変えたいとなると
- 要素ノードを取得
- 直接取得
- 文書ツリー間の行き来
- イベントドリブンモデル
- 属性値やテキストを取得・設定
- 取得・設定
- フォームタグ
- ノードを追加・置換・削除
の、最後、ノードを追加・置換・削除の部分となります。
ノードを追加・置換・削除
HTMLの編集には「innerHTML」を使うことができたが、これはセキュリティなどの観点からも使うタイミングを考えたいといけなかった。今回のアプローチはノードを作成して任意のノードにくっ付けると言う操作についてまとめる。
これで、要素・属性とテキストを区別できるので、ユーザーからの入力によってスクリプトが混入する危険が回避されやすくなる。反面、コードは冗長になりやすいため、
シンプルなコンテンツの編集 → innerHTMLプロパティ
複雑なコンテンツの編集 → 今回のアプローチ
と使い分けると良い。ノードの作成
まず、必要なタイプのノードを作成する。イメージはパズルのピースを作る感じ。
作成するにはcreateXxxxxと言う、ノードに応じて様々な種類がある。
主なcreateXxxxxメソッド
メソッド 生成するノード createElement(要素名) 要素ノード createAttribute(属性名) 属性ノード createTextNode(テキスト) テキストノード ※プレーンなテキストなのでHTMLがあってもテキスト扱いとなる。 createCDATASection(テキスト) CDATAセクション createComment(テキスト) コメントノード createEntityReference(実体名) 実体参照ノード createProcessingInstruction(ターゲット名、データ) 処理命令ノード createDocumentFragment() ドキュメントの断片 ノード同士の組み立て・追加
ノード同士を組み立てる。ピースを一つの塊にする。
appendChild/insertBeforeメソッドを使う。
メソッド 概要 elem.appendChild(node) elem:要素オブジェクト の最後の子要素として、node:追加ノード を追加する elem.insertBefore(node, desNode) elem:要素オブジェクト の子要素 desNode:指定箇所のノード の後ろに node:追加ノード を追加する 属性ノードの追加
属性ノードの設定は、属性と同名のプロパティを設定するだけ。または、createAttributeメソッドを使用する。
let anchor = document.createElement('a'); // anchor.href = "http://~~~"; let href = document.createAttribute('href'); href.value = "http://~~~"; anchor.setAttributeNode(href);・DocumentFragment()について
appendChildで追加するたび、コンテンツが再描写される。そのため、forなどで繰り返して追加するとパフォーマンスが低下する。そのため、DocumentFragment()オブジェクト上でノードの塊を作成してから、一括で追加する。ノードの置換・削除
ノードの置換 replaceChildメソッド
elem.replaceChild(after, before)elem:要素オブジェクト after:置き換え後のノード before:置き換え対象のノード
ノードの削除 removeChildメソッド, removeAttributeメソッド
elem.removeChild(node) elem.removeAttribute(属性名)elem;属性オブジェクト node:削除対象のノード
これで、追加・置換・削除ができるようになった。
結構、一つの物を変更するだけでも手間がかかるんですね…
いい勉強になりました!→ 完
→ と思いきやおまけ スタイルシートの操作
- 投稿日:2020-10-28T18:04:43+09:00
【JS】sliceメソッドを使って指定した配列番号の要素を削除する方法
sliceメソッドを使って指定した配列番号の要素を削除する方法
sliceを活用することで、指定した番号の要素を削除することができる。
pythonのpopメソッドは指定した配列番号を削除できるが、JSでは配列の末尾から削除する仕様となってしまう。
pythonのpopのように指定した配列番号の要素を削除する機能は、jsのsliceメソッドを使うことで実現できる。
sliceメソッド
arr.slice(開始番号,終了番号)
┗ 開始番号から、終了番号の手前までの要素を抜き出す。
┗ 終了番号の要素は含まない。
┗ 終了番号がない場合は、配列の最後の用をまでを表示sliceは非破壊的なため、元の配列はそのまま。
元の配列も変更する場合は代入が必要。arrs=["aaa","bbb","ccc","ddd","eee"] arrs.slice(0, 2) //出力 //["aaa", "bbb"] //非破壊の確認 arrs //出力 //["aaa","bbb","ccc","ddd","eee"]
▼終了番号を指定しない場合arrs=["aaa","bbb","ccc","ddd","eee"] arrs.slice(2) //出力 //["ccc", "ddd", "eee"]配列番号2以降の要素が抽出される。
▼元の値を置き換える
元の値に代入すれば、破壊的な処理にすることができる。arrs=["aaa","bbb","ccc","ddd","eee"] arrs = arrs.slice(3) arrs //出力 //["ddd", "eee"]
スプレッド構造
最初に、配列の値を結合する際に使用するスプレッド構文を確認しておく。
スプレッド構文とは、配列の前に「...」をつけることで、配列のカッコ([ ])を外す方法。
配列同士を結合する場合に、それぞれにスプレッド構文を付けて、カッコ([ ])で囲むと結合できる。
スプレッド構文a = [1,2] b = [3,4] console.log(...a) //1 2 console.log([...a, ...b]) //[1,2,3,4]
ちなみに、スプレッド構文を使用しない場合は、望んだ形にならない。a = [1,2] b = [3,4] console.log(a+b) //1,23,4
sliceを使って指定した要素を削除する
考え方は、(1)指定した配列番号の前までの値と(2)指定した配列番号より後ろの値を取得し、それぞれを結合する。
arrs = [ "aaa", "bbb", "ccc", "ddd", "eee" ] //削除する配列番号 rmIndex = 2 //sliceを使った削除 arrs = [ ...arrs.slice(0,rmIndex), ...arrs.slice(rmIndex+1) ] console.log(arrs) //出力 //["aaa", "bbb", "ddd", "eee"]指定した番号arrs[2]を削除することができた。
削除する番号を動的に指定すれば、指定した要素を消すことができる。
- 投稿日:2020-10-28T17:42:01+09:00
【 Vue.js 】Async/awaitとaxiosによる非同期処理
methodsに記述したaxiosによるHTTP通信の完了を待ってから残りの処理をしたい必要があったのですが、少し詰まったので記録として残しておきます。
※Async/awaitやaxiosについての説明記事ではありませんのでご了承くださいmounted: { this.settingXxx(); }, methods: { fetchSample: async function(){ let ret = null // 非同期処理を記述 await axios.get('(url)', { .then((response) => { ret = response }) .catch((error) => { this.errorMsg = 'Error! Could not reach the API. ' + error console.log(this.errorMsg) }) return ret }, settingXxx: async function(){ // this.fetchSample()の実行が完了するまで待機 let result = await this.fetchSample() console.log(result) //待機後の残りの処理を記述 }, }fetchSample();によりPromiseオブジェクトが返され,
Promiseの状態が確定しその結果が返されるまで、JavaScriptを待機させます。参考にさせていただいた記事
- 投稿日:2020-10-28T17:42:01+09:00
【 Vue.js 】(簡易メモ) Async/awaitとaxiosによる非同期処理
methodsに記述したaxiosによるHTTP通信の完了を待ってから残りの処理をしたい必要があったのですが、少し詰まったので記録として残しておきます。
※Async/awaitやaxiosについての説明記事ではありませんのでご了承くださいmounted: { this.settingXxx(); }, methods: { fetchSample: async function(){ let ret = null // 非同期処理を記述 await axios.get('(url)', { .then((response) => { ret = response }) .catch((error) => { this.errorMsg = 'Error! Could not reach the API. ' + error console.log(this.errorMsg) }) return ret }, settingXxx: async function(){ // this.fetchSample()の実行が完了するまで待機 let result = await this.fetchSample() console.log(result) //待機後の残りの処理を記述 }, }fetchSample();によりPromiseオブジェクトが返され,
Promiseの状態が確定しその結果が返されるまで、JavaScriptを待機させます。参考にさせていただいた記事
- 投稿日:2020-10-28T17:32:14+09:00
JavaScriptでViewの中の表示を変えてみるなかで重要だと感じたメゾット(mouseover,mouseout編)
イベント
JavaScriptではみているViewの中の「何かが起きる」とそれに応じて動くというふうになっています。
この「何かが起きる」ということをイベントと言います!イベントが起きてjsの中で書いたコードが読み込まれて、色が変わったり、プルダウンが表示できたりします。イベントが発火する超代表的な例
名前 意味(~した時に発火する) load ページが全て読みこむ click 指定した要素にクリック mouseover 要素の上にマウスカーソルが乗った mouseout 要素の上から マウスカーソルは離れた addEventListener
こちらを使うと、jsのファイルでイベントが発生した時どうするのかを定義することができます。ちなみに本当に簡単なイベントならhtmlに直接イベント属性を書いてあげることもできます。ただしデメリットとしては、
htmlに直接書いてあげると、同じ要素に一つしか要素を入れることができないです。
(クリックしたらプルダウンを表示して、mouseoverしたら色を変えるとかみたいに2つの動作は❌)addEventListenerメソッドはこのように書きます。
card.js要素.addEventListener('イベント名', 関数)//イベントが発火したら実行する関数を定義するためのメソッドになってます。//ここからviewの表示を変える段階を簡単に記しながら覚えるべき語句を記していきたいと思います。
まずhtmlの欲しい要素にidを付与します。
そしてjs側でcard.jsconst pullDownUp = document.getElementById("lists")今回はlistsをhtmlから取得してきてpullDownUpに代入しています!
これだけだとコンソールには何も表示されないのでcard.jswindow.addEventListener('load', function(){ const pullDownUp = document.getElementById("lists") }この記述で、ページ全体が読み込まれた後にJavaScriptの処理が実行されるようにできます。
その後はイベント発火の為のmouseoverやmouseoutをaddEventListenerの中に記述することで、イベントを設定することができます。
その時都度都度でconsole.logで確認しておくといいかと思います!インラインスタイル
HTML要素の開始タグの中に直接CSSのソースコードを記述することでプロパティを指定できます!
追加と削除はそれぞれsetAttributeとremoveAttributeというメソッドで設定することができます。setAttribute
setAttributeは追加や値を変更します。
card.js要素.setAttribute(name, value)書き方はこんな感じです。例でいうとmouseoverした時にリストの色を変更させることができるみたいな感じです。
removeAttribute
書き方としてはsetAttributeとたいして変わりません。
こちらは反対に値を削除します。上記と合わせた例で言うとmouseoutした時にリストの色を削除(元の色に戻す)ことができるみたいな感じです。まとめ
ほかにもクリックした時など山ほど書きたいことはあるのですが、またそのこともかいてしまうととても長くなるので別の時にでもアウトプットさせていただきます。
- 投稿日:2020-10-28T17:01:16+09:00
属性値やテキストの取得
前回の
DOM操作系 まとめ
の続きあるHTMLのdivタグの文章を変えたいとなる時の
- 要素ノードを取得
- 直接取得
- 文書ツリー間の行き来
- イベントドリブンモデル
- 属性値やテキストを取得・設定
- 取得・設定
- フォームタグ
- ノードを追加・置換・削除
の2つ目、属性値やテキストを取得・設定の部分となります。
属性値やテキストを取得・設定
属性値の取得・設定
ほとんどはHTMLタグの属性値と同じ名前でアクセスできるが、一部違う場合がある。
let link = document.getElementsByTagName('a'); //<a>は1つとする let url = link.href; //取得 link.href = 'http://www.~~~~~'; // 設定違う場合のよく使うものでは、「class」でこれに対するDOMプロパティは「className」となります。
他にも、属性とプロパティの名前の違いを意識したく無い場合、getAttribute/setAttributeがある。
上を書き換えるとlet url = link.getAttribute('href'); link.setAttribute('href','http://www.~~~~~');となる。メリットは名前の相違を気にしないことと、(文字列として指定できるので)取得・設定する属性名を、スクリプトから動的に変更出来ることがあり、都度状況に合わせて使い分けると良い。
他にも、全ての要素を取得したい場合は、attributesがある。
これである要素ノードの全ての属性を取得でき、item(i)で番号を指定することで、ある要素だけを取り出すこともできる。テキストを取得・設定
テキストを取得・設定するにはどちらもinnerHTML/textContentと言うプロパティを使う。両者の違いは
例:<div>にテキスト<a href="~~~">テスト</a>を埋め込む場合
プロパティ 概要 innerHTML 設定の場合:HTMLを認識してくれ、テストに<a>が効いた状態になる。 取得の場合:HTMLも合わせて文字列として取得する。 textContent 設定の場合:HTML含む全てをテキストとして認識するので、そのまま文字として埋め込む。 取得の場合:HTMLタグの中にあるテキストのみ取得する では、どちらがいいのか?
一般的にはHTMLを埋め込む必要がないならば、textContentを使用する。これは、セキュリティの観点からも注意が必要である。悪意ある他社がインプットなどから入力でき直接タグに渡すなどする場合、innerHTMLだとHTMLを認識してしまい、悪意ある行動をとることができる。このことをクロスサイトスクリプティング(XSS)という脆弱性を持っている。もし使うとしても、直接HTMLを渡さないように注意する。
ただ、「入力値をもとにHTML文字列を組み立てて、ページに反映したい」などの場合、はcreateElement/createTextNodeメソッドを利用する。各フォーム要素にアクセスする
入力ボックス・選択ボックス
値を取得・変更するには、valueプロパティにアクセスするだけで良い。
チェックボックス・ラジオボックス
値を取得するには、valueプロパティにアクセスするだけではダメ。なぜなら、チェックされているいないに関わらずに値を返すため。チェックスされた値を取得するならば、 if(要素.checked){} のように、チェックされているかどうかを判別して、valueプロパティにアクセスする必要がある。また、初期値にチェックを入れておきたい場合は、同様にここの要素に条件式を当てはめ、 要素.checked = true としてチェックをいれる。
<form> <div> 好きな食べ物は?: <label><input type="checkbox" name="food" value="ラーメン" />ラーメン</label> <label><input type="checkbox" name="food" value="餃子" />餃子</label> <input id="btn" type="button value="送信" /> </div> </form>document.addEventListener('DOMContentLoaded',function(){ document.getElementById('btn').addEventListener('click',function(){ let result = []; let foods = document.getElementsByName('food'); for(let i = 0, len = foods.length; i < len; i++){ let food = foods.item(i); if(food.checked){ result.push(food.value); } } window.alert(result.tiString()); },false); },false);複数選択できるリストボックスの値の取得
取得・選択は入力ボックスなどと考え方は同じで、<select>要素配下の<option>要素群を取得、その後は、for , if(要素.selected)で条件を絞り込んで、要素.valueで値を取得・選択する。
注意はselectedとなっていること。アップロードされたファイルの情報の取得
アップロードされたファイルの情報を取得する。
手に入る情報として
プロパティ 概要 name ファイル名 type コンテンツタイプ size サイズ(バイト単位) lastModifiedDate 最終更新日 がある。アクセスするにはfilesプロパティを利用する。
<form> <label for="file">ファイル</label> <input id="file" name="file" type="file" multiple /> </form>window.addEventListener('DOMContentLoaded',function(){ document.getElementById('file').addEventListener('change',function(e){ let inputs = document.getElementById('file').files; for ( let i = 0, len = inputs.length; i < len ; i++){ let input = inputs[i]; console.log(input.name); console.log(input.type); } },true); });テキストファイルの内容を取得する場合は、FileReaderオブジェクトを使う。
・ファイル内容がテキストの場合<form> <label for="file">ファイル:</label> <input id="file" type="file" /> <form> <pre id="result"></pre>window.addEventListener('DOMContentLoaded',function(){ document.getElementById('file').addEventListener('change',function(e){ let input = document.getElementById('file').files[0]; let reader = new FileReader(); // ファイル読み込み成功したタイミングで実行 // まだこの時点では、読み込み時の挙動を定義しただけ reader.addEventListener('load',function(){ //File.Reader.resultプロパティで読み込んだテキストにアクセスできる document.getElementById('result').textContent = reader.result; },true); // ここでreadAsTextメソッドでファイルを読み込む。 // バイナリファイルはreadAsDataURLメソッドを使う。以下参照 reader.readAsText(input,'UTF-8'); }); });readAsTextメソッドについて
reader.readAsText( file [,charset])reader: FileReaderオブジェクト、 file: 読み込みファイル、 charset: 文字コード(デフォルトはUTF-8)
・バイナリファイルの場合
readAsTextメソッドの代わりに、readAsDataURL(file)メソッドを使う。これでData URL形式で取得が可能。URLに直接、画像・音声等のデータを埋め込む表現で、いちいちファイルとして保存する必要がなく、srcに入れることができる。以上で、属性値やテキストの内容を取得することができた。次回はこれをもとに、ノードを追加・置換・削除する
→ 次回:ノードを追加・置換・削除する
- 投稿日:2020-10-28T16:05:37+09:00
vue.jsを初めて学ぶ ③ [レンダリング]
前回へのリンク
vue.jsを初めて学ぶ ① [hello world]
vue.jsを初めて学ぶ ② [テンプレート構文]
目次
条件付きレンダリング
1. v-if
要素を消す
切り替えが高頻度だと、処理が重くなる
要素を削除しているため切り替えが頻繁ではない時は、v-if
2. v-show
cssベースで消す
初期表示が遅い
表示してからdisplay noneしているためとても頻繁に切り替える時は、v-show
リストレンダリング
1. v-forとは
- リストレンダリングを可能にする。
2. 配列から取り出す
index.html<div id = "app"> <ul> <li v-for="fruit in fruits"> {{fruit}} </li> </ul> </div>
- v-for の使い方
(引数) in 配列名
- {{ }} 二重括弧内に引数を入れる。
- index, value の2つの要素を持っていることをチェック。
index.jsnew Vue ({ el: '#app', data: { fruits: ['りんご','ばなな','チェリー'] } })
- 変数fruitsに代入。
配列形式で{value}を作成。3. オブジェクトから取り出す
index.html<div id = "app"> <ul> <li v-for="(value,key,index) in object"> {{index}}{{key}}{{value}} </li> </ul> </div>
- v-for の使い方
(引数) in オブジェクト名
- {{ }} 二重括弧内に引数を入れる。
- key, value, index の3つの要素を持っていることをチェック。
index.jsnew Vue ({ el: '#app', data: { object: { firstName: '太郎', lastName: '田中', age: 21 } } })
- 変数objectに代入。
オブジェクト形式で{key: value}を作成。4. タグを利用する
タグより、要素が残らないタグを使うとスッキリ。index.htmlタグ使用<div id = "app"> <ul> <template v-for="(value, index) in fruits"> <li> ({{index}}){{value}} </li> <hr> </template> </ul> </div>index.htmlタグ使用<div id = "app"> <ul> <div v-for="(value, index) in fruits"> <li> ({{index}}){{value}} </li> <hr> </div> </ul> </div>div タグより、スッキリして見えますね!
5. inとofの分類
ofは、javascriptのイテレーター構文
ofが使われていても間違ってはいない!
Vue.jsでは、基本的にinを使用する6. v-forレンダリング時に、必須のキー属性
v-forのリストレンダリング時、
必ずキー属性を使用キー属性とは、templateタグで使用できない
v-forは、
要素の移動を最小限に抑えるアルゴリズムを使用し可能な限り同じタイプの要素を再利用しようとする性質があるから。index.html<div id = "app"> <ul> <div v-for="fruit in fruits"> <p>{{fruit}}</p> <input type="text"> </div> </ul> <button v-on:click="remove">先頭削除</button> </div>index.jsnew Vue ({ el: '#app', data: { fruits: ['いちご','ばなな','キウィ'] }, methods: { remove: function() { this.fruits.shift() } } })
- 3つのテキストボックス内に入力し、先頭削除ボタンをクリック。
- 先頭が削除された時、テキストボックス内の文字列がずれてしまう。
- これはVue.jsが効率の良いアルゴリズムでレンダリングした結果。
- 要するに効率を優先する事で、ズレが起きてしまう
どうすれば良い??
7. レンダリング時、キー属性を指定する!
index.html<div id = "app"> <ul> <div v-for="fruit in fruits" key="fruit"> <p>{{fruit}}</p> <input type="text"> </div> </ul> <button v-on:click="remove">先頭削除</button> </div>index.jsnew Vue ({ el: '#app', data: { fruits: ['いちご','ばなな','キウィ'] }, methods: { remove: function() { this.fruits.shift() } } })keyとして、毎回レンダリングで代入される変数fruitを指定
keyとして、indexは指定できない。ズレが生じる
- 投稿日:2020-10-28T15:54:19+09:00
【javascript】classList を使って class を操る
対象者
- javascript で class を追加したり削除したりなんのしたい方
- そもそも getElementById で null が返ってきて困っている方
動的に class を追加したり削除したりしたい時、
「getElementById とか classList を使うのは何となく覚えているけど、どう書くんだっけ…」
ってことがあったので、パッと見つけられるように簡単に備忘録。javascript で class を追加・削除
例として、下記のようなHTMLがあって、
javascript で要素に class を 追加したり削除したりしたいとき、
classList を利用するとそれができる。HTML<div id="dog" class="chiwawa shiba">犬</div>classに追加する
javascriptdocument.getElementById("dog").classList.add("Pomeranian") // class="chiwawa shiba Pomeranian"
document.getElementById("dog")
→ id="dog" の要素を見つけてくれる。
classList.add("Pomeranian")
→ ここで、Pomeranian が class に追加される。classから削除する
javascriptdocument.getElementById("dog").classList.remove("chiwawa") // class="shiba Pomeranian"
classList.remove("chiwawa")
→ chiwawa が class から削除される。
先ほど追加した Pomeranian と、もともと class にあった shiba だけが残る。<便利> 指定したclass名があったら削除、なかったら追加
javascriptdocument.getElementById("dog").classList.toggle("Pomeranian") // class="shiba"
classList.toggle("Pomeranian")
→ class に Pomeranian があるため、削除される。javascriptdocument.getElementById("dog").classList.toggle("poodle") // class="shiba poodle"
classList.toggle("poodle")
→ class に poodle がないため、追加される。getElementById で null が返ってくる場合
console.log(document.getElementById("xxx"))
で、null
が返ってくる場合の対処法。解決方法
そもそも id があっているか確認する
例:HTML<div id="dog" class="chiwawa shiba">犬</div>例:javascriptdocument.getElementById("cat")
document.getElementById("cat")
で、id="cat"
を見つけ出せずnull
が返ってくるパターン。
document.getElementById("dog")
にしてあげるか、
HTML側をid="cat"
にして、id を合わせてあげることで解決できる。ロード順を見直す
HTML を先に読み込んでから、javascript を読み込むようにすることで解決できるパターン。
これに関して、2つ方法がある。①【JavaScript】getElementByIdでnullが返ってきたときに確認すること
HTML よりも先に javascript を読み込んでしまっていることで、
id を見つけ出せず、 null になってしまう。
そのため、HTML と javascript の読み込み順を入れ変えてあげれば解決。javascript側で、HTMLの読み込みが終わってから実行するようにする方法。
javascriptwindow.onload = function () { document.getElementById("dog").classList.add("Pomeranian"); // HTMLの読み込み後に実行されるため、id="dog"が見つけ出せる。 };参考
Element.classList
classList の使い方が詳しく載っている。【JavaScript】classListの使い方まとめ(add.remove.contains.toggle)
ボタンを押したら色が変わることを例に、実用的かつ分かりやすい。
- 投稿日:2020-10-28T15:46:46+09:00
DockerでNuxt.jsを起動するまで 改訂版
実施環境
・macOS Catalina バージョン 10.15.6
・Docker Desktop Community Version 2.3.0.5この記事ではDockerのインストール方法は記述していません。
各自インストールした状態で記事を読み進めてください。Dockerの準備
- 準備するにあたって
- node.js や nuxt のフレームワークのインストールは docker のコンテナ内でおこなう
- docker を利用する事で、開発環境を共有したいときの時間を短縮できる
アプリを作成したい、好みのディレクトリに移動します。
私はMyNuxtApp
としました。まず、アプリケーションのディレクトリを作成し、
Docker関連のファイルを作成します。terminal(任意のディレクトリへ移動) cd ~/Applications mkdir MyNuxtApp cd MyNuxtApp touch Dockerfile
Dockerfile
DockerfileFROM node:12.16.1 RUN mkdir -p /usr/src/app WORKDIR /usr/src/appDockerfile編集後は保存を忘れずにお願いします。
また、ターミナルで編集しても良いです。terminaltouch Dockerfile && \ echo 'FROM node:12.16.1' >> Dockerfile && \ echo 'RUN mkdir -p /usr/src/app' >> Dockerfile && \ echo 'WORKDIR /usr/src/app' >> DockerfileDockerfile の準備ができたら、初期のDocker イメージを作成します。
今回はnuxt-test
という名前のイメージを作成します。
これから開発するアプリケーションの名前を入れると良いでしょう。terminaldocker build -t nuxt-test .
Docker イメージが作成できたらイメージからコンテナを作成し、コンテナ内に入ります。
コンテナ名はctr-nuxt-test
としています。terminaldocker run -it --rm --name ctr-nuxt-test -v "$PWD":/usr/src/app nuxt-test /bin/bashコンテナ内で nuxt-app をインストールしましょう。
nuxtコミュニティが公開しているcreate-nuxt-app
を利用します。
私はyarn を使用し、nuxt-test-web
という名前でアプリケーションを作成します。create-nuxt-appのオプションについては、
他記事を参照ください。terminal# yarn create nuxt-app nuxt-test-web ... create-nuxt-app v3.2.0 ✨ Generating Nuxt.js project in nuxt-test-web ? Project name: nuxt-test-web ? Programming language: JavaScript ? Package manager: Yarn ? UI framework: Bootstrap Vue ? Nuxt.js modules: Axios ? Linting tools: ESLint, Prettier ? Testing framework: None ? Rendering mode: Single Page App ? Deployment target: Static (Static/JAMStack hosting) ? Development tools: jsconfig.json (Recommended for VS Code if you're not using typescript) ? Continuous integration: GitHub Actions (GitHub only) ? What is your GitHub username?: Your Github Username ? Version control system: Git yarn run v1.22.0 ... ? Successfully created project nuxt-test-web To get started: cd nuxt-test-web yarn dev To build & start for production: cd nuxt-test-web yarn build yarn start上記のコマンドでもアプリケーションをビルドできますが、せっかくDockerを使うので、
docker-compose up
で起動するようにします。terminal(インストールが終了したら一旦、コンテナから出ます) # exit exitこの段階で、
~/Applications/MyNuxtApp/nuxt-test-web
というディレクトリにアプリケーションができています。ディレクトリ構造をご確認ください。MyNuxtApp |___ Dockerfile |___ nuxt-test-web (ここにNuxt.js アプリケーションが入っている)
nuxt-test-web
内のアプリケーションを1つ階層をあげて、MyNuxtApp
内に移します(docker利用のため)。
-n
コマンドは、移動元と移動先で同一名ファイルがあった時に上書きをせずに両方残すことができます。terminal(隠しファイルを除く全ファイルを移動) mv -n ~/Applications/MyNuxtApp/nuxt-test-web/* ~/Applications/MyNuxtApp (隠しファイルを移動) mv -n ~/Applications/MyNuxtApp/nuxt-test-web/.[^\.]* ~/Applications/MyNuxtApp
移動元のディレクトリ(nuxt-test-web)が空になっているか確認します。
空でない場合でも、削除して問題なければディレクトリを削除します。terminalls -al ~/Applications/MyNuxtApp/nuxt-test-web total 16 drwxr-xr-x 3 ryota staff 96 10 28 15:05 . drwxr-xr-x 27 ryota staff 864 10 28 15:05 .. -rw-r--r--@ 1 ryota staff 6148 10 28 15:05 .DS_Store rm -r ~/Applications/MyNuxtApp/nuxt-test-web
ディレクトリの整理が終わったら、Docker周りの設定をします。
terminal(初期のイメージを削除) docker images REPOSITORY TAG IMAGE ID CREATED SIZE ... nuxt-test latest b02d332b3a85 5 weeks ago 916MB ... docker rmi nuxt-test touch docker-compose.yml (compose のcommand スクリプトを書く。実行権限を付与する必要があります。) touch compose_command.bash chmod +x compose_command.bashdocker-compose.ymlversion: '3.7' services: web: container_name: ctr-nuxt-test build: context: . image: img-nuxt-test volumes: - .:/usr/src/app ports: - '3000:3000' environment: PORT: 3000 HOST: 0.0.0.0 command: './compose_command.bash'compose_command.bashyarn install && \ yarn dev
これでいよいよ準備完了です!実行の確認をしましょう。
(初回) docker-compose up --build (2回目以降) docker-compose up立ち上がったかどうか、localhost:3000 で確認してください!
自分で指定したプロジェクト名が表示されていればdocker compose での起動が成功です!
- 投稿日:2020-10-28T15:09:15+09:00
ワイ「TypeScriptのエラーが読めない」
前回の記事の続きです。
TypeScriptが気に入ったワイ
ワイ「TypeScript、素敵やん」
ワイ「だって、ミスったコードを書くと・・・」ワイ「↑こんな感じで、コンパイラが教えてくれるから」
ワイ「だいぶバグ発生率を抑えられそうや〜ん」しかし
ワイ「おっ」
ワイ「またエラーメッセージが出たで」ワイ「ファッ!?」
ワイ「今度は英語やないか」
ワイ「しかもちょっと長い!」
ワイ「意味わからんで」
ワイ「Google翻訳してみよか」タイプの引数 '{名前:文字列; 年齢:文字列; } 'はタイプ' User 'のパラメータに割り当てることはできません。
プロパティ「age」のタイプには互換性がありません。
タイプ 'string'はタイプ 'number'に割り当てることができません。ワイ「いや翻訳しても分からん!」
助けを呼ぶ
ワイ「ハスケル子ちゃ〜ん!」
ハスケル子「なんですか」
ワイ「TypeScriptのエラーメッセージが読まれへんねん」
ハスケル子「(いや、それは知らん・・・)」
ハスケル子「(だって英語力の問題だから・・・)」
ハスケル子「とりあえず、何をしようとしたらエラーが出たんですか?」ワイ「ええとやな・・・」
type User = { name: string age: number }ワイ「↑こんな感じの
User
っていう型を定義したんや」ハスケル子「なるほど」
- ユーザーは、名前と年齢を持っている
ハスケル子「っていうことを表してるわけですね」
ハスケル子「いいじゃないですか」ワイ「ほんで、実際にユーザ型の変数
takashi
を作ったんや」const takashi = { name: 'たかし', age: '36' }ハスケル子「なるほど」
ワイ「そんで、次は関数を作って、実行しようとしたんや」
ワイ「引数としてUser
型の値を受け取る関数や」const makeProfileText = (user: User): string => { return `${user.name}(${user.age}歳)` }ワイ「↑こんな感じで
makeProfileText
って関数を作ったんや」
ワイ「そんでこの関数に、takashi
というユーザを渡して・・・」const profileText = makeProfileText(takashi)ワイ「↑こんな風に実行してやると・・・」
'たかし(37歳)'
ワイ「↑こんな感じの、プロフィール用のテキストを返してくれるっていう」
ワイ「そんな関数や」
ワイ「でも、ここでさっきの・・・」ワイ「↑訳わからんエラーが出てもうたんや・・・」
ワイ「takashi
いう変数が問題みたいや・・・」ハスケル子「たぶん、一行ずつ読んでいけば分かりますよ」
ハスケル子「英語にビビらず、一行ずつ読んでいきましょう」一行ずつ読んでいく
Argument of type '{ name: string; age: string; }' is not assignable to parameter of type 'User'.
ハスケル子「↑まずはここは・・・」
User
型の引数として
{ name: string; age: string; }
をぶち込むことはできまへん!ハスケル子「↑こんな意味です」
ワイ「なるほどな」
ワイ「{ name: string; age: string; }
ってのは何のこと?」ハスケル子「それは
takashi
という変数の型ですね」
ハスケル子「つまり」
User
型の引数としてtakashi
をぶち込むことはできまへん!ハスケル子「↑ってことです」
ワイ「なるほどな」
ハスケル子「そして次の行は・・・」
Types of property 'age' are incompatible.
age
というプロパティの型が合ってまへんねん!ハスケル子「↑ってことですね」
ハスケル子「そして最後の行は・・・」Type 'string' is not assignable to type 'number'.
文字列型の値は、数値型にはぶち込めまへん!
ハスケル子「↑こうですね」
ハスケル子「まとめると・・・」
User
型の引数としてtakashi
をぶち込むことはできまへん!
age
というプロパティの型が合ってまへんねん!
文字列型の値は、数値型にはぶち込めまへん!ワイ「なるほど」
ワイ「要約すると・・・」
takashi
のage
がstring
だから、アカン!ワイ「ってことか」
const takashi = { name: 'たかし', age: '36' }ワイ「あ、ほんまや」
ワイ「takashi
のage
が文字列になってしまってるわ」
ワイ「User
型のage
はnumber
のはずやのに」ハスケル子「そういうことですね」
ハスケル子「一行ずつちゃんと読めば、意外と簡単ですよね」ワイ「まあ、なんとかなりそうやな」
ハスケル子「具体的に修正すべきポイントは一番下の行に表示されているので」
ハスケル子「まずは一番下の一行を読むのも良いと思います」ワイ「ほんまや」
できるだけ型を書こう
ハスケル子「あと、
takashi
にも明示的に型を書いたほうが良いですよ」ワイ「どういうこと?」
const takashi: User = { name: 'たかし', age: '36' }ハスケル子「↑こういうことです」
ハスケル子「こうしておくと・・・」ワイ「おお、
takashi
のage
がアカンってことが一目瞭然やな」ハスケル子「そうなんです」
ハスケル子「型情報を書かなくても、ある程度はTypeScriptくんが気を利かせてくれるんですが」「
takashi
はUser
型の値です!」
「私はそういうつもりでこのコードを書いてます!」ハスケル子「っていう風に、能動的に型を書いて宣言したほうが」
ハスケル子「より正確なエラーメッセージを表示してもらえる可能性が高くなりますね」ワイ「なるほどなぁ」
まとめ
Hoge
型の引数としてfuga
をぶち込むことはできまへん!
piyo
というプロパティの型が合ってまへんねん!
aaa
型の値は、bbb
型にはぶち込めまへん!
- TypeScriptでは↑この形式のエラーメッセージが表示されることが多い。
- 英語だし、文が長めだし、怯みそうになるけど、一行ずつ読めば意外と何とかなる。
- 型は能動的に書いていこう。そのほうがエラーの意味が分かりやすくなるよ。
ワイ「↑ってことやな!」
ハスケル子「ですね!」
〜おしまい〜
- 投稿日:2020-10-28T14:26:26+09:00
【JavaScript】ちゃんと勉強してみた④
JavaScriptちゃんと学習中。
ほぼ自分の勉強メモです。
割と見られているみたいなので、ちょっと工夫して書いてみますが、
過度な期待はしないでください。過去投稿記事
【JavaScript】ちゃんと勉強してみた
【JavaScript】ちゃんと勉強してみた②
【JavaScript】ちゃんと勉強してみた③
オブジェクトと関数
- オブジェクトとは
オブジェクトは配列と同じく複数のデータをまとめて管理するもの。
オブジェクトは、それぞれの値にプロパティと呼ばれる名前をつけて管理する。
書き方:{プロパティ1: 値1, プロパティ2: 値2} のように書き作成する。
- 関数とは
関数は、「いくつかの処理をまとめたもの」
定数や変数を定義した横に、「function()」と書き、その後ろの中括弧「{ }」の中に
まとめたい処理を書くことで関数を用意することが出来る。
アロー関数を用いれば、「function()」の部分を「( ) =>」と省略して書く事が出来る。
今回ここでは、この書き方で記載している。
- オブジェクトと関数
オブジェクトの「値」の部分には、関数を用いることも出来る。
書き方は、書きに記載。また、その関数を呼び出すには、
「定数名.プロパティ名()」
と記載する。
プロパティ名の後ろの( )を忘れがち。書き方const 定数名 = { プロパティ名:() =>{ //まとめたい処理内容 } }; // 関数の呼び出し 定数名.プロパティ名();例// 定数animalを定義 const animal = { type:"ライオン", age:5, name:() => { console.log("シンバ"); } } // animalのnameプロパティの値を出力 console.log(animal.type); // animalのgreetプロパティの関数を実行、console.log()は必要無し animal.name();出力結果// animalのnameプロパティの値を出力 ライオン // animalのgreetプロパティの関数を実行 シンバ
クラス
- クラスとは
「もの」を生成する仕組み。その「もの」を生成するには、まずその「設計図」を用意する必要がある。
その設計図のことをクラスという。
「class クラス名」
とすることで新しくクラスを用意する事が出来る。
クラス名は基本的に大文字から始める例// クラスを作成 class Car{ }
インスタンス
- インスタンスの生成
オブジェクトを生成するための設計図(class)が用意できたので、
その設計図から実際にオブジェクトを生成する為には、「new クラス名()」
とする。
クラスから生成したオブジェクトはインスタンスと呼ぶ。例class Car{ } // Carクラスのインスタンスを定数carに代入 const car = new Car();
コンストラクタ
- コンストラクタとは
クラスにはコンストラクタと呼ばれる機能が用意されている。
コンストラクタはインスタンスを生成するときに実行したい処理や設定を追加するための機能。
クラスの中括弧 { } 内に「constructor() { }」
と記述する。書き方class Car{ // クラスの中に追加 constructor() { } }
- コンストラクタの処理
コンストラクタの中に処理を記述することで、ここに書いた処理は
インスタンスが生成された直後に実行される。
そして、大切なのは、インスタンスごとに毎回実行されるという事。例class Car{ constructor() { console.log("新車が発売されます!"); } } const car1 = new Car(); const car2 = new Car();出力結果// インスタンスごとに実行される 新車が発売されます! 新車が発売されます!
- プロパティと値を追加
コンストラクタの中で、生成したインスタンスに関する情報を追加するには、
コンストラクタの中で「this.プロパティ = 値」
とする事で、
生成されたインスタンスにプロパティと値を追加する事が出来る。書き方class Car{ constructor() { this.プロパティ名 = 値; } }
コンストラクタの中で追加した値は、
「インスタンス.プロパティ」
とする事でクラスの外で使用出来る。例class Car{ constructor() { this.name = "トラック"; } } const car = new Car(); // 「名前: 〇〇」となるように出力 console.log(`名前: ${car.name}`);出力結果名前: トラック
- インスタンスごとに値を変える
コンストラクタでは、関数と同じように、引数を受け取ることが可能。
「constructor」の後の括弧「( )」内に引数名を記述することで、
その引数をコンストラクタの処理内で使用出来る。コンストラクタに引数として値を渡すには、
「new クラス名( )」の括弧「( )」内に値を追加する。例class Car{ constructor(name, color) { this.name = name; this.color = color; } } // 引数に「"トラック"」と「黒」を渡す const car = new Car("トラック", "黒"); // 「名前: 〇〇」となるように出力 console.log(`名前: ${car.name}`); // 「色: 〇〇」となるように出力 console.log(`色: ${car.color}`);出力結果名前: トラック 色: 黒
メソッド
- メソッドとは
メソッドとはそのインスタンスの「動作」のようなもの。
「名前」や「年齢」などの情報はプロパティで追加したのに対して、
メソッドは「挨拶をする」「値を計算する」などの処理のまとまりを表す。
- メソッドの定義
メソッドはクラスの中で定義します。
「メソッド名() { }」
とすることでメソッドは定義出来る。
メソッドは関数と似たようなもので、中括弧「{ }」の中にそのメソッドで行いたい処理を記述する。書き方class Car{ メソッド名() { // 行いたい処理 } }
- メソッドの呼び出し方
「インスタンス.メソッド名()」
とする事でそのメソッドを呼び出し、
処理を実行することが出来る。例class Car{ constructor(name, color) { this.name = name; this.color = color; } // actionメソッドを追加 action() { console.log("走る"); } } const car = new Car("トラック", "黒"); // carに対してactionメソッドを呼び出す car.action();出力結果走る
- メソッド内で値を使う
メソッド内でインスタンスの値を使用するには、「this」という特殊な値を用いて、
「this.プロパティ名」
とします。例class Car{ constructor(name, color) { this.name = name; this.color = color; } action() { console.log(`${this.name}は、走る`); } } const car = new Car("トラック", "黒"); // carに対してactionメソッドを呼び出す car.action();出力結果トラックは、走る
- メソッド内でのメソッド呼び出し
メソッド内で他のメソッドを呼び出すことも可能。
メソッド内で「this.メソッド名()」
とすることで、同じクラスの他のメソッドを使うことが出来る。例class Car{ constructor(name, color) { this.name = name; this.color = color; } // actionメソッド action() { console.log(`${this.name}は、走る`); } // infoメソッド info() { // actionメソッドを呼び出す this.action(); console.log(`この車は${this.name}です`); console.log(`${this.color}色です`); } } const car = new Car("トラック", "黒"); car.action();出力結果トラックは、走る この車はトラックです 黒色です
継承
- 継承とは
継承とは、すでにあるクラスをもとに、新しくクラスを作成する方法のこと
例、「Carクラス」から「Truckクラス」を継承すると、「Carクラス」の全ての機能を引き継いで、
「Truckクラス」を作成することが出来る。
- 継承の書き方
継承を用いてクラスを作成するには「extends」を用いる。
「Carクラス」を継承して「Truckクラス」を作成するには、
「class Truck extends Car」
と書きます。
継承では元となるクラスを親クラス(Carクラス)、
新しく作成するクラスを子クラス(Truckクラス)と呼ぶ。書き方class Truck extends Car{ }
「Truckクラス」は「Carクラス」のすべての機能を引き継いるため、
「Truckクラス」内にはまだ何もメソッドは定義されてないが、
「Carクラス」に定義されている「infoメソッド」などを使用することが出来る。例class Car{ constructor(name, color) { this.name = name; this.color = color; } action() { console.log(`${this.name}は、走る`); } info() { this.action(); console.log(`この車は${this.name}です`); console.log(`${this.color}色です`); } } // 「Carクラス」を継承して「Truckクラス」を定義 class Truck extends Car{ } // 定数truckにTruckクラスのインスタンスを代入 const truck = new Truck("トラック", "黒"); // truckに対してinfoメソッドを呼び出す truck.action();出力結果トラックは、走る この車はトラックです 黒色です
- 子クラスにメソッド追加
継承して作成したクラス(子クラス)にも、これまでと同じようにメソッドを追加出来る。
今回は、戻り値を使って追加。ちなみに、子クラスで定義したメソッドは、親クラスから呼び出すことは出来ない。
例class Car{ constructor(name, color) { this.name = name; this.color = color; } action() { console.log(`${this.name}は、走る`); } info() { this.action(); console.log(`この車は${this.name}です`); console.log(`${this.color}色です`); } } class Truck extends Car{ detailsColor(){ return `色の詳細は${this.color}と白です`; } } const truck = new Truck("トラック", "黒"); const detail = truck.detailsColor(); console.log(detail);出力結果色の詳細は黒と白です
- 投稿日:2020-10-28T13:09:28+09:00
Reactを実際に動かしてみる
create-react-app
を導入するところまではやったので、これからReactを使って、どうやってwebページを表示させるのかのアウトプットやります本番環境で公開するフォルダの作成
ターミナルnpm run buildこれでアプリケーション内にbuildフォルダが作成されました。
その後
ターミナルnpm startを入力するとブラウザが立ち上がる
Edit src/App.js and save to reload
と書いてある通り
src
フォルダの中にApp.js
というファイルがあると思います。App.jsimport logo from './logo.svg'; import './App.css'; function App() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Edit <code>src/App.js</code> and save to reload. </p> <a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer" > Learn React </a> </header> </div> ); } export default App;このファイルの中の
Learn React
の部分がブラウザで確認できる青文字で下線が引かれている部分に当たります
ここを別の文字列に変えればその文字列が同様にブラウザで確認ができる様になります実際にこのページのビューを作っているファイルというのは
index.html
というファイルですではなぜ
App.js
の内容がwebページで表示されているのかは、index.jsimport React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; ReactDOM.render( <React.StrictMode> <App /> </React.StrictMode>, document.getElementById('root') ); // If you want to start measuring performance in your app, pass a function // to log results (for example: reportWebVitals(console.log)) // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals reportWebVitals();このindex.jsファイルの
<App />
の部分でApp.js
を呼び出して、それを
document.getElementById('root')
の部分でrootというidに入れ込みますよと記述してあるんです。
そしてのそのroot
というidはどのことを指しているのかというと、、、index.html<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> <!-- <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> --> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="theme-color" content="#000000" /> <meta name="description" content="Web site created using create-react-app" /> <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> <!-- manifest.json provides metadata used when your web app is installed on a user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ --> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <!-- <link href="https://fonts.googleapis.com/css2?family=Open+Sans+Condensed:wght@300&display=swap" rel="stylesheet"> --> <!-- Notice the use of %PUBLIC_URL% in the tags above. It will be replaced with the URL of the `public` folder during the build. Only files inside the `public` folder can be referenced from the HTML. Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> <title>My Favorite Things</title> </head> <body> <noscript>You need to enable JavaScript to run this app.</noscript> <div id="root"></div> <!-- This HTML file is a template. If you open it directly in the browser, you will see an empty page. You can add webfonts, meta tags, or analytics to this file. The build step will place the bundled scripts into the <body> tag. To begin the development, run `npm start` or `yarn start`. To create a production bundle, use `npm run build` or `yarn build`. --> </body> </html>ここに
<div id="root"></div>
とあります
そう、こいつのことです!!流れとしては
index.html :「rootを表示するで」
index.js :「rootはApp.jsに任せるわ」
App.js :「これ見したる」ちょっと違うけど変数を使って、どんどんと代入されていっているイメージですかね
こんな感じでwebページに表示させるところまでは完了!!
create-react-app
を使うとこんなに簡単にReactでアプリケーションの土台が作れるんですね〜
☆☆ SU・GO・I ☆☆
それよかJSの知識不足がすごいな、、、
少し勉強したからReactも出来っかなとか楽観的に考えていたけどそんなこともないんかね
React触っていればJSの復習も出来るからって考えで良いのか、それ以前にJSをもっと勉強するべきか、、、うーん、わからん!!
どっちが良いのか誰かエロい人教えて下さい!!(>人<;)
- 投稿日:2020-10-28T11:28:58+09:00
【JS】Chromeでは動作OK。だが、iOS、safariでReferenceError!?!?
エラー内容
sample.jslet a = xxxxx.join('');ReferenceError: Can't find variable: xxxxxChromeでは、出なかった参照エラー。
ブラウザ特有のエラーということで、「safariのみ ReferenceError JS」
などのワードでググってみましたが、jQueryのクリックイベントのDOM参照方法であったり、cssのcursor: pointer;
を足してみるであったり、自分のエラーと本質的に異なるものばかりで諦めていました。また、clickイベントで出たーエラーでだったので、イベントが発生するところとしない箇所でconsoleで確認してみたりと時間をかなり掛けました。
しかし、原因はとても単純でした・・・
解決策
変数宣言を変えるだけ。
sample.jsvar rivalNumText = xxxxx.join('');まさかのvarに変えるだけでした。。。
原因は結局??
varよりも、letの方が比較的新しいということで使用していました。
しかし、let
はSafariやIE、iOS、またAndroidのブラウザではサポートされていないみたいです。
動作スピード的にはletやconstを使用する方が早いみたいですが、ブラウザ対応の面で考えるとその点についても考慮する必要があるみたいです。●<コメントより修正>
let
が使用できないのは、自分自身の環境の問題の可能性もあります。
対応しているブラウザであれば、ブラウザの更新でlet
を使用できるよう解決できます。今回で言えば、
let
がどのブラウザで使えるのかこちらのサイトから確認できるみたいです。参考
- 投稿日:2020-10-28T10:59:09+09:00
ワイ「TypeScriptなんも分からん」
TypeScriptなんも分からんワイ
ワイ「なぁ、ハスケル子ちゃん」
ハスケル子「はい」
ワイ「最近、TypeScriptっていうのが流行ってるみたいやけど」
ワイ「何がええの?」
ワイ「普通のJavaScriptじゃアカンの?」ハスケル子「うーん」
ハスケル子「じゃあ、VSCodeでコードを書きながら説明しますね」
ハスケル子「例えば・・・」sample.jsconst titles = ['記事タイトル1', '記事タイトル2', '記事タイトル3'] const newestTitle = titles.pop() console.log(newestTitle)ハスケル子「↑このコードは正しく動くと思いますか?」
ワイ「ええと」
ワイ「まずはtitles
という変数に、記事タイトルが3つ入った配列を格納するんやな」
ワイ「そんで、配列が持ってるpop()
メソッドを実行して」
ワイ「一番最後の要素を取得しとるわけか」
ワイ「ほな、newestTitle
という変数には'記事タイトル3'
が格納されるな」
ワイ「せやから、コンソールには'記事タイトル3
と表示されるはずや」
ワイ「問題ないな」
ワイ「これは、正しく動くコードやで!」ハスケル子「そうですよね」
ハスケル子「じゃあ次は・・・」sample.jsconst titles = '記事タイトル1' const newestTitle = titles.pop() console.log(newestTitle)ハスケル子「↑このコードはどうですか?」
ハスケル子「正しく動きますか?」ワイ「ええと、
titles
という変数に'記事タイトル1'
という文字列を格納して」
ワイ「そのあとtitles.pop()
しとるわけか」
ワイ「あれ、文字列はpop()
なんてメソッドを持ってたっけ?」
ワイ「たぶんエラーになるやろ」ハスケル子「そうですね」
ハスケル子「実際にブラウザ上でこのコードを実行してみると、こんなエラーが出ます」titles.pop is not a function
(titles.popは関数とちゃうで!)ワイ「せやな」
ワイ「文字列はpop()
なんていうメソッドを持ってへんからな」
ワイ「titles.pop
は未定義、つまりundefined
扱いや」
ワイ「変数でも関数でもあらへん」
ワイ「せやから」titles.popは関数とちゃうで!
ワイ「って怒られてまうんやな」
ハスケル子「はい」
ハスケル子「要するに」文字列を、間違えて配列みたいに扱うとバグっちゃうよ
ハスケル子「って話ですよね」
ワイ「せやね」
ハスケル子「実際のWebサイトでこういうバグを起こすと、マズいですよね」
ワイ「まあ、クライアントさんから怒られてまうな」
ハスケル子「そこでTypeScriptです」
ワイ「ふーん」
あり得ないことをしていたら教えてくれる
ハスケル子「さっきの間違ったコードを、TypeScriptを使って書いてみましょう」
ハスケル子「やめ太郎さん、やってみてください」
ハスケル子「拡張子は.ts
でお願いします」ワイ「はいよ」
ワイ「TypeScriptでは.js
やなくて.ts
なんやね」
ワイ「はい、書きましたやで」ワイ「おお、
pop
の部分に赤い波線が出とるな」ハスケル子「文字列は
pop()
メソッドを持っていないので」
ハスケル子「そういうあり得ないことをしようとすると」
ハスケル子「エディタが教えてくれるんです」ワイ「なるほどな」
ワイ「ブラウザ上でポチポチ動作テストせんくても」
ワイ「コードを書いとる段階で気づけるわけやね」ハスケル子「はい」
ハスケル子「あとですね、赤い波線が出ているpop
の部分に」
ハスケル子「マウスをホバーさせてみてください」ワイ「おお、詳細なエラーメッセージが表示されたで」
ワイ「なんや、TypeScriptって親切なやつなんやな」ハスケル子「そうなんですよ」
ハスケル子「あり得ないことをしてundefined
エラーが出るのを、事前に防いでくれるわけです」ワイ「なるほどな」
ワイ「小さめのサイトなら、気をつけてコーディングすれば」
ワイ「バグは出ないかもしれんけど」
ワイ「大規模なサイトだと、どっかでミスは起きるから」
ワイ「TypeScript君に見つけてもらえると」
ワイ「だいぶバグを減らせそうやな」ハスケル子「はい」
ハスケル子「ほかにもVSCodeくんから受けられる恩恵がだいぶ増すので」
ハスケル子「かなりメリット大きいです」ワイ「ハスケル子ちゃん、ありがとうやで」
ワイ「ワイもTypeScript勉強してみるわ!」ハスケル子「TypeScriptは、JavaScriptに変換しないとブラウザ上で動かないので」
ハスケル子「変換するための環境構築とかも勉強してくださいね!」ワイ「おう!」
続編もよろしくやで!
勉強におすすめのサイト
とりあえず触ってみたい人におすすめのサイト
TS Playground
ブラウザ上でTypeScriptを書いてみられるサイトセットアップ | Nuxt TypeScript
Nuxt.js(Vue)で使ってみたい人はこちらTypeScript Next.js example
Next.js(React)で使ってみたい人はこちら
- 投稿日:2020-10-28T10:49:16+09:00
【JavaScript】ちゃんと勉強してみた③
JavaScriptちゃんと学習中。
ほぼ自分の勉強メモです。
過度な期待はしないでください。過去投稿記事
【JavaScript】ちゃんと勉強してみた
【JavaScript】ちゃんと勉強してみた②
関数
- 関数とは、「いくつかの処理をまとめたもの」
- 関数の定義
定数等を定義した横に、「function()」と書き、その後ろの中括弧「{ }」の中にまとめたい処理を
書くことで関数を用意することが出来る。書き方const 定数名 = function(){ //まとめたい処理内容 }; 定数名(); → 「定数名()」とする事で関数を呼び出す事が出来る例// 関数の定義 const greet = function() { console.log("こんにちは!"); console.log("元気ですか?"); }; // 関数を呼び出す greet(); // 出力結果 → こんにちは! 元気ですか?
アロー関数
- 「function()」の部分を「( ) =>」としても、これまでと同じように関数を
定義することが出来る。この関数の書き方のことを、特別にアロー関数と呼ぶ。
これはES6から導入された新しい書き方。アロー関数// 関数の定義 const greet = () => { console.log("こんにちは!"); console.log("元気ですか?"); }; // 関数を呼び出す greet(); // 出力結果 → こんにちは! 元気ですか?
引数
- 関数に与える追加情報のようなもの、
関数を呼び出すときに一緒に値を渡すことで、関数の中でその値を利用することが出来る。引数を受け取る関数を定義// どちらでも可 const 定数名 = function(引数名){ //まとめたい処理内容 console.log(`私の名前は${引数名}`です); }; const 定数名 = (引数名)=>{ //まとめたい処理内容 }; // 引数を受け取る関数を呼び出す 定数名(値) → その値は関数の引数に代入されます例// 関数の引数にnameを追加 const greet = (name) => { // 「こんにちは、〇〇さん」となるように出力 console.log(`こんにちは、${name}さん`); }; // greetの引数に「Aki」を渡して呼び出す greet("Aki"); // 出力結果 → こんにちは、Akiさん
- 複数の引数を受け取る関数
引数は、左から順番に「第1引数、第2引数、...」と呼ぶ。
( )の中に受け取る引数をコンマ(,)で区切って並べる。複数の引数を受け取る関数を定義const 定数名 = (第1引数, 第2引数)=>{ //まとめたい処理内容 }; // 引数を受け取る関数を呼び出す 定数名(第1引数の値, 第2引数の値);例// 関数の引数にnameとageを追加 const information = (name, age) => { console.log(`彼女の名前は、${name}さん`); console.log(`彼女の年は、${age}です`); }; // greetの引数に「Aki」を渡して呼び出す information("Aki", 25); // 出力結果 → 彼女の名前は、Akiさん 彼女の年は、25です
戻り値
- 呼び出し元で受け取る処理結果を戻り値(もどりち)と呼ぶ。
「return 値」と書くことで、関数はその値を戻り値として返す。例const half = (number) => { // numberを2で割った値を戻り値として返す return number / 2 ; }; // 定数resultを定義 const result = half(130); // 「130の半分は〇〇です」となるように出力 console.log(`130の半分は${result}です`); // 出力結果 → 130の半分は65です
スコープ
- 関数の引数や、関数内で定義した定数や変数は、その関数の中でしか使うことが出来ない。
→ その範囲のことをスコープと呼ぶ。関数の中の定数const introduce = () => { // 定数nameを定義 const name = "Aki"; // 定数nameが使えるは範囲 }; // 定数nameが使えない範囲
- 関数の外で定義した定数や変数は、関数の中でも外でも使うことが出来る。関数の外の定数// 定数nameを定義 const name = "Aki"; const introduce = () => { // 定数nameが使える範囲 }; // 定数nameが使える範囲
- 投稿日:2020-10-28T10:43:16+09:00
乳幼児の予防接種管理ツールを作った。
乳幼児の予防接種スケジュール管理ツールを作りました!
初めてオリジナル企画で作ったサイトです。
カレンダーから日付を入力して、そこから計算した指定の日付を表示させるだけなので、開発と呼ぶにはおこがましい程のレベルのものですが、まぁ作ったよね。ww
jQueryとMoment.jsを使用。この時初めて、静的なサイトであれば、S3にアップロードするだけでサーバが要らないという事実を知りました!(*'ω'*)↓↓ こんなツールです。お子さんのお誕生日を入力すると、いつ頃予防接種に行けばいいのかを自動表示します。
予防接種スケジューラー ← こっからサイトに飛んでください~。
私自身、1歳10ヶ月と8ヶ月の息子が2人いるのですが、予防接種の種類や回数が多くて、ちゃんと管理できるか不安だったんですね。そんな時に長男を産んだ産院で、今回作ったツールのような管理表をもらいまして、それがすっごい便利~!と思ったのでWebバージョンを作ろうと思ったわけです。
ただ、色々と調べたのですが、各市区町村によって定期接種対象のワクチン等が微妙に違う(!?)みたいで、埼玉県の予防接種情報を元に作っています。ご了承ください。(m´・ω・`)m ゴメン… (でも、あまり大きな違いはなかったので、埼玉県外の方も十分使っていただけると思います!)小さいお子さんを持つ方はぜひ使ってみてください!
また、お知り合いでいらっしゃる方はぜひ紹介してみてください~。宜しくです。( `・∀・´)ノヨロシク
- 投稿日:2020-10-28T08:09:36+09:00
[javascript]Promiseと配列系メソッドを上手に組み合わせて使う!
※新しい便利クラスを定義してやろうという記事です。普通に使うなら使いたいメソッドに合わせて関数を作るだけで充分かもしれません。
はじめに
Promiseとasync/awaitは便利ですが、そのまま配列と組み合わせて使おうとすると途端に面倒になります。
//並列に実行したい時 const res = array.map(url=>fetch(url).then(res=>res.json())) Promise.all(res).then(~)//直列に実行したい時 [5000, 2000, 6000].reduce((p, val)=>p.then(async ()=>{ await wait(val) console.log(val) }), Promise.resolve())
Promise.all
やreduce
が途中に入るので、何をやっているかが分かりにくい書き方になっています。for-await-ofなどを使うという手もありますが、普通の配列のようにmap()とかfilterとかでチェーンしていきたい時もあると思います。
そこで、Promiseが入った配列を手軽に扱うために、メソッドチェーンのたびに内部で暗黙的にPromise.all()が呼ばれるような配列クラスを自作したいと思います。クラス名はAsyncArrayです。配列のメソッドの分類
PromiseとArrayを組み合わせていくにあたって、配列系メソッドにはいくつかの種類があることが分かりました。以下のように分類できます。
A. メソッドを呼ぶ際に配列の中身がすべて決まっている必要があるもの
- find, sort, indexOf, join, flat, reduce, toString, などなど
これらのメソッドは、実行の際に配列の中身がすべて求まっている必要があります。
B. メソッドを呼ぶ際に配列の中身がすべて決まっている必要がないもの
- concat, fill, pop, push, reverse, slice, entries, forEach, filter, map, every, some, などなど
これらのメソッドは、実行の際に配列の中身がすべて求まっている必要がありせん。Promise.allを呼ぶことなく、Promiseが入った配列として操作することができます。
更に、Bは以下のようにさらに細かく分類することができます。B-1. 配列の順番通りに実行してほしいもの
- map(一部の場合), forEach(一部の場合), などなど
- for-await-ofのような動作
- 「forEachのasync関数が順番通り実行されなくて困る」みたいな
B-2. それ以外
- その他大勢
メソッドを使う際にタイプAかタイプBかなんていちいち気にしてられないので、定義する際は、メソッド名にAsyncを付けたらタイプB-2で動作するることにします。
例)
-.map()
→実行前にPromise.all()が呼ばれる(順番通り)
-.mapAsync()
→実行前にPromise.all()が呼ばれない(順番が前後する)タイプB-1は要は「他のPromiseが解決前であっても処理を始める(ただし順番は守る)」という事です。しかしjavascriptはシングルスレッドなので、順番が前後しようが実行時間は変わりません。このタイプが有効なのはfetchとsetTimeoutだけだと思います。なのでforEachに対してしか実装してません(後述)。
前提
await hogehoge
と書くと暗黙的にhogehoge.then()が実行されて値が返ります。
hogehoge
がPromiseであってもなくても、then()が実行されます。await 114514
のようにthenメソッドを持たない値が来たら、値がそのまま返ります。- thenメソッドを持つオブジェクトをthenable(=then可能)といいます。
実装方針
- Arrayは継承しない
- Object.getOwnPropertyNames(Array.prototype)からメソッド一覧を取得し、definePropatyする
- AsyncArray.from(arrat)でインスタンスを生成し、asyncArray.then(array=>~)又はawait asyncArrayで普通の配列に戻せる
await AsyncArray.from([0, 1, 2]).map(i=>i*1000).map(i=>wait(i))
みたいな感じ- async関数も渡せる
- メソッドチェーン可能
.then(arr=>arr.map(~)).then(arr=>arr.map(~))
ではなく.map(~).map(~)
と書ける- 配列本体はは内部プロパティで保持する。内部プロパティは「「値を返すPromise」の配列を返すPromise」とする
Promise<Array<Promise<T>>>
(でいいのか?)(知らない)- 途中で内部的にPromise.allを噛ませる必要があるため、配列がPromiseでラップされてしまう
- lengthとかは無い (flatとか呼ばれると全てのPromiseが完了するまで取得できないので、同期的には無理)
- なので[Symbol.iterator]もない([Symbol.asyncIterator]はいける)
実装内容
const currentValue = Symbol('currentValue') const fromValue = Symbol('fromValue') class AsyncArray { constructor(...args) { //Arrayコンストラクタのように動作 this[currentValue] = Promise.resolve(new Array(...args)) } then(resolve, reject) { //currentValueのPromiseが全て解決された状態で返す return this[currentValue] .then(v=>Array.isArray(v)?Promise.all(v):v) .then(resolve, reject) } async forAwaitEach(callback, thisArg) { //for-await-ofのように動作 const useThisArg = 1<arguments.length const array = await this[currentValue] let promise = Promise.resolve() array.forEach((value, ...args)=>{ promise = Promise.all([promise, Promise.resolve(value)]).then( ([_, resolvedValue])=>useThisArg ?callback.call(thisArg, resolvedValue, ...args) :callback(resolvedValue, ...args) ) }) await promise } get [Symbol.toStringTag]() { return 'AsyncArray'; } /*[Symbol.iterator]() { //値で解決されるPromiseを取り出す //lengthが確定しないので無理 }*/ async *[Symbol.asyncIterator]() { //値を取り出す const array = await this[currentValue] for await (const value of array) { yield value } } toString() {//Array#toString.applyで上書きされないように return Object.prototype.toString.call(this)//'[object AsyncArray]' } } AsyncArray[fromValue] = function (promiseValue) { //AsyncArrayを生成して返す(arrayへキャストしない) //メソッドチェーン用(Arrayが期待されるが、Arrayでない場合もthen()を呼ぶことで値を取り出せる) const result = new AsyncArray() result[currentValue] = Promise.resolve(promiseValue) return result } AsyncArray.from = function (...args) { //AsyncArrayを生成して返す(arrayへキャストする) const result = new AsyncArray() result[currentValue] = Promise.resolve(Array.from(...args)) return result } AsyncArray.of = function (...args) { //AsyncArrayを生成して返す(arrayへキャストする) const result = new AsyncArray() result[currentValue] = Promise.resolve(Array.of(...args)) return result } AsyncArray.isAsyncArray = function (obj) { return Object.prototype.toString.call(arg)==='[object AsyncArray]'; } //constructorなどを上書きしない const originalMethods = new Set(Object.getOwnPropertyNames(AsyncArray.prototype)) //Array.prototypeのメソッドを追加 for (const prop of Object.getOwnPropertyNames(Array.prototype)){ if (typeof(Array.prototype[prop])!=='function'||originalMethods.has(prop)) { continue } Object.defineProperty(AsyncArray.prototype, prop, { writable: true, enumerable: false, configurable: true, value(...args) { const result = this[currentValue].then( promiseArray=>Promise.all(promiseArray).then( resolvedArray=>[Array.prototype[prop].apply(resolvedArray, args), resolvedArray] ) ) this[currentValue] = result.then(([returnValue, promiseArray])=>promiseArray) return AsyncArray[fromValue](result.then(([returnValue, promiseArray])=>returnValue)) } }); } //Array.prototypeのメソッドで、実行の際にPromiseが解決されている必要がないメソッド //hogehogeAsyncのような名前で追加 const asyncableMethods = ['concat', 'copyWithin', 'fill', 'pop', 'push', 'reverse', 'shift', 'unshift', 'slice', 'splice', 'keys', 'entries', 'values', 'forEach', 'filter', 'map', 'every', 'some'] for (const originalProp of asyncableMethods){ const prop = `${originalProp}Async` Object.defineProperty(AsyncArray.prototype, prop, { writable: true, enumerable: false, configurable: true, value(...args) { const result = this[currentValue].then( promiseArray=>promiseArray.map(v=>Promise.resolve(v)) ).then( promiseArray=>[Array.prototype[originalProp].apply(promiseArray, args), promiseArray] ) this[currentValue] = result.then(([returnValue, promiseArray])=>promiseArray) return AsyncArray[fromValue](result.then(([returnValue, promiseArray])=>returnValue)) } }); }使用方法
const asyncArray = AsyncArray.from([5, 1, 2]) await asyncArray.map(i=>i*1000).map(async i=>{ await wait(i) return i }).forAwaitEach(i=>console.log(i))const wait = n => new Promise(r=>setTimeout(_=>r(n), n)) const asyncArray = AsyncArray.from([3, 5, 1, 2]) for await (const v of asyncArray.map(i=>i*1000).map(wait)) { console.log(v); }await AsyncArray.from([0, 1, 2]) .mapAsync(async i=>await(i)+1) .flatMap(i=>[i, i+10, i+50]) .sort((a, b)=>b-a) .slice(0, 5) .join('-')forEachやmapに普通にasync functionが渡せます。
注意点として、メソッドチェーンの返り値は必ず(stringであっても)AsyncArrayにラップされます。これは、値を返す時点では返り値の型が確定していないからです。(Promiseを返した場合はメソッドチェーンできなくなる)
配列にない独自のメソッドとしてforAwaitEach
を実装しています。上で分類したタイプB-1にあたるメソッドで、for-await-ofのように動作します。mapAsyncなどの非同期メソッドで配列内の値にアクセスすると、promiseが返されるので、内部で適宜awaitしてください。
おわりに
AsyncArrayを実装している人は他にもいらっしゃるようです。ただmapやfilterなどを全部自作しているのがほとんどで、配列にもとからあるメソッドを流用するやり方をしている方がいなかったので、作ってみました。今後Array.prototypeのメソッドが増えても、この方式なら対応可能だと思います。
- 投稿日:2020-10-28T05:46:53+09:00
音声合成で しゃべらせる JavaScript
とりあえず しゃべらせる。
以下のファイルをダブルクリックで実行。talk.js
var tts = WScript.CreateObject("SAPI.spVoice"); tts.Speak("生麦 生米 生卵");しゃべらない? なら、
マイクロソフトスピーチプラットフォームをインストール。http://www.microsoft.com/en-us/download/details.aspx?id=27225
x86_SpeechPlatformRuntime¥SpeechPlatformRuntime.msi
または
x64_SpeechPlatformRuntime¥SpeechPlatformRuntime.msi日本語を追加
http://www.microsoft.com/en-us/download/details.aspx?id=27224
MSSpeech_SR_ja-JP_TELE.msi
MSSpeech_TTS_ja-JP_Haruka.msi
- 投稿日:2020-10-28T02:10:53+09:00
JavaScriptでGETパラメータ(URIクエリ)を取得する方法(コードスニペット)
サンプルのクエリ:
?a=0&b=2&a=1&d[]=0&d[]=1&こんにちは=hoge=piyo配列と値にイコールが含まれてることを考慮しない
const $_GET = Object.fromEntries(decodeURIComponent(location.search).slice(1).split('&').map(prop => prop.split('=')));結果:
{ "a": "1", "b": "2", "d[]": "1", "こんにちは": "hoge" }配列を考慮しない
const $_GET = Object.fromEntries(decodeURIComponent(location.search).slice(1).split('&').map(prop => { const data = prop.split('='); return [data[0], data.slice(1).join('=')] }));結果:
{ "a": "1", "b": "2", "d[]": "1", "こんにちは": "hoge=piyo" }配列も考慮する
const $_GET = (query => { const dataSet = {}; for (const data of query) { const key = data[0]; const val = data.slice(1).join('='); if (key.endsWith('[]')) { dataSet[key] ??= []; dataSet[key].push(val); continue; } dataSet[key] = val; } return dataSet; })(decodeURIComponent(location.search).slice(1).split('&').map(prop => prop.split('=')));結果:
{ "a": "1", "b": "2", "d[]": [ "0", "1" ], "こんにちは": "hoge=piyo" }
- 投稿日:2020-10-28T00:32:42+09:00
React: Google Chrome for macOSでメモリリークが起こる
Google Crome for macOSで、メモリの消費が増え続けるという現象に遭遇しました。英語を含めても情報が少なかったので、備忘録としてまとめます。
Simple app causing a memory leak?
(2018/02/17)
リスナーが増え続けて、メモリが費やされるという問題が示されました。
それに対して、ReduxやCreate React Appの開発に携わったDan Abramov氏は、つぎのようにコメントしています(なお「React開発者ではありません」とは、ご自身の紹介文)。メモリ消費が増えているのはリスナーを膨大につくっているからだというのです。メモリリークではなく、開発ビルドでのみ起こり、プロダクション版では生じないと説明しています。また、リスナーもガベージコレクションにより片づけられるそうです。
Dan Abramov氏によるコメント(2018/02/19)
Fairly sure this isn't a memory leak but just excessive listener creation. It only happens in development builds, not in production. You can see these thread for more details: facebook/react#10576 and facebook/react#12141. We do create listeners but we expect GC to clean them up.
この問題はDan Abramov氏が同日にクローズしました。
Apparent Slow (25 Bytes per Render) Memory Leak with React / React-Dom?
(2017/08/31)
John Tucker氏により、メモリリークがわずかずつ増えるという問題が報告されました。簡単なテストサンプルで、現象が再現されています。けれど、2017年8月31日に同氏のコメントで撤回され、クローズされました。
テストの仕方がまずかったということです。ただし、ミスがあったというのではありません。ReactでなくChromeデベロッパーツールの問題だというのです。デベロッパーツールのメモリ消費が大きく、ツールを閉じるだけでタスクマネージャのメモリ使用量は減りました。再度ツールを開くと1時間につき1MBほどずつアプリケーションのメモリ使用量が増えたということです。
ガベージコレクションを実行できるようデベロッパーツールを開いていたものの、閉じればメモリ消費は適宜減り、ガベージコレクションが適切に働いたといいます。
John Tucker氏によるコメント(2017/08/31)
NEVERMIND.... It appears that my testing methodology was broken and it does NOT look like there is a problem with React (rather a problem with Chrome). I just so happened to be testing my actual application and noticed that my Chrome "Tab: Developer Tools" was insanely large (70+ MB). When I closed it, I noticed that Chrome's Task Manager stopped showing the steady growth in the application that I was troubleshooting. I was seeing growth like 1 MB per hour in the application itself when Developer's Tools was open.
I went back to the React test that I did here and this time left the Chrome Developer's Tools CLOSED and just watched the Task Manager. Originally, I left Developer's tools open so that I could trigger a GC.
Without Developer's tools open, I saw the automatic GC process get triggered every so often and the memory would regularly drop way down to 5MB.
React16 dev memory leak on render with event listeners
(2018/02/03)
スクロールやキーボード入力のイベントリスナーが加えられたとき、すばやくイベントを起こすとメモリリークにより反応が遅れると報告されました。ただし、ほかの人たちは適切に再現できなかったようです。それに対して、Dan Abramov氏からコメントが寄せられました。
Reactが開発用に起こしているフェイクイベントは、レンダリングのためのもので、ユーザーイベントには関わらないといいます(該当コードはコメントのリンク参照)。
Dan Abramov氏によるコメント(2018/02/03)
I don't believe it has to do with generating events fast. It has to do with renders.
Because fake events are created every time we call into user code in DEV here.
Reactは開発モードで、レンダリングされるコンポーネントに、それぞれリスナーを割り当てます。フェイクイベントは、コンポーネントコードから例外スローを切り分けるためのものだということです。プロダクション版では、この処理は外されます。
Dan Abramov氏によるコメント(2018/03/01)
As I mentioned before, it is expected that React will allocate a listener on every component render in development mode.
We use fake events to "isolate" component code so that if it throws, the browser displays an "Uncaught error" message even if some code above in the call stack accidentally catches it. Similarly this is what makes "break on all exceptions" work even though React is wrapping your components in a try/catch.
This overhead doesn't exist in production versions.
- 投稿日:2020-10-28T00:25:37+09:00
【初心者向け】HTML・CSSのセルフチェック方法のポイント
どうも7noteです。一通りコーディングが終わった後の最後のチェック方法について
やっとこさコーディングが完了したと思って油断したら、後から「よく見たらなんか崩れてる」なんてことも。
そうならないためにもセルフチェックのスキルはとても重要です。特に1人で開発している人は自分が見落とせばもう取り返しはつかないので、必須スキルになります。
でも始めたての頃はどのように何をチェックしたらいいのかわからない方も多いと思うので、初心者の方が抜けやすいポイントを抑えながら、基本的なチェック方法を確認していきます。
コーディングのセルフチェック方法
〜必須チェック項目!〜
- デザインと見比べて差異がないか目視でチェック。
- aタグなどのリンク先が抜けてたり間違いがないか。
- 画像のaltに抜けがないか。
- テキストのコピペミスや誤字脱字がないか。(英語のスペルミスもチェック)
- javascriptがきちんとすべて動作しているかチェック。
- 異なるブラウザ(特にIE)で見ても綺麗に見えるか。
- マウスホバー時の処理が適切にできているか。
- タグ構造に間違いがないか。(タグの閉じ忘れ等。)
- ダミーの情報やすかし入りの画像が使われていないか。
- フォームのあるサイトなら、ちゃんとメール送信ができているか。
- ウィンドウ幅を大きくしたり小さくしたりしてレイアウトが崩れないか。
〜レスポンシブサイトならチェックしておきたい事〜
- 必ず実機のスマホでチェック。とくにiPhone。(inputやselectのデザインが変わってしまうので注意。)
- 幅指定の影響で、横スクロールができてしまわないかチェック。
- 画像がぼやけてないかチェック。
- 検証ツールを使って、幅320pxのような画面の小さいスマホで見ても崩れないか。
- タップするところが押しやすい作りになっているか。
〜細かいところもしっかりチェック!!!〜
- aタグのクリック範囲は適切か。
- javascriptは後で見た時に何をしているかわかりやすくコメントを残しているか。(必須ではない)
- 使っていないゴミclassが残っていないか。
- CSSはショートハンドをしっかり使っているか。
- marginの相殺が起こっていないか。
- faviconやogp画像は設定されているか。
- 「&」を「
&
」で記述できているか。- 上下中央に配置した要素は、しっかりドンピシャで上下中央ですか?
〜SEO対策をするなら・・・〜
- titleタグやmeta情報はきちんと設定されているか。
- アナリティクスのタグは入っているか。
- サーチコンソールは設定しているか。
- 画像イメージを圧縮などして、最適化しているか。
- サイトマップは作成しているか。
- robots.txtの記述が適切か。
- canonicalの設定がきっちりできているか。
ポイント解説
チェックする時は必ず目視でチェックしましょう!ツールを使ってデザインデータと重ねてチェックする方法などもあるので、より正確にチェックをするのであればそのようなツールを使うことをおすすめします!
関連記事→「1pxのズレもなくコーディングするための便利ツール」またよくやってしまいがちなのがwidth指定が適切でなく、横スクロースバーが出てしまう現象です。
特にスマホで見た時に対処していないと見た人が下にスクロールしたつもりが斜め下方向に移動してしまい、見たい箇所が見切れてしまうということが起きます。
些細なことですが、小さな気配りができていないためユーザーが不快な思いをしてせっかくみていたページを閉じてしまうかも!!!いざ完成して見直ししていると、たまに使っていないclassやCSSの指定が残っていたりします。
数カ月後に見直した時に、なんの役割を果たしているのかわからなくなってしまうので、ゴミclassは忘れずに削除しましょう。まとめ
きっと他にも様々なチェックポイントがあります。ただ、人によって癖がかならずあるので一度抜けたりミスしたことはメモを取っておく事をおすすめします。
とったメモを「公開前のチェックシート」等の形式でまとめておけばミスがあっても安心して修正対応ができます!どのようなところにミスがあるのか検討をつけないままチェックをしていても、何度も同じところを見たり、あっちを開いたりこっちを開いたり時間ロスにも繋がってしまうので、自分なりのチェックの手順や順番を組み立てていくのがいいかなと思います!
慣れてくれば、必然的に組み込んでいる最中に気を使いながらコーディングができるようになるので、チェック項目が多くても短い時間でチェックが完了できるようになるので、「質より量!」という意気込みでどんどん場数を増やしていきましょう。
おそまつ!
~ Qiitaで毎日投稿中!! ~
【初心者向け】HTML・CSSのちょいテク詰め合わせ