20201028のJavaScriptに関する記事は25件です。

JavaScript スタイルシートの操作

前回の ノードを追加・置換・削除
においての、おまけの続き

スタイルシートの操作

インラインスタイルにアクセス styleプロパティ

HTMLのStyle属性にアクセスする方法。最も簡単にできるが、構造と装飾を分離するために、使いどころは考えなければいけない。

 elem.style.prop = value

elem:要素オブジェクト 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 = clazz

elem:要素オブジェクト clazz:スタイルクラス

.highlight {
  background-color: red;
}
elem.addEventListener('mouseover',function(){
  this.className = 'highlight';
},false);

cssで定義された.highlightをJSファイルで読み込んで、スタイルを変更している。

これにて ー完ー

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

Gatsbyのチュートリアルを試してみた style編

はじめに

前回に引き続きgatsbyのチュートリアルを試してみた話を書こうと思います
今回はスタイリングについてです
・・・netlifyへのデプロイは特に問題も無くスイスイできたので記事にするのはやめました

Gatsbyチュートリアル

環境

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.js
import "./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.js
import React from "react"
import conainerStyles from "./container.module.css"

export default function Container({ children }) {
    return <div className={conainerStyles.container}>{children}</div>
}
about-css-module.js
import 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.js
module.exports = {
  plugins: [`gatsby-plugin-emotion`],
}

複数のプラグインを使用している場合はこんな感じ

gatsby-config.js
module.exports = {
  plugins: [
    {
      resolve: `gatsby-plugin-typography`,
      options: {
        pathToConfigModule: `src/utils/typography`,
      },
    },
    `gatsby-plugin-emotion`,
  ],
}

これでEmotionを使用する準備はできたのでJSXにこんな感じでスタイルを記載していきます

index.js
import 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.js
const 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が効かないのでプラグインを探さないと
ちょっと効率が悪い気がしました

今回のスタイルについてはここまでにします
まだまだチュートリアルは続くので引き続き進めていこうと思います

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

【Rails】星★の評価を実装する(入力、保存、表示)

はじめに

開発しているメモアプリにランク付けをしていきたいと思います。
今回は三段階で評価していこうと思います。
それに伴い、星★による評価の入力・保存・表示について学習していきます。

参考

jQuery Raty

Railsで星の評価機能をつける

jQuery Ratyの使い方

マイグレーションファイルにカラムを追加

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
end
rails db:migrate:reset

javascriptの読み込み、★画像の読み込み

上記参考リンクを確認しながら、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.rb
class 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 :param, :numericality => { :less_than_or_equal_to => }
   # 数字が3以下であるか
   validates :param3, :numericality => { :greater_than_or_equal_to =>  }
   # 数字が1以上であるか

end

コントローラーの記述

notes_controller.rb
class 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 %>,

これで完成!

最後に

説明が分かりづらかったりすると思いますがご了承ください。
また、間違っているところがあればご教授いただけると幸いです。

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

【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文の中で定義された変数に代入や計算などの処理を行っても上書きされるだけ。

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

ノードを追加・置換・削除

前回の
属性値やテキストの取得
の続き

ある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:削除対象のノード

これで、追加・置換・削除ができるようになった。
結構、一つの物を変更するだけでも手間がかかるんですね…
いい勉強になりました!

→ 完
→ と思いきやおまけ スタイルシートの操作

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

【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]を削除することができた。
削除する番号を動的に指定すれば、指定した要素を消すことができる。

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

【 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を待機させます。

参考にさせていただいた記事

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

【 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を待機させます。

参考にさせていただいた記事

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

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.js
const pullDownUp = document.getElementById("lists")

今回はlistsをhtmlから取得してきてpullDownUpに代入しています!
これだけだとコンソールには何も表示されないので

card.js
window.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した時にリストの色を削除(元の色に戻す)ことができるみたいな感じです。

まとめ

ほかにもクリックした時など山ほど書きたいことはあるのですが、またそのこともかいてしまうととても長くなるので別の時にでもアウトプットさせていただきます。

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

属性値やテキストの取得

前回の
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に入れることができる。

以上で、属性値やテキストの内容を取得することができた。次回はこれをもとに、ノードを追加・置換・削除する

→ 次回:ノードを追加・置換・削除する

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

vue.jsを初めて学ぶ ③ [レンダリング]

前回へのリンク

vue.jsを初めて学ぶ ① [hello world]
vue.jsを初めて学ぶ ② [テンプレート構文]


目次

1. v-ifを使用した、条件付きレンダリング

2. v-forを使用した、リストレンダリング


条件付きレンダリング

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>
  1. v-for の使い方
    (引数) in 配列名
  2. {{ }} 二重括弧内に引数を入れる。
  3. index, value の2つの要素を持っていることをチェック。
index.js
    new Vue ({
      el: '#app',
      data: {
        fruits: ['りんご','ばなな','チェリー']
      }
    })
  1. 変数fruitsに代入。
    配列形式で{value}を作成。

3. オブジェクトから取り出す

index.html
<div id = "app">
    <ul>
      <li v-for="(value,key,index) in object"> 
        {{index}}{{key}}{{value}}
      </li>
    </ul>
  </div>
  1. v-for の使い方
    (引数) in オブジェクト名
  2. {{ }} 二重括弧内に引数を入れる。
  3. key, value, index の3つの要素を持っていることをチェック。
index.js
    new Vue ({
      el: '#app',
      data: {
        object: {
          firstName: '太郎',
          lastName: '田中',
          age: 21
        }
      }
    })
  1. 変数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>
    

    スクリーンショット 2020-10-28 15.15.28.png

    index.html
    タグ使用
      <div id = "app">
        <ul>
          <div v-for="(value, index) in fruits">
            <li>
                ({{index}}){{value}}
            </li>
            <hr>
          </div>
        </ul>
      </div>
    

    スクリーンショット 2020-10-28 15.14.27.png

    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.js
        new Vue ({
          el: '#app',
          data: {
            fruits: ['いちご','ばなな','キウィ']
          },
          methods: {
            remove: function() {
              this.fruits.shift()
            }
          }
        })
    
    1. 3つのテキストボックス内に入力し、先頭削除ボタンをクリック。
    2. 先頭が削除された時、テキストボックス内の文字列がずれてしまう。
    3. これはVue.jsが効率の良いアルゴリズムでレンダリングした結果。
    4. 要するに効率を優先する事で、ズレが起きてしまう

    どうすれば良い??

    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.js
        new Vue ({
          el: '#app',
          data: {
            fruits: ['いちご','ばなな','キウィ']
          },
          methods: {
            remove: function() {
              this.fruits.shift()
            }
          }
        })
    

    keyとして、毎回レンダリングで代入される変数fruitを指定

    keyとして、indexは指定できない。ズレが生じる

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

【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に追加する

javascript
document.getElementById("dog").classList.add("Pomeranian")
// class="chiwawa shiba Pomeranian"

document.getElementById("dog")
→ id="dog" の要素を見つけてくれる。

classList.add("Pomeranian")
→ ここで、Pomeranian が class に追加される。

classから削除する

javascript
document.getElementById("dog").classList.remove("chiwawa")
// class="shiba Pomeranian" 

classList.remove("chiwawa")
→ chiwawa が class から削除される。
先ほど追加した Pomeranian と、もともと class にあった shiba だけが残る。

<便利> 指定したclass名があったら削除、なかったら追加

javascript
document.getElementById("dog").classList.toggle("Pomeranian")
// class="shiba" 

classList.toggle("Pomeranian")
→ class に Pomeranian があるため、削除される。

javascript
document.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>
例:javascript
document.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 の読み込み順を入れ変えてあげれば解決。

HTMLの読み込みが完了してから実行

javascript側で、HTMLの読み込みが終わってから実行するようにする方法。

javascript
window.onload = function () {
  document.getElementById("dog").classList.add("Pomeranian");
  // HTMLの読み込み後に実行されるため、id="dog"が見つけ出せる。
};

参考

Element.classList
 classList の使い方が詳しく載っている。

【JavaScript】classListの使い方まとめ(add.remove.contains.toggle)
 ボタンを押したら色が変わることを例に、実用的かつ分かりやすい。

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

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

Dockerfile
FROM node:12.16.1
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

Dockerfile編集後は保存を忘れずにお願いします。
また、ターミナルで編集しても良いです。

terminal
touch Dockerfile && \
echo 'FROM node:12.16.1' >> Dockerfile && \
echo 'RUN mkdir -p /usr/src/app' >> Dockerfile && \
echo 'WORKDIR /usr/src/app' >> Dockerfile

Dockerfile の準備ができたら、初期のDocker イメージを作成します。
今回は nuxt-test という名前のイメージを作成します。
これから開発するアプリケーションの名前を入れると良いでしょう。

terminal
docker build -t nuxt-test .

Docker イメージが作成できたらイメージからコンテナを作成し、コンテナ内に入ります。
コンテナ名は ctr-nuxt-test としています。

terminal
docker 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)が空になっているか確認します。
空でない場合でも、削除して問題なければディレクトリを削除します。

terminal
ls -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.bash
docker-compose.yml
version: '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.bash
yarn install && \
yarn dev

これでいよいよ準備完了です!実行の確認をしましょう。

(初回)
docker-compose up --build

(2回目以降)
docker-compose up

立ち上がったかどうか、localhost:3000 で確認してください!
スクリーンショット 2020-10-28 15.41.16.png

自分で指定したプロジェクト名が表示されていればdocker compose での起動が成功です!

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

ワイ「TypeScriptのエラーが読めない」

前回の記事の続きです。

TypeScriptが気に入ったワイ

ワイ「TypeScript、素敵やん」
ワイ「だって、ミスったコードを書くと・・・」

スクリーンショット 2020-10-28 10.11.07.png

ワイ「↑こんな感じで、コンパイラが教えてくれるから」
ワイ「だいぶバグ発生率を抑えられそうや〜ん」

しかし

ワイ「おっ」
ワイ「またエラーメッセージが出たで」

スクリーンショット 2020-10-28 12.13.02.png

ワイ「ファッ!?
ワイ「今度は英語やないか」
ワイ「しかもちょっと長い!」
ワイ「意味わからんで」
ワイ「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歳)'

ワイ「↑こんな感じの、プロフィール用のテキストを返してくれるっていう」
ワイ「そんな関数や」
ワイ「でも、ここでさっきの・・・」

スクリーンショット 2020-10-28 12.28.21.png

ワイ「↑訳わからんエラーが出てもうたんや・・・」
ワイ「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というプロパティの型が合ってまへんねん!
文字列型の値は、数値型にはぶち込めまへん!

ワイ「なるほど」
ワイ「要約すると・・・」

takashiagestringだから、アカン!

ワイ「ってことか」

const takashi = {
  name: 'たかし',
  age: '36'
}

ワイ「あ、ほんまや」
ワイ「takashiage文字列になってしまってるわ」
ワイ「User型のagenumberのはずやのに」

ハスケル子「そういうことですね」
ハスケル子「一行ずつちゃんと読めば、意外と簡単ですよね」

ワイ「まあ、なんとかなりそうやな」

ハスケル子「具体的に修正すべきポイントは一番下の行に表示されているので」
ハスケル子「まずは一番下の一行を読むのも良いと思います」

ワイ「ほんまや」

できるだけ型を書こう

ハスケル子「あと、takashiにも明示的に型を書いたほうが良いですよ」

ワイ「どういうこと?」

const takashi: User = {
  name: 'たかし',
  age: '36'
}

ハスケル子「↑こういうことです」
ハスケル子「こうしておくと・・・」

スクリーンショット 2020-10-28 14.51.54.png

ワイ「おお、takashiageがアカンってことが一目瞭然やな」

ハスケル子「そうなんです」
ハスケル子「型情報を書かなくても、ある程度はTypeScriptくんが気を利かせてくれるんですが」

takashiUser型の値です!」
「私はそういうつもりでこのコードを書いてます!」

ハスケル子「っていう風に、能動的に型を書いて宣言したほうが」
ハスケル子「より正確なエラーメッセージを表示してもらえる可能性が高くなりますね」

ワイ「なるほどなぁ」

まとめ

Hoge型の引数としてfugaをぶち込むことはできまへん!
piyoというプロパティの型が合ってまへんねん!
aaa型の値は、bbb型にはぶち込めまへん!

  • TypeScriptでは↑この形式のエラーメッセージが表示されることが多い。
  • 英語だし、文が長めだし、怯みそうになるけど、一行ずつ読めば意外と何とかなる。
  • 型は能動的に書いていこう。そのほうがエラーの意味が分かりやすくなるよ。

ワイ「↑ってことやな!」

ハスケル子「ですね!」

〜おしまい〜

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

【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);

出力結果
色の詳細は黒と白です
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactを実際に動かしてみる

create-react-appを導入するところまではやったので、これからReactを使って、どうやってwebページを表示させるのかのアウトプットやります

本番環境で公開するフォルダの作成

ターミナル
npm run build

これでアプリケーション内にbuildフォルダが作成されました。

その後

ターミナル
npm start

を入力するとブラウザが立ち上がる

スクリーンショット 2020-10-28 10.51.46.png

Edit src/App.js and save to reload
と書いてある通り
srcフォルダの中にApp.jsというファイルがあると思います。

App.js
import 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.js
import 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をもっと勉強するべきか、、、

うーん、わからん!!
どっちが良いのか誰かエロい人教えて下さい!!(>人<;)

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

【JS】Chromeでは動作OK。だが、iOS、safariでReferenceError!?!?

エラー内容

sample.js
let a = xxxxx.join('');
ReferenceError: Can't find variable: xxxxx

Chromeでは、出なかった参照エラー。
ブラウザ特有のエラーということで、「safariのみ ReferenceError JS」などのワードでググってみましたが、jQueryのクリックイベントのDOM参照方法であったり、cssのcursor: pointer;を足してみるであったり、自分のエラーと本質的に異なるものばかりで諦めていました。

また、clickイベントで出たーエラーでだったので、イベントが発生するところとしない箇所でconsoleで確認してみたりと時間をかなり掛けました。

しかし、原因はとても単純でした・・・

解決策

変数宣言を変えるだけ。

sample.js
var rivalNumText = xxxxx.join('');

まさかのvarに変えるだけでした。。。

原因は結局??

varよりも、letの方が比較的新しいということで使用していました。
しかし、letはSafariやIE、iOS、またAndroidのブラウザではサポートされていないみたいです。

動作スピード的にはletやconstを使用する方が早いみたいですが、ブラウザ対応の面で考えるとその点についても考慮する必要があるみたいです。

●<コメントより修正>
letが使用できないのは、自分自身の環境の問題の可能性もあります。
対応しているブラウザであれば、ブラウザの更新でletを使用できるよう解決できます。

今回で言えば、letがどのブラウザで使えるのかこちらのサイトから確認できるみたいです。

参考

JavaScriptのvarとletについて

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

ワイ「TypeScriptなんも分からん」

TypeScriptなんも分からんワイ

ワイ「なぁ、ハスケル子ちゃん」

ハスケル子「はい」

ワイ「最近、TypeScriptっていうのが流行ってるみたいやけど」
ワイ「何がええの?」
ワイ「普通のJavaScriptじゃアカンの?」

ハスケル子「うーん」
ハスケル子「じゃあ、VSCodeでコードを書きながら説明しますね」
ハスケル子「例えば・・・」

sample.js
const titles = ['記事タイトル1', '記事タイトル2', '記事タイトル3']

const newestTitle = titles.pop()

console.log(newestTitle)

ハスケル子「↑このコードは正しく動くと思いますか?」

ワイ「ええと」
ワイ「まずはtitlesという変数に、記事タイトルが3つ入った配列を格納するんやな」
ワイ「そんで、配列が持ってるpop()メソッドを実行して」
ワイ「一番最後の要素を取得しとるわけか」
ワイ「ほな、newestTitleという変数には'記事タイトル3'が格納されるな」
ワイ「せやから、コンソールには'記事タイトル3と表示されるはずや」
ワイ「問題ないな」
ワイ「これは、正しく動くコードやで!」

ハスケル子「そうですよね」
ハスケル子「じゃあ次は・・・」

sample.js
const 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なんやね」
ワイ「はい、書きましたやで」

スクリーンショット 2020-10-28 10.31.38.png

ワイ「おお、popの部分に赤い波線が出とるな」

ハスケル子「文字列はpop()メソッドを持っていないので」
ハスケル子「そういうあり得ないことをしようとすると」
ハスケル子「エディタが教えてくれるんです」

ワイ「なるほどな」
ワイ「ブラウザ上でポチポチ動作テストせんくても」
ワイ「コードを書いとる段階で気づけるわけやね」

ハスケル子「はい」
ハスケル子「あとですね、赤い波線が出ているpopの部分に」
ハスケル子「マウスをホバーさせてみてください」

スクリーンショット 2020-10-28 10.11.07.png

ワイ「おお、詳細なエラーメッセージが表示されたで」
ワイ「なんや、TypeScriptって親切なやつなんやな」

ハスケル子「そうなんですよ」
ハスケル子「あり得ないことをしてundefinedエラーが出るのを、事前に防いでくれるわけです」

ワイ「なるほどな」
ワイ「小さめのサイトなら、気をつけてコーディングすれば」
ワイ「バグは出ないかもしれんけど」
ワイ「大規模なサイトだと、どっかでミスは起きるから」
ワイ「TypeScript君に見つけてもらえると」
ワイ「だいぶバグを減らせそうやな」

ハスケル子「はい」
ハスケル子「ほかにもVSCodeくんから受けられる恩恵がだいぶ増すので」
ハスケル子「かなりメリット大きいです」

ワイ「ハスケル子ちゃん、ありがとうやで」
ワイ「ワイもTypeScript勉強してみるわ!」

ハスケル子「TypeScriptは、JavaScriptに変換しないとブラウザ上で動かないので」
ハスケル子「変換するための環境構築とかも勉強してくださいね!」

ワイ「おう!」

続編もよろしくやで!

ワイ「TypeScriptのエラーが読めない」

勉強におすすめのサイト

TypeScript Deep Dive 日本語版

とりあえず触ってみたい人におすすめのサイト

TS Playground
ブラウザ上でTypeScriptを書いてみられるサイト

セットアップ | Nuxt TypeScript
Nuxt.js(Vue)で使ってみたい人はこちら

TypeScript Next.js example
Next.js(React)で使ってみたい人はこちら

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

【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が使える範囲
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

乳幼児の予防接種管理ツールを作った。

乳幼児の予防接種スケジュール管理ツールを作りました!
初めてオリジナル企画で作ったサイトです。
カレンダーから日付を入力して、そこから計算した指定の日付を表示させるだけなので、開発と呼ぶにはおこがましい程のレベルのものですが、まぁ作ったよね。ww
jQueryとMoment.jsを使用。この時初めて、静的なサイトであれば、S3にアップロードするだけでサーバが要らないという事実を知りました!(*'ω'*)

↓↓ こんなツールです。お子さんのお誕生日を入力すると、いつ頃予防接種に行けばいいのかを自動表示します。
image.png

予防接種スケジューラー ← こっからサイトに飛んでください~。

私自身、1歳10ヶ月と8ヶ月の息子が2人いるのですが、予防接種の種類や回数が多くて、ちゃんと管理できるか不安だったんですね。そんな時に長男を産んだ産院で、今回作ったツールのような管理表をもらいまして、それがすっごい便利~!と思ったのでWebバージョンを作ろうと思ったわけです。
ただ、色々と調べたのですが、各市区町村によって定期接種対象のワクチン等が微妙に違う(!?)みたいで、埼玉県の予防接種情報を元に作っています。ご了承ください。(m´・ω・`)m ゴメン… (でも、あまり大きな違いはなかったので、埼玉県外の方も十分使っていただけると思います!)

小さいお子さんを持つ方はぜひ使ってみてください!
また、お知り合いでいらっしゃる方はぜひ紹介してみてください~。宜しくです。( `・∀・´)ノヨロシク

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

[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.allreduceが途中に入るので、何をやっているかが分かりにくい書き方になっています。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のメソッドが増えても、この方式なら対応可能だと思います。

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

音声合成で しゃべらせる 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

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

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"
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.

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

【初心者向け】HTML・CSSのセルフチェック方法のポイント

どうも7noteです。一通りコーディングが終わった後の最後のチェック方法について

やっとこさコーディングが完了したと思って油断したら、後から「よく見たらなんか崩れてる」なんてことも。

そうならないためにもセルフチェックのスキルはとても重要です。特に1人で開発している人は自分が見落とせばもう取り返しはつかないので、必須スキルになります。

でも始めたての頃はどのように何をチェックしたらいいのかわからない方も多いと思うので、初心者の方が抜けやすいポイントを抑えながら、基本的なチェック方法を確認していきます。

コーディングのセルフチェック方法

〜必須チェック項目!〜

  1. デザインと見比べて差異がないか目視でチェック
  2. aタグなどのリンク先が抜けてたり間違いがないか。
  3. 画像のaltに抜けがないか。
  4. テキストのコピペミスや誤字脱字がないか。(英語のスペルミスもチェック)
  5. javascriptがきちんとすべて動作しているかチェック
  6. 異なるブラウザ(特にIE)で見ても綺麗に見えるか。
  7. マウスホバー時の処理が適切にできているか。
  8. タグ構造に間違いがないか。(タグの閉じ忘れ等。)
  9. ダミーの情報やすかし入りの画像が使われていないか。
  10. フォームのあるサイトなら、ちゃんとメール送信ができているか。
  11. ウィンドウ幅を大きくしたり小さくしたりしてレイアウトが崩れないか。

〜レスポンシブサイトならチェックしておきたい事〜

  1. 必ず実機のスマホでチェック。とくにiPhone。(inputやselectのデザインが変わってしまうので注意。)
  2. 幅指定の影響で、横スクロールができてしまわないかチェック。
  3. 画像がぼやけてないかチェック。
  4. 検証ツールを使って、幅320pxのような画面の小さいスマホで見ても崩れないか。
  5. タップするところが押しやすい作りになっているか。

〜細かいところもしっかりチェック!!!〜

  1. aタグのクリック範囲は適切か。
  2. javascriptは後で見た時に何をしているかわかりやすくコメントを残しているか。(必須ではない)
  3. 使っていないゴミclassが残っていないか。
  4. CSSはショートハンドをしっかり使っているか。
  5. marginの相殺が起こっていないか。
  6. faviconogp画像は設定されているか。
  7. 「&」を「&amp;」で記述できているか。
  8. 上下中央に配置した要素は、しっかりドンピシャで上下中央ですか?

〜SEO対策をするなら・・・〜

  1. titleタグやmeta情報はきちんと設定されているか。
  2. アナリティクスのタグは入っているか。
  3. サーチコンソールは設定しているか。
  4. 画像イメージを圧縮などして、最適化しているか。
  5. サイトマップは作成しているか。
  6. robots.txtの記述が適切か。
  7. canonicalの設定がきっちりできているか。

ポイント解説

チェックする時は必ず目視でチェックしましょう!ツールを使ってデザインデータと重ねてチェックする方法などもあるので、より正確にチェックをするのであればそのようなツールを使うことをおすすめします!
関連記事→「1pxのズレもなくコーディングするための便利ツール

またよくやってしまいがちなのがwidth指定が適切でなく、横スクロースバーが出てしまう現象です。
特にスマホで見た時に対処していないと見た人が下にスクロールしたつもりが斜め下方向に移動してしまい、見たい箇所が見切れてしまうということが起きます。
些細なことですが、小さな気配りができていないためユーザーが不快な思いをしてせっかくみていたページを閉じてしまうかも!!!

いざ完成して見直ししていると、たまに使っていないclassやCSSの指定が残っていたりします。
数カ月後に見直した時に、なんの役割を果たしているのかわからなくなってしまうので、ゴミclassは忘れずに削除しましょう

まとめ

きっと他にも様々なチェックポイントがあります。ただ、人によって癖がかならずあるので一度抜けたりミスしたことはメモを取っておく事をおすすめします。
とったメモを「公開前のチェックシート」等の形式でまとめておけばミスがあっても安心して修正対応ができます!

どのようなところにミスがあるのか検討をつけないままチェックをしていても、何度も同じところを見たり、あっちを開いたりこっちを開いたり時間ロスにも繋がってしまうので、自分なりのチェックの手順や順番を組み立てていくのがいいかなと思います!

慣れてくれば、必然的に組み込んでいる最中に気を使いながらコーディングができるようになるので、チェック項目が多くても短い時間でチェックが完了できるようになるので、「質より量!」という意気込みでどんどん場数を増やしていきましょう。

おそまつ!

~ Qiitaで毎日投稿中!! ~
【初心者向け】HTML・CSSのちょいテク詰め合わせ

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