20201026のJavaScriptに関する記事は30件です。

Riot.js フォームの扱い

Riot.js

Riotでのフォームの扱い方を紹介します。

DOM要素に直接アクセス

おそらく一番簡単でわかりやすい方法です。フォームがちょっとでも複雑になるとコードがカオスになりがちなので実務ではあまり使いません。
サッと簡単なフォームを作る時にはこれで十分ですね。

<app-form>
  <form onsubmit="{ handleSubmit }">
    <input type="email" name="email" placeholder="メールアドレス" />
    <input type="password" name="password" placeholder="パスワード" />
    <input type="checkbox" id="password_visible" onchange="{ handleCheckChange }">
    <label for="password_visible">パスワード表示</l1abel>
    <button>送信</button>
  </form>
  <script>
    export default {
      handleCheckChange(e) {
        this.$('[name=password]').type = e.target.checked ? 'text' : 'password'
      },

      handleSubmit(e) {
        e.preventDefault()
        const email = this.$('[name=email]').value
        const password = this.$('[name=password]').value
        console.log({ email, password })
      }
    }
  </script>
</app-form>

form要素から値を取得

送信するだけのフォームならおすすめです。
DOMを検索しない分上の方法よりパフォーマンスはいいのですが、誤差ですね?

<app-form>
  <form onsubmit="{ handleSubmit }">
    <input type="email" name="email" placeholder="メールアドレス" />
    <input type="password" name="password" placeholder="パスワード" />
    <button>送信</button>
  </form>
  <script>
    export default {
      handleSubmit(e) {
        e.preventDefault()
        const fields = e.target.elements
        const email = fields['email'].value
        const password = fields['password'].value
        console.log({ email, password })
      }
    }
  </script>
</app-form>

stateにフォームの値を持つ

冗長だけど凝ったことをするならこれ一択ですね。
自分はフォームを作るとき大体あとから複雑になってくるので、最初から全部この方法で統一しています。

<app-form>
  <form onsubmit="{ handleSubmit }">
    <input type="email" name="email" placeholder="メールアドレス" oninput="{ handleEmail }" value="{ state.email }" />
    <input type="{ state.passwordType }" name="password" placeholder="パスワード" oninput="{ handlePassword }" value="{ state.password }" />
    <input type="checkbox" id="password_visible" onchange="{ handleCheckChange }">
    <label for="password_visible">パスワード表示</l1abel>
      <button>送信</button>
  </form>
  <script>
    export default {
      state: {
        email: '',
        password: '',
        passwordType: 'password'
      },

      handleEmail(e) {
        this.state.email = e.target.value
        this.update()
      },

      handlePassword(e) {
        this.state.password = e.target.value
        this.update()
      },

      handleCheckChange(e) {
        this.state.passwordType = e.target.checked ? 'text' : 'password'
        this.update()
      },

      handleSubmit(e) {
        e.preventDefault()
        console.log(this.state)
      }
    }
  </script>
</app-form>

さすがにフィールドごとにイベントハンドラーhandle...を用意するのは冗長すぎるので、普段は一つのハンドラーで工夫しています。

-    <input type="email" name="email" placeholder="メールアドレス" oninput="{ handleEmail }" value="{ state.email }" />
-    <input type="{ state.passwordType }" name="password" placeholder="パスワード" oninput="{ handlePassword }" value="{ state.password }" />
+    <input type="email" name="email" placeholder="メールアドレス" oninput="{ handleChange }" value="{ state.email }" />
+    <input type="{ state.passwordType }" name="password" placeholder="パスワード" oninput="{ handleChange }" value="{ state.password }" />
...

-      handleEmail(e) {
-        this.state.email = e.target.value
-        this.update()
-      },
-      handlePassword(e) {
-        this.state.password = e.target.value
-        this.update()
-      },
+      handleChange(e) {
+        this.state[e.target.name] = e.target.value
+        this.update()
+      },

その他

name属性がないフィールドも扱う

<awesome-color-picker value="" on-change="" />という感じのname属性のないカスタムなコンポーネントがある時 + なるべくhandleChange一つにまとめたい

<app-form>
  <form onsubmit="{ handleSubmit }">
    <awesome-color-picker on-change="{ color => handleChange('color', color) }" value="{ state.color }" />
    <input type="text" oninput="{ e => handleChange('text', e.target.value) }" value="{ state.text }" />
  </form>
  <script>
    export default {
      state: {
        text: '',
        color: '#ffffff'
      },

      handleChange(name, value) {
        this.state[name] = value
        this.update()
      },

      handleSubmit(e) {
        e.preventDefault()
        console.log(this.state)
      }
    }
  </script>
</app-form>

{ e => handleChange('text', e.target.value) } ← 好みの問題ですがこれが「なんか気持ち悪い...」なら

-    <awesome-color-picker on-change="{ color => handleChange('color', color) }" value="{ state.color }" />
-    <input type="text" oninput="{ e => handleChange('text', e.target.value) }" value="{ state.text }" />
+    <awesome-color-picker on-change="{ handleChange('color') }" value="{ state.color }" />
+    <input type="text" oninput="{ handleChange('text') }" value="{ state.text }" />
...

-      handleChange(name, value) {
-        this.state[name] = value
-        this.update()
-      },
+      handleChange(name) {
+        return e => {
+          this.state[name] = e.target ? e.target.value : e;
+          this.update();
+        }
+      },

こっちの方がすっきりする気がします。

フォームが複雑になってくるとイベントハンドラ一つでカバーできないときもあるので、無理に一つにまとめずに複数のイベントハンドラを使いましょう。自分は単純なものは上にあったようなhandleChangeを使い、それ以外のものは個別にイベントハンドラを作ります。

最後に

質問などがあれば気軽にコメントしてください!(^^♪

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

アニメ情報のWebアプリ開発してみたけど上手くいかず、、(要改善)

出来たもの

「検索」で、指定した年度・クールごとのアニメ番組一覧を表示してくれるWebアプリ。
 ※実際に動くものはコチラにアップしてます。

利用ライブラリ

 ・Vue.js
  CDN:https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js
 ・Bootstrap
  CDN:https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js
  CDN:https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.3/js/bootstrap.min.js
 ・axios
  CDN:https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.0/axios.min.js
 ・ShangriLa Anime API

やりたかったこと

新アニメが始まった時にとりあえず1話見てみて、減らしていくことが多い。(1話すら見ないこともあるが。)
始まってすぐだと、タイトルだけだと分からないことが多いので、自分用で優先順位を管理できるようなWebアプリを作ってみたかった。
 ・年度、クールでアニメ番組を取得して取捨選択。
 ・優先順付けて並び変え。または削除。
みたいなことをやりたかった。

出来なかったこと

四苦八苦の様子、参考にしたURLなどは自分用メモとして保管してます。
Webサイトのサムネイル画像を取得して表示したかったが、上手くいかず。。
色々調べて試してみて、最終的にはWebページのOGPイメージ(og:image)を取得すれば良い考えにシフトしたが時間が足りなくイメージ画像の取得が上手くいってません。

改善点(今後に期待)

今回時間なくて断念したが、色々改造したい。
 ・ちゃんとサムネイル画像を表示できるようにしたい。
 ・ビジュアルをもっとかっこよくしたい。
 ・番組の曜日、時間、PV再生など追加したい。
 ・検索結果の保存。
 ・カードの並び変えや削除。

コード

html
<!-- 全体をVue.js有効にする -->
<div class="container text-center text-white bg-dark" id="app">

  <!-- タイトル行 -->
  <div class="row my-3">
    <div class="col-sm-6 mx-auto"><h1>ANIME Watch</h1></div>
  </div>

  <!-- 検索フォーム -->
  <div class="form-inline justify-content-center">
    <input v-model:value="Year" placeholder="2020" class="form-control">
    <label class="col-sm-1"></label>

    <select v-model:value="Cours" class="custom-select">
      <option></option>
      <option></option>
      <option></option>
      <option></option>
    </select>

    <div class="col-sm-2">
      <button v-on:click="Search" class="btn btn-primary">検索</button>
    </div>
  </div>

  <!-- 全てのタスクをクリアするボタン -->
  <div class="row my-3">
    <div class="col-sm-6 mx-auto">
      <button v-on:click="clearAll" class="btn btn-danger">検索結果のクリア</button>
    </div>
  </div>

  <!-- タスク追加されると表示される部分 -->
  <div class="flex-container">
      <p v-for="Anime in AniData">{{Anime.Title}}<br>
      <a v-bind:href="Anime.WebUrl" target="_blank">
        <img v-bind:src="Anime.ImgUrl" width="250" height="150"></img>
      </a>
      </p>
  </div>
</div>
js
const app = new Vue({
  el: '#app', // Vueが管理する一番外側のDOM要素

  data: {
    // Vue内部で使いたい変数は全てこの中に定義する
    Year: '2020',  // 年
    Cours: '',   // クール
    Animes: '',    // APIで取得したアニメ情報JSON
    AniData: [],   // 表示するアニメ情報配列
  },
  methods: {
    // 関数はココに記述
    Search: async function() {
      // 検索時の関数
      try {
        // クール値を取得
        let CoursNo=4;
        switch (this.Cours){
          case '': CoursNo=1; break;
          case '': CoursNo=2; break;
          case '': CoursNo=3; break;
          case '': CoursNo=4; break;
        }

        // ShangriLa Anime API Server
        let response;
        response = await axios.get(`https://api.moemoe.tokyo/anime/v1/master/${this.Year}/${CoursNo}`);
        this.Animes = response.data;

        // アニメ情報表示用配列作成
        this.AniData = [];
        for (var item in this.Animes) {
          var data = {Title: this.Animes[item].title, 
                      WebUrl: this.Animes[item].public_url,
                      ImgUrl: ""};
          this.AniData.push(data);
        }

        /*
        // OGP取得 上手くいかないからあきらめ。
        const CORS_PROXY = 'https://cors-anywhere.herokuapp.com/'
        for await (var item of this.Animes) {
          await fetch(CORS_PROXY + this.Animes[item].public_url)
            .then((res) => res.text())
            .then((text) => {
              const el = new DOMParser().parseFromString(text, 'text/html')
              const headEls = el.head.children
              Array.from(headEls).map((v) => {
                //console.log(v)
                const prop = v.getAttribute('property')
                if (!prop) return
                if (prop == "og:image") {
                  //console.log(prop, v.getAttribute('content'))
                  var ogimg = v.getAttribute('content')
                }
              })

              var data = {Title: this.Animes[item].title, 
                          WebUrl: this.Animes[item].public_url,
                          ImgUrl: ogimg};
              this.AniData.push(data);
            })           
          }*/
      } catch (error) {
        console.error(error);
      }
    },

    clearAll: function() {
      // 表示クリア時の関数
      this.AniData = [];
      console.log('クリア');
    },
  },
});
css
.flex-container {
  display: -webkit-flex;
  display: flex;
  -webkit-justify-content: center;
  justify-content: center;
  -webkit-align-items: center;
  align-items: center;
  flex-wrap: wrap;
}

感想

Web開発は初でしたが、色々勉強にはなったかな。。
経験積んで、技術力を上げたいと思う。(Web開発は非常に興味あるので。)

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

デバックの方法 〜初学者向け

はじめに

 まだ苦手意識があるJS。コンソールのエラーがなかなか読み取れない。そこで、本日の学習でデバックのやり方が少し身についたので、記録しておく。

基本パターン

debuggerをコードに入れておけば、そこで強制的に処理を止めてくれる。Railsで言うbinding.pryのようなもの。
どの辺りでエラーが出ているのか、エラー文を見ればわかるが、debuggerを使えば、行ごとに細かく検証していくことができる。

変数定義ができているかの確認

JSでDOMを操作するときはとにかく変数に入れることが多いと感じる。その時々で、ちゃんと変数に意図したものが入っているのか確認する必要がある。そんなときは、cosole.log(変数名)でブラウザのコンソールに出力させる。そのときに、nilと出れば、変数定義されていないことに気付くことができる。

最後に

 本日の学習では、この2つのパターンを使って、エラーを解決することができた。ファイルの読み込みはできているのか、イベント発火は正常かなど、細かく検証していくことが大切だと感じた。

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

JSエコシステムぶらり探訪(4): npmとコマンドライン

前回に続きnpmの機能について扱います。今回はnpmとコマンドラインツールとの関わりを中心に見ていきます。

←前 目次

注意: Windowsとそれ以外では、npmのフォルダ配置は異なります。Windowsでの挙動についてはnpm-foldersを参照してください。

グローバルインストール

npmは通常、Node.jsの配布物に同梱されていますが、yarnは同梱されていません。yarnを使う場合は次のようなコマンドを実行します。

npm install -g yarn

これによって以下のような効果が発生します。

  • $PREFIX/lib/node_modules/yarn 以下にyarnの中身がインストールされる。
  • $PREFIX/lib/node_modules/yarn/node_modules 以下にyarnの依存関係がインストールされる。
  • $PREFIX/bin にシンボリックリンク yarn, yarnpkg が生成される。どちらも ../lib/node_modules/yarn/bin/yarn.js を参照している。
    • これはyarnの package.json の "bin" フィールドの記述に基づいて生成される

$PREFIX/bin にはNode.jsの実行バイナリ (node) も入っているので、Node.jsが使えている時点でここにPATHは通っていると仮定してよさそうです。

$PREFIX/lib/node_modules/yarn/bin/yarn.js は実行可能属性が付与されていて、冒頭は以下のようになっています。1

#!/usr/bin/env node

/* eslint-disable no-var */
/* eslint-disable flowtype/require-valid-file-annotation */
'use strict';

つまり、nodeにパスが通った状態で yarn を実行すると node $PREFIX/bin/yarn が実行されます。Node.jsはモジュール解決前にシンボリックリンクを解決する2ので、これは node $PREFIX/lib/node_modules/yarn/bin/yarn.js と同じ意味になります。 (yarn.js を起点に require が解決される。)

npm install -g のインストール先は $PREFIX/lib/node_modules であって $PREFIX/lib/node ではありません。Node.jsの探索ルールに含まれているのは前者ではなく後者です3から、これによってライブラリをインストールしても require から使われることはありません。

yarnの場合

  • npm install -g のかわりに yarn global add を使います。
  • インストール先はnpmとは異なり、バイナリは ~/.yarn/bin に、パッケージ本体は ~/.config/yarn/global にそれぞれインストールされます。そのため、npmと違い、node用のパスとは別にPATHを通しておく必要があります。
  • また、上記のインストール先はyarnでは変更可能です。

npm installの古い挙動

-g をつけない場合、 npm install はローカルインストールの挙動になります。

npm v4までは、npm installはデフォルトでは ./node_modules への展開のみを行い、 package.json を更新しませんでした。そのため、 npm install -S と書くのが一般的でした。npm v5以降では -S が自動的に仮定されます。

アプリケーションのローカルインストール, yarn exec, npx

現代のJavaScript開発ではprettier, eslint, typescript (tsc), webpackなど多くのCLIツールを使います。これらはグローバルインストールすることもできますが、以下のような懸念があります。

  • これらのツールのバージョンが開発者ごとにバラバラだと、再現性の低いトラブルに遭遇しやすくなる。
  • eslintやwebpackはプラグインも含めて使うことが多く、これらを含めた全てのパッケージを個別にインストールさせるのはセットアップの手間につながる。

このため、プロジェクトで使うアプリケーションは package.json に指定してローカルインストールほうが主流になっています。方法は簡単で、ライブラリと同様に npm install (-S) または yarn add するだけです。

グローバルインストール時には $PREFIX/bin 以下にシンボリックリンクが作成されますが、ローカルインストールの場合は同様に ./node_modules/.bin 以下にシンボリックリンクが作成されます。通常このディレクトリにはPATHは通っておらず、 ローカルインストールされたアプリケーションは、通常のコマンドと同様に呼び出すことはできないため、npm/yarnを経由して使います。以下のコマンドを経由して使われるのが一般的でしょう。

  • npx (npm exec) / yarn exec
  • npm run / npm run-scripts / yarn run (run が省略された場合も含む)

yarn exec

yarn exec はyarn共通のセットアップを行ったあと所与のコマンドを実行します。このセットアップには PATH の設定が含まれているため、ローカルインストールされたアプリケーションの実行が可能です。

yarn exec prettier -w 'src/**/*.js'

npx / npm exec

npx / npm execyarn exec と同様の目的で使用することができますが、ローカルインストールされたパッケージがない場合は自動的にnpmレジストリからパッケージを探し、一時的にインストールしてから実行します。

npx prettier -w 'src/**/*.js'

prettier がない状態で上記を実行すると、一時的なインストールが行われます。 ~/.npm/_npx/ 以下に prettier への依存が記述されたダミーパッケージが作られ、そこで npm install が行われてからローカルパッケージが実行されます。

npx / npm exec をサポートするバージョンは以下の通りです。

  • npm exec はnpm v7以降に存在します。挙動は npx とほぼ同じです。
  • npx はnpm v5.2.0以降に同梱されていますが、独立した npx パッケージとしても提供されています。
    • v5.2.0~v6に同梱されている npx の実装は libnpx パッケージを使っているため、 npx パッケージと共通です。
    • v7に同梱されている npxnpm exec のエイリアスです。

yarn v2 (berry) にはこれに対応する yarn dlx が組み込まれていますが、本記事では詳しくは述べません。

scripts

npm run / npm run-script / yarn runpackage.json に記述されたスクリプトを実行します。つまり、 npm/yarnには簡易的なタスクランナーとしての機能があるといえます。

package.json
{
  "scripts": {
    "build": "tsc",
    "test": "jest",
    "fmt": "prettier -w src/**/*.ts"
  }
}

npm runnpm run-script のエイリアスです。また、曖昧性がない場合は yarn runrun は省略できます。 (yarn build / yarn fmt など)

scripts 内のスクリプトは ./node_modules/.bin にパスが通った状態で実行されるため、 node_modules/.bin/webpack のように明示する必要はありません。

npm/yarnの管理下でコマンドが実行されるときは、PATH以外にも NODEnpm_lifecycle_event, npm_config_registry などいくつかの環境変数がセットされます。これについては npm-run-scriptnpm-config などのマニュアルを参照してください。

npm run / yarn run ともに、後続引数はスクリプト文字列の末尾に連結されます。たとえば、

package.json
{
  "scripts": {
    "lint": "eslint 'src/**/*.ts'"
  }
}

という記述があるとき、 yarn lint --fixeslint 'src/**/*.ts' --fix を実行します。

ライフサイクルスクリプト

scripts で定義されるスクリプトの中には特別な意味を持つものがあります。これらをライフサイクルスクリプト (lifecycle scripts) と呼びます。ライフサイクルスクリプトを使うとnpmの処理にフックをかけることができます。

ライフサイクルスクリプトのうち重要なのは以下の2種類です。

  • prepublish / prepublishOnly / prepare / prepack / postpack / publish / postpublish ... パッケージをレジストリに上げるときに呼ばれます。
  • preinstall / install / postinstall ... パッケージがインストールされるときに呼ばれます。「依存関係として」「ローカルパッケージとして」「アプリケーションとして」の区別を問いません。

原則として、「preのついているフック」→「フック対象の処理」→「接頭辞なしのフック」→「postのついているフック」の順で呼ばれます。たとえばinstallの場合、以下の順番に呼ばれます4

  • preinstall フック
  • npmが行うインストール処理
  • install フック
  • postinstall フック

上記の重要なライフサイクルスクリプトの関係をまとめたのが以下の図です。

図: 重要なライフサイクルスクリプト

ただし、prepublish以下のフックは様々な事情からサポート状況がまちまちです。以下の表を参照してください。

npm2 npm3 npm4 npm5 npm6 npm7 yarn
prepublish (pack / publish) ※1
prepublish (git install)
prepublish (local install)
prepare (pack / publish) ※2
prepare (git install)
prepare (local install)
prepublishOnly
prepack / postpack (pack / publish)
prepack / postpack (git install) × ×
(pre-, post-)shrinkwrap

※ バグと思われるものには×をつけている
※1 prepublishは yarn publish では実行されるが yarn pack では実行されない
※2 npm7では、pack/publish内の prepareprepack の直前ではなく直後に実行される

これらの状況を踏まえて、以下のようにスクリプトを配置するのがよいでしょう。

  • トランスパイルが必要なパッケージではトランスパイルを prepack で行う。ただし、git依存関係の prepack 呼び出しは現時点でnpm/yarnともに盛大にバグっているので、将来的なバグ修正に期待するしかないでしょう。
  • ネイティブパッケージのビルドは install で行う。

その他、npmが規定するライフサイクルスクリプトとして以下があります。

  • preuninstall, uninstall, postuninstall ... npm uninstall にフックします。
  • preversion, version, postversion ... npm version にフックします。
  • preshrinkwrap, shrinkwrap, postshrinkwrap ... npm shrinkwrap にフックします。
  • pretest, test, posttest ... npm test のときに呼ばれます。
  • prestart, start, poststart ... npm start のときに呼ばれます。
  • prestop, stop, poststop ... npm stop のときに呼ばれます。
  • prerestart, restart, postrestart ... npm restart のときに呼ばれます。

また、 npm run / yarn run も実際にはpre/postスクリプトを実行します。たとえば npm run buildprebuild, build, postbuild の3つのスクリプトを実行することになります。 (preprebuild などは実行しません) 上に挙げたうちtest/start/stop/restartは単に npm runrun を省略できるケースともみなせます。

また、npmにはいくつか既定のスクリプトが存在します。

  • start: node server.js (server.js というファイルがある場合のみ)
  • install: node-gyp rebuild (binding.gyp というファイルがあり、 install/preinstall がどちらも定義されていない場合のみ)
  • restart: stop してから start するのがデフォルトの挙動です。

なお、yarn v2 (berry) ではライフサイクルスクリプトの扱いが大幅に整理されていて、使えるスクリプトも限定されているようです。詳しくはLifecycle Scriptsを参照してください。

npm link / yarn link

npm/yarnはデフォルトではシンボリックリンクを使いませんが、シンボリックリンク操作のためのコマンドとして npm link / yarn link が存在しています。主に以下の2つの用途があります。

  • 開発中のコマンドラインアプリケーションをグローバルに利用可能な状態にする (npm link のみ)
  • 開発中のライブラリを別のパッケージから利用可能な状態にする (npm link / yarn link)

アプリケーションのリンク

package.json のあるディレクトリで npm link を無引数で実行すると $PREFIX/lib/node_modules 以下に作業中のパッケージ (カレントディレクトリ) へのシンボリックリンクが作成されます。また、 $PREFIX/bin に対応するシンボリックリンクが作成されます。これらのディレクトリは npm install -g が使用しているものと同じなので、実質的にローカルパッケージのコマンドをグローバルに利用可能な状態にしていることになります。

無引数の npm link は「シンボリックリンクであること」以外は npm install -g と同じなので、 npm uninstall -g <パッケージ名> で元に戻せます。 (なお、 npm unlinknpm uninstall のエイリアスです)

ライブラリのリンク (npm link)

npm link にパッケージ名を引数にして実行すると、 $PREFIX/lib/node_modules/$package_name が指していたディレクトリへのシンボリックリンクが ./node_modules/$package_name として作成されます。 (元々 ./node_modules/$package_name に展開されていたファイルは消滅します)

このコマンドは通常、「無引数の npm link」と組み合わせて以下のように使うことが想定されています。

cd /path/to/foo1
npm link
cd /path/to/bar1
npm link foo1 # foo1 は/path/too/foo1/package.jsonに記載のパッケージ名
# 以降はbar1は /path/to/foo1のソースを参照するようになる

この操作の正しい取り消し方法はわかりませんが、以下のようにすると良さそうです。なお、 npm unlinknpm uninstall のエイリアスであり、 npm link の逆を行ってくれるわけではありません。

npm uninstall --no-save $package_name
npm install --force

ライブラリのリンク (yarn link)

yarn link も同様の目的で使うことができます。

  • 無引数の yarn link~/.config/yarn/link/$package_name というシンボリックリンクを作成します。
  • 引数つきの yarn link $package_name./node_modules/$package_name を消し、 ~/.config/yarn/link/$package_name へのシンボリックリンクで置き換えます。
    • npm link が作成するシンボリックリンクは直接参照ですが、 yarn link が作成するシンボリックリンクは間接参照になるようです。

使い方は npm link と同じです。

cd /path/to/foo1
yarn link
cd /path/to/bar1
yarn link foo1 # foo1 は/path/too/foo1/package.jsonに記載のパッケージ名
# 以降はbar1は /path/to/foo1のソースを参照するようになる

yarn link を取り消すには以下のようにします。 (npmとは異なり、 yarn unlinklink の逆をするためのコマンドです)

yarn unlink

yarn link $package_name を取り消すには以下のようにします。

yarn unlink foo1 # foo1 はyarn linkしていた依存関係のパッケージ名
yarn install --force

linkの問題点

linkを使うと、利用側パッケージの動作を確認しながらライブラリを編集できるようになります。しかしNode.jsの require の挙動上、linkした依存関係は通常の依存関係とは異なる挙動をすることがあります。これは以下の2つの理由によります。

  1. 依存元パッケージの node_modules を参照できないこと。
  2. ライブラリ側の node_modules が優先されてしまうこと。

上記の理由により、以下のような現象が発生する可能性があります。

  • 間接依存関係のバージョンが一致しない。 (ライブラリ側が使うバージョンはライブラリ側の package-lock.json / yarn.lock に基づいて決まるため)
  • peerDependenciesに書かれたパッケージが見つからない。 (peerDependenciesは依存元パッケージ側の node_modules に存在するため)
  • 間接依存関係のバージョンが同じで、巻き上げの条件を満たしていても、二重requireが発生する。

node_modules を用いた古典的なパッケージ管理を使っている限り、これらを綺麗に解決するのは難しいですが、 1. については --preserve-symlinks オプションを使うことで緩和できる可能性があります。Node.jsは通常シンボリックリンクを明示的に展開しますが、 --preserve-symlinks が指定されたときはこの挙動がスキップされます。これにより、 ../node_modules../../node_modules を参照するときの親ディレクトリの計算結果が変化し、ライブラリ側から依存元パッケージの node_modules がrequireできるようになります。

gulp / grunt

package.jsonscripts では賄いきれないような複雑な処理 (タスク定義の共通化や依存管理) を定義したい場合は、GulpやGruntのようなタスクランナーを使うことができるようです5。ただし、JavaScriptのプロジェクトで行う必要があるタスクは典型的なもの (トランスパイルやバンドリング) が多く、TypeScriptやWebpackなどそれぞれのツールが依存管理を含めたパイプラインを提供しているため、これらで済んでしまうことも多いでしょう。

まとめ

  • npm install -g / yarn global add を使うと、パッケージをグローバルにインストールし、CLIツールとして使うことができる。
  • npm install / yarn add で追加したパッケージも node_modules/.bin にPATHを通すことでCLIツールとして使うことができる。 npm / yarn の内部で呼ばれるコマンドはこのディレクトリにPATHが通った状態で呼ばれる。
  • yarn exec / npx / npm exec を使うと、任意のコマンドを node_modules/.bin にPATHが通った状態で実行することができる。また、 npx / npm exec に存在しないコマンドを渡した場合は、自動インストールが行われる。
  • npm run (npm run-script) / yarn run を使うと、 package.jsonscripts に登録されたコマンドを実行できる。 yarn runrun は省略できる。
  • scripts の中には特別なコマンド名がいくつかあり、npm/yarnの特定の処理にフックすることができる。どのフックが呼ばれるかはバージョンによる挙動の違いが激しいので注意が必要。
    • ネイティブ拡張のビルドが必要な場合は installpostinstall で行うのがよい。
    • トランスパイルは prepack で行うのがよいが、現状ではnpm/yarnともにバグがあってあまりうまく動かない。
  • npm link / yarn link を使うと、別のディレクトリにあるパッケージを直接使うことができる。アプリケーションの動作を確認しながらライブラリの開発するのに有用だが、依存解決の観点からはlinkによって異なる挙動をする可能性があるため、注意が必要。
  • npm run / yarn run は単なるスクリプト実行機能しかないので、Makefileのようなより複雑なタスク管理が必要ならGulpやGruntなどのタスクランナーを使うのがよい。ただし、トランスパイルやアセットのビルドなど典型的なものであれば、Webpackなどそれに特化したツールで目的を達成できることも多い。

次回はモジュールバンドラーの基本的な役割と実装について、webpackを例に説明します。

←前 目次


  1. #! がOKな理由についてはこちらの記事を参照。 

  2. シンボリックリンクの解決については第3回を参照。 

  3. Node.jsの探索ルールについては第3回を参照。 

  4. npm7ではpreinstallの動作が例外的になり、インストール処理よりも後になったようです。 

  5. タスクランナーの詳細については省きます。また、筆者はGulpもGruntもきちんと使ったことがないのですが、認識に間違いがあったらすみません…… 

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

アニメ番組管理のWebアプリ開発(自分用メモ)

Annict API の実装メモ

アニメ番組管理の自分用Webアプリとか作れないかな。(ぼんやり。)
新アニメとか、とりあえず1話見てみるってのが多いからな。
スタート直後はタイトルだけ見ても見てたかどうか分からんことが多いし。。

アニメ番組情報を取れそうなAPI

 ・annict.js
 ・ShangriLa Anime API Server
 ・しょぼいカレンダー(json.php)

Annict API(Annict.js) サインインできない
Annictは個人開発っぽい。番組表を自分で管理できるWebアプリの模様。そのままでも使えそう?
ただ、、メールアドレスを入れて「Send」してもメールが届かない?サインインできない。意味不明。
 ※追記:かなり遅れてメールが届いた。(時間帯?)

ShangriLa Anime API Server
APIの仕様も使いやすそう。ただ放送時間は取れない。基本情報のみ。

しょぼいカレンダー(json.php)
オタク系番組表。仕様が分かり辛い。放送時間を取れそうだけどxml形式。
 ・タイトル取得(例):http://cal.syoboi.jp/json.php?Req=TitleLarge
 ・番組時間取得(例):http://cal.syoboi.jp/db.php?Command=ProgLookup&TID=5766

実装

エラー1:CodePenではformタグは使えない。
 
エラー2:axios読み込み忘れ。CodePen「Settings」の"JS"タブから追加。

リンク先Webページのサムネイル表示したい

link-prevueライブラリ
 title、descriptionでサイトカードを生成しているっぽい。
 ・リンクのプレビュー機能を「link-prevue」を使って実装する
 ・link-prevue -GitHubリポジトリ
 ※上手く取得できないサイト多数。説明に表示された言語もバラバラだったので利用見送り。

LinkPreview API
 URLからサイトサムネイル画像のURLや簡単な説明を返してくれそう。よさげ。
 ・LinkPreview
 ※エラー423が返される場合がある。(リクエスト先Webページで拒否される場合。)
 ※エラー429が返される場合がある。(リクエスト上限がかなり低いっぽい。)

そもそもWebページの画像って?OGPイメージなのでは?
 APIだとなんか微妙なので、OGPイメージを取ればよいって考え方にシフト。
 ※取得はうまいことできそう。非同期の罠にはまる。→解決せず。
 ・JavaScriptでURLからOGP取得する - Qiita

imgが表示されない、、
 ・【Vue.js】なぜかimgが表示されない時の解決法とその理由
 ・Vue.js imgタグのsrc要素は指定の仕方によって読み込み方が違う -Qiita
 ・vueで画像が表示されない
 ※良く分からん、、とりあえず以下で上手くいった。(上手く表示しないイメージもあったけど。)

.html
<img class="img" v-bind:src="urlを指定" width="200"> </img>

カード表示で横並びにしたい
 ・よく使うタイル(カード)デザインを CSS のパターン別でご紹介
 ・フレックスボックス(フレキシブルボックス)とはなにか?

その他参考

 ・Vue.jsを100時間勉強して分かったこと - Qiita
 ・Vue.jsでv-forをネストする - Qiita
 ・忘れやすい、複雑なJSONの要素をfor...in文で取り出す方法
 
 

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

【JavaScript】画像をランダムに表示させる方法

プログラミング勉強日記

2020年10月26日
何枚かの写真からJavaScriptを使ってランダムに1つ表示させる方法を紹介する。

方法

 表示候補の画像ファイルリストを事前に準備しておき、乱数を使って1つを選び出して表示させる。

image.png

具体的な手順
1. 表示させる可能性のある画像ファイル名をすべて配列に入れる
2. その中からアクセスされるたびに乱数を使って1枚選ぶ
3. 選んだ画像1つを表示させる

サンプルコード

JavaScript
const imageArea = document.getElementById('imageArea');
const images = ['【画像パス1】', '【画像パス2】', '【画像パス3】'];

const imageNo = Math.floor( Math.random() * images.length)
imageArea.src = images[imageNo];
HTML
<img src="【画像パス】" id='imageArea'>

参考文献

画像をランダムに表示する方法!JavaScriptで切り替え
JavaScriptで文字列や画像をランダムに表示する方法を現役エンジニアが解説【初心者向け】

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

娘とあと何日お風呂に入れるか計算するWebアプリ作ったら泣けてきた

娘とあと何回一緒にお風呂入れるだろう?

 「もう一緒に入りたくない」と言われる前に自ら娘とのお風呂は卒業したいもの。とはいえ、可能な限り一緒に入りたいというのが、父親の心情。あと何回一緒に入れるだろうかと考えたら、今日の1回も特別に思えるはず。そんな思いから、娘とあと何日一緒にお風呂に入れるか計算するWebアプリを作ってみました。
作ったWebアプリはこちら:point_right:あと何日一緒にお風呂に入れる?
image.png

あとこれしかない。。泣けてきた。。

娘の「パパ風呂」卒業のタイミングは、小学校3~4年生頃が多いようです。
娘の9歳の誕生日を卒業する日と定めると...
2020年10月26日時点で、あと、2608日
2日に一回だとしたら1304日
一週間に一回だとしたら372日...
泣けてきた:cry:

環境

コーディング
CodePen:Webブラウザ上でHTML/CSS/JavaScriptなど、主にフロントエンド言語のコーディングができるサービス
使用したライブラリ
vue.js
splitting.js:テキストアニメーションが実装できるライブラリ

ソース

index.html
<!-- 全体をVue.js有効にする -->
<div class="container text-center text-white" id="app">

  <!-- タイトル行 -->
  <div class="row my-3">
  <div class="col-sm-15 mx-auto"><div data-splitting><h1>あと何日一緒にお風呂入れる?</h1></div>
  </div>
  </div>

<img src="https://4.bp.blogspot.com/-YUk507i8b7s/UfIJFj6AMQI/AAAAAAAAWc0/KzQO-AiH6Jg/s800/dakko_papa_girl.png" width="30%"/>

  <div class="form-group">
  <label for="lastday">卒業する日</label><input class="form-control col-sm-6 mx-auto" type="date" id="lastday" onChange="calc();"/>
    <label for="today">今日の日付</label><input class="form-control col-sm-6 mx-auto" type="date" id="today" onChange="calc();"/>
 </div>
  <div class="form-group">
    <label for="days">一緒にお風呂に入れる日数</label><input class="form-control col-sm-6 mx-auto" type="text" readonly id="days"/>
  </div>

 <div data-splitting><label for="days">卒業のその日まで、有意義に過ごしてほしい。小さい娘を持つ全てのパパへ。</label></div>
<br>
</div>
<!-- 全体ここまで -->
style.css
@import url(https://fonts.googleapis.com/css?family=Kanit:600);

html { height: 100%; display: flex; }
body { margin: auto; }
html, body {
  background: #00043C;
  color: #FFF;
  font: normal 600 1.5vw/1.5 Kanit, sans-serif;
}

.splitting .char {
  animation: slide-in 1s cubic-bezier(.5, 0, .5, 1) both;
  animation-delay: calc(60ms * var(--char-index));
}

@keyframes slide-in {
  from {
    transform: translateY(-1em) rotate(-.5turn) scale(0.5);
    opacity: 0;
  }
}
script.js
Splitting();

function calc() {
  lastday = new Date($('#lastday').val());
  today = new Date($('#today').val());
  console.log(lastday.getTime());
  console.log(today.getTime());

  var days = Math.floor((lastday.getTime() - today.getTime()) / (1000 * 60 * 60 *24));

  $('#days').val(days);  
}

function today() {
  $('#today').val(formatdate());
}

function formatdate(date) {
  var toTwoDigits = function (num, digit) {
    num += ''
    if (num.length < digit) {
      num = '0' + num
    }
    return num
  };

  date = date ? date : new Date()
  var year = date.getFullYear()
  var month = date.getMonth() + 1
  var day = date.getDate()
  var yyyy = toTwoDigits(year, 4)
  var mm = toTwoDigits(month, 2)
  var dd = toTwoDigits(day, 2)
  var ymd = yyyy + "-" + mm + "-" + dd;
  return ymd;
}

$(function() 
{
  $('#lastday').val(formatdate());
  $('#today').val(formatdate());
}());

はじめてのWebアプリケーション

 プログラミング初心者の私でもCodepenと便利なライブラリを駆使して、割と簡単に実装することができました。今後は音を入れるとか、もう少しリッチな見た目とか、スマホ用に便利な画面とか、作れるようになったらより楽しいだろう。時間を見つけて、色々と試作してみたいです。

おまけ

 あまりに早く成長してしまうことが悲しいと思うこともありますが、我が子の成長はやはり嬉しいものです。一緒にお風呂に入れなくなっても、いつか娘に一緒とプレモルが飲めることを楽しみに頑張ります:beers:

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

random関数でランダムな値を生成する

以下のコードで0〜5の中からランダムな値を生成できる

Math.floor(Math.random() * 6);

*6の意味がわからなかったため調べたことを残しておく。

Math.random()

random関数は0以上、1未満の浮動小数点の擬似乱数を生成する関数

console.log(Math.random());
// 0.3538826537319486 ← 0以上、1未満
// 1未満のため最大でも0.999999999999・・・

floorメソッドは引数として与えた数以下の最大の整数を返すためMath.floor(Math.random());とすると0しか返ってこないことになる。
返ってくる値の個数を増やしたいのであればMath.random()の値を大きくすればいいため*nとして最大値を大きくする。
そしてMath.random()にかける数を3倍, 4倍, 5倍と増やしていくごとに、得られる数値の数も3個, 4個, 5個と増えていくため欲しい数値分掛け算をすればいい。

初めの疑問に戻ると6倍することで乱数の最大値を5.9999・・・として0〜5の間でランダムな値を生成しているというのが今回の疑問に対する答え。

応用編 最小値を0以外にする

今までの方法は最小値を0として最大値を設定していたが以下の式によって最小値を0以外にすることも可能。

Math.floor(Math.random() * ( 最大値 - 最小値 + 1 ) + 最小値);

例えば5〜10でランダムの値を取りたい場合、上記の式に当てはめると( 最大値 - 最小値 + 1 )の部分は6になる。
そして、もし+最小値の式を入れなかった場合取れる値は0〜5になる。
0〜5の値を5〜10にするためにはどうすればいいかというと最小値を足せばいい。

0, 1, 2, 3, 4, 5
5, 6, 7, 8, 9, 10 //上段の値に5(最小値)を足すと下段の値になる

5〜10に限らずどの範囲であっても上の式に当てはめることで範囲を指定できます。
以上、random関数についてでした!

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

Javascript random関数について

以下のコードで0〜5の中からランダムな値を生成できる

  const number = Math.floor(Math.random() * 6);

*6の意味がわからなかったため調べたことを残しておく。

流れは以下のとおり。
1.floorメソッドの引数としてMath.random() * 6を指定する。

2.random関数は0以上、1未満の浮動小数点の擬似乱数を生成する関数

console.log(Math.random());
// 0.3538826537319486 ← 0以上、1未満

 →random()で生成された擬似乱数を*6することで0以上6未満の乱数が生成される。
(上の例でいうと0.3538826537319486に6をかける)
3.floorメソッドは引数として与えた数以下の最大の整数を返してくれるため0〜5の中からランダムな値が出来上がる。

以上、random関数についてでした!

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

JavaScript学習 OutPut part1 JavaScriptとは??

はじめに

こんにちは。のりべんと申します。
この度プログラミング学習のoutputの場(兼文書作成の練習)を設けるために
Qiitaを開設しました。
自分の学習内容が後の初学者のお供になれるような、
誰が見ても分かりやすい記述
を心がけていこうと思います。よろしくお願いします。

投稿主詳細

  • (一応)理系大学院卒
  • システムエンジニア1年目
  • 後々はマネジメントに携わるより永遠とプログラム組んでスペックを上げたい
  • paiza:Aランク(確かCで取ったけどコーディングはお察し)
  • python使って画像処理したりunityでc#かじったり(鼻くそ程度)

JavaScriptの振り返り

ここからは教材で学習した内容をoutputしていきます。
使用した教材はこちら
それでは以下常体。

JavaScriptって何?

簡潔に言うとHTMLやCSSだけでできないことを補うブラウザ操作のためのプログラム言語。
webページは基本的にHTMLとCSSで構成される。
しかしHTMLとCSSだけでは静的(一度ブラウザに読み込まれたら変化しない)なwebページとなる。
webページをその場で書き換えて動的(リアルタイムに書き換えて動きをつける)にすることを可能にするのがJavaScriptなのである。

キーワード

  • ブラウザを操作することができる
  • HTMLやCSSだけではできないことを補うために使う
  • HTMLやCSSをリアルタイムに書き換えられる

スクリーンショット 2020-10-21 23.44.51.png
↑この小計が
スクリーンショット 2020-10-21 23.45.29.png
↑数量を変更した時、ページ読み込み無しで表示が変化するのが動的なwebサイトと言える

JavaScriptでHTMLやCSSを書き換える

では具体的にどのような書き換えを行っているのか?
HTMLやCSSの書き換えには大きく分けて4パターンある。
これらの書き換えはブラウザの表示に即座に反映される。
(ページの再読み込みは必要ない。)
これにより
ホームページではない、アプリらしい動きのwebページを作ることができる。

パターン1 タグに囲まれたテキストの書き換え

例)タグ内の書き換え

<p>こんにちは</p>

↑これを

<p>さようなら</p>

↑にJavaScriptが書き換えてくれる

パターン2 要素の追加、削除

例)タグの追加

<ul>
   <li>こんにちは</li>
</ul>

↑これに

<ul>
   <li>こんにちは</li>
   <li>元気ですか?</li>
</ul>

↑liタグで「元気ですか?」とJavaScriptが追加してくれる

例)タグの削除

<ul>
   <li>おはようございます</li>
   <li>寒くなってきましたね</li>
</ul>

↑このliタグの寒くなってきましたねを

<ul>
   <li>おはようございます</li>
</ul>

↑JavaScriptで削除できる

パターン3 タグの属性を変更

例)srcタグの属性の値を変更

<img src = "noriben.jpg">

↑この場合noriben.jpgというファイル名がsrcタグの属性の値となる。これを

<img src = "karaben.jpg">

↑JavaScriptでkaraben.jpgに値の変更ができる

パターン4 CSSの値を変更

例)CSSのの背景色の変更

body{
   background:#ffffff;
}

↑CSSでwebサイトの背景色を設定しているところを

body{
   background:#fff100;
}

↑JavaScriptで中身を変更することができる

JavaScriptの基本的で重要な役割

JavaScriptの重要な役割は次の2点

  • ブラウザに表示されているHTMLやCSSを書き換える
  • ブラウザに表示されているHTMLやCSSから情報を読み取る

上記では書き換えに関する内容を説明したが、実際には情報の読み取りも可能である。
例)住所などの入力フォームに入力された内容の読み取りなど

JavaScriptの仕組み

では、実際にJavaScriptがどのような書き換えを行っているのかを具体的な例で見ていきます。

例)ネットでポケモンカード拡張パックを買う場合(消費税は考慮しない)
※ポケモンカードは1パック150円とする。
ネット通販なんかで購入しようとするとおそらくこのようなフォームが想像できるでしょう。
(数量はユーザーが書き換え可能)

品名          |単価 |数量 |小計
ポケモンカード拡張パック|     |    |
列空の覇者       |150  |1   |150

これは単価と数量によって小計が変化するため、JavaScriptでは単価と数量を取得する。
そこから取ってきた値を掛け合わせた数を小計として出力していく。

品名          |単価 |数量 |小計
ポケモンカード拡張パック|     |    |
列空の覇者       |150  |3   |450

↑単価と数量をJavaScriptで取得して小計を出力している

この処理は4つのステップに分けることができる。

1. 単価をHTMLから取得
2. 数量をHTMLから取得
3. 単価×数量で小計を計算する
4. 計算結果を使ってHTMLを書き換える

1、2に関しては必要なデータを取得するこのステップはインプットのステージ
3に関してはインプットされたデータを加工して結果を得るステージ
4に関しては加工して得られた結果を出力するアウトプットのステージ
ということができる。
JavaScriptでは
インプット → 加工 → アウトプット
という流れが共通する処理の流れとなる。

JavaScriptのイベント

インプット → 加工 → アウトプットはJavaScriptの処理の一連の流れである。
ではこの処理はどのタイミングで発生するのか?
今回の例であれば「パックの数量が変更された時」である。
数量の変更されたタイミングで
単価と数量がHTMLから取得され(インプット)
単価と数量が掛け合わされ(加工)
小計として合計値が出力される(アウトプット)
が行われ、数量を変更するたびに描画が繰り返される。
この処理のきっかけのことをJavaScriptではイベントと呼ばれている。

終わりに

今回はJavaScriptがどのような場面で使われるか、どのような働きをするか、その仕組みについて取り上げていきました。
次回からはJavaScriptのアウトプットについて触れていきます。
以上となります。お疲れ様でした。

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

formタグは1つのまま、1つの要素クリックで複数の関連するnameだけを送信(post)させる方法

どうも7noteです。綺麗に書きたいがあまりちょっと無理やり作りました。

(※データを送信するだけならhtmlとjavascriptでできますが、受け取ってその後利用するにはphpの知識が必要です!)

例えば都道府県のように地域(関東とか)と都道府県名の2種類のデータを個別にpostさせたい時に使える方法です。

普通、複数のデータを送る時はinputのname属性を配列にする方法が一般的です。


一般的に複数のデータを送信する例)

<form action="./" method="post">
  <input type="checkbox" name="pref[]" value="tokyo">
  <input type="checkbox" name="pref[]" value="osaka" >
  <input type="submit" value="送信">
</form>

※ 受け取り側はpostを受け取れるようphpで作成してください。


この方法で、東京と大阪をpostすることはできましたが、同じ方法でエリアを送信してしまうと
pref['kanto', 'tokyo']のようにエリア名と都道府県名が1つの配列内に混ざって送信されてしまいます。

今回はこれを↓のように分けて送信する方法を書きます。

area ⇒ "kanto"
pref ⇒ "tokyo"

都道府県ごとに、エリアと都道府県情報をpostさせる方法

※jQueryを使用しています。

index.html
<form action="./" method="post" name="prefform">
  <input type="hidden" name="area" value="">
  <input type="hidden" name="pref" value="" >
</form>

<ul>
  <li area="kanto" pref="tokyo">東京</li>
  <li area="kanto" pref="kanagawa">神奈川</li>
  <li area="kansai" pref="osaka">大阪</li>
</ul>
script.js
$(function(){
  $("ul li").on("click", function(e){         // li要素がクリックされたとき
    area = $(this).attr("area");              // li要素のarea属性の値を取得
    pref = $(this).attr("pref");              // li要素のpref属性の値を取得
    $("#input_area").attr("value",area);      // inputのvalue値を変数areaに置き換え
    $("#input_pref").attr("value",pref);      // inputのvalue値を変数prefに置き換え
    document.forms.prefform.submit();         // submit(送信)する。
  });
});

とび先は、form要素のactionにURLやディレクトリを記述してください。

説明するほどのことはしていないのですが、クリックされた要素の属性値、それぞれprefとareaを取得します。
サブミット(送信)する前に、inputの中のvalue値に値を入れます。
値を入れた後、サブミットすることでエリアと都道府県をそれぞれ個別で送信することができます。
もしとび先の修正などが入った場合でも、formタグのactionを1箇所修正するだけ済むので、管理が楽になるかと。

まとめ

もっとよい方法や綺麗な書き方などあるかもしれませんが、ひとまずこれで十分の機能ははたせるかなと思います。
javascriptは使わず、htmlだけでもできなくはないですが、何度も何度もformタグやinputタグを書くとなるとソースが見にくく管理もしにくくなるので、何かしらで対応できるような作りの方がいいかなと思います。

おそまつ!

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

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

【JavaScript】開発を行う前に整理しておきたい言葉の定義

「言語」、「フレームワーク」、「ミドルウエア」、「ライブラリー」、「テンプレートエンジン」など様々な言葉には意味がある。
今回は「JavaScript」と「Node.js」について整理した。

■JavaScriptとNode.jsについて
JavaScript:JavaScriptはブラウザ上で動くために開発されたプログラミング言語。
Node.js:イメージはサーバサイドのJavaScript。正確にはNode.jsはサーバサイドでJavaScriptを実行できるようにしてくれるプラットフォーム。

■Node.jsの特徴
・サーバーサイドのJavaScript
・非同期
 ⇢I/O(Input/Output)の処理結果を待たず処理を進める。
・ノンブロッキングI/O
 ⇢I/Oの結果を待たないで処理をすすめる。I/O処理が終了したらコールバック関数を実行。
・イベントドリブン (何かしらのキッカケで動き始める)
 ⇢https://qiita.com/hththt/items/aefbcc6eb191588dadff
・シングルスレッド
 ⇢メモリ消費が少ない、仕事切り替えが少ないので速い
・JavaScript エンジンが Google の V8 で速い
 ⇢V8は、Googleが開発するオープンソースのJIT Virtual Machine型のJavaScriptエンジン。

■定義と使用区分の振り分け
・言語:Javascript、Node.js
 ⇢PCに下す命令言語
・フレームワーク:Express、React、jQuery、AngularJS、Vue
 ⇢JavaScriptを使用してWebサイト開発やWebアプリケーションを開発する際の土台として機能するソフト
・ミドルウェア:PostgreSQL
 ⇢OS(オペレーティングシステム)とアプリケーションソフトの中間役割を担うソフト
・ライブラリー:express-session、express-validator
 ⇢使用頻度の高い機能や効果などのプログラムをまとめたもの
・テンプレートエンジン:ejs
 ⇢テンプレートと呼ばれるHTMLのひな形を元にプログラムで加工し、画面に出力するためのライブラリ。

■変数の定義
・「const」や「var」、「let」で変数を定義。何らかの値を変数に代入して使用する。

【例】

// 数値
const numValue = 100;
// 文字列
const strValue = "私は値です!";
// 真偽値
const boolValue = true;
// 配列
const arrayValue = [1, 2, 3];
// 連想配列
const objValue = { key: 'value' };

■データの型
var:変数を宣言し、ある値に初期化することもできる。
let:スコープのローカル変数を宣言し、ある値に初期化することもできる。
const:スコープで読み取り専用の名前付き定数を宣言する。

■MDN 文法とデータ型
https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Grammar_and_types

■スコープ
JavaScriptでは関数の中で定義された変数やオブジェクトは関数の外からアクセスすることができない。
関数の中で定義された変数やオブジェクトを操作したい場合は同じ関数の中で処理を記述する必要がある。

【例-1】

function hoge(){
    var x = 0;
}
hoge();

console.log(x);

⇢この場合console.log(x);でエラーとなる。変数xはhoge関数の中で定義されているため。

【例-2】

function hoge(){
    var x = 0;
    console.log(x);
}
hoge();

⇢この場合、エラーとならない。

■関数の定義
・関数の定義を行い、定義した関数を使用する。

// 関数を定義する
function 関数名(仮引数){
  実施したい処理内容
}

// 関数を使用する
関数名();

【例】

// add関数が定義される
function add(a, b) {
  return a + b;
}

// add関数を出力する
console.log(add(1, 2)); 
// この場合「add関数」と「log関数」が使用されている。

・javascriptでは関数を変数に代入することができる。
 つまりjavascriptでは関数も値。

// addFunction変数にadd関数を代入
const addFunction = function add(a, b) {
  return a + b;
}

// addFunction変数を出力
console.log(addFunction(1, 2)); 

// 定義した関数を変数に入れることも出来る
const addFunction = add;

・Javascriptで関数は値。関数を複数回使用することもできる。
 関数を受け取る関数を「高階関数」と呼ぶ。

// 関数を2回実行する関数の作成
function toDoTwin(func) {
  func(); // 1回目の関数
  func(); // 2回目の関数
}

// Hello Worldが2回呼び出される
toDoTwin(function() {
  console.log('Hello World');
});

【例題】
 配列を受け取って配列の末尾に数値3を加える関数を作る。
 ※配列に追加する関数は[].push(数値)で入れられる。

【例】

 var arr = [1,2]

 // 関数は動詞。キャメル型を使用。
 function addNum(arr) {
   arr.push(3)
   return arr;
 };

 addNum();

↓修正中

■コールバック関数とは
シンプルに言うと「高階関数に渡すための関数」。
【例】

setTimeout(function() {
  console.log('Hello World');
}, 2000);

⇢この場合、2000ミリ秒後に「Hello World」と出力させる

■非同期処理とイベントとコールバック関数
非同期処理は「書いた順に動く」というプログラムの基本とは違う動きになる。「書かれているコードを今は飛ばして後で実行して」という矛盾した状況になる。

JavaScriptの多くの非同期処理は、これを以下のような方法で実現。
・非同期処理関数はコールバック関数を受け取る高階関数にする
・利用者は「終わったら実行したい処理」をコールバック関数として渡す
・非同期処理関数は処理が終わったらコールバック関数を呼び出す

非同期処理はコードを複雑化させてしまうため、非同期処理を簡単に扱うことができる、Promiseやasync/awaitという機能がある。

【例】

setTimeout() => {
console.log('hello'), 500);
console.log('world!');
};

「world」が先に表示され、500ミリ秒が経過してから「hello」が表示される。
非同期処理では、実行順序はコード通りにはならない。

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

_.debounce と _.throttle は何が違うのか

Vue.js 公式ガイドにて学習をしていたところ、lodash_.debounceを使っている箇所があり、
以下のように記載されていた。

    // _.debounce (とその親戚である _.throttle )  についての詳細は
    // https://lodash.com/docs#debounce を見てください。
    this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)


参考URL先のドキュメントを読んでみたのですが
_.debounce_.throttleの違いがいまいち分からず(英語苦手....)

挙動としては以下の感じの実装です。(ソースはこちら)
ezgif.com-gif-maker.gif

文字を連続で入力したときにgetAnswerの関数が何度も呼ばれないようにしているみたいです。



調べてみると以下の違いのようでした。

  • _.debounce:連続して呼び出しても指定された時間以内なら処理は実行しない
  • _.throttle:連続して呼び出しても指定された時間以内に1回しか実行しない


なるほど...(わからん)

ということでどっちも実装して違いを見てみました。

_.debounceの場合

以下のように実装。
(2000ミリ秒で設定)

this.debouncedGetAnswer = _.debounce(this.getAnswer, 2000)

これで一定の間隔で文字を入力してみます。

ezgif.com-gif-maker (2).gif

入力する間隔が2000ミリ秒以内だとgetAnswerの処理が呼ばれない
「連続して呼び出しても指定された時間以内なら処理は実行しない」

_.throttleの場合

以下のように実装
(2000ミリ秒で設定)

this.debouncedGetAnswer = _.throttle(this.getAnswer, 2000)

こちらも先程と同じ間隔くらいで文字を入力してみます。

ezgif.com-gif-maker (1).gif

連続して入力しても2000ミリ秒に1回しかgetAnswerの処理が呼ばれない
「連続して呼び出しても指定された時間以内に1回しか実行しない」



...ということでやっと違いが理解できました!
やはり実際に動かすだけで頭に入りますね。



ちなみにlodash_としてるのは
lodash = low(低い) + dash(-) = _だからとかそんな理由らしいです。


引き続き勉強がんばります。

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

[lodash] [async.js] jsのいろいろなforEachを試してみた

はじめに

javascriptでforEachを書くパターンは色々あります
使用していて「このループを書くならこっちのほうがいいな」「このforEachって非同期処理のときどうなるんだっけ」と思い返すことが多々あったので記事にまとめておこうと思いました。
今回取り上げるのは以下です
・ Array.prototype.forEach
・ lodash forEach
・ async.js each
・ async.js eachSeries

サンプルコード内で非同期関数 asyncFunc, arr を呼び出していますが、これはそれぞれ

const arr = [1, 2, 3];

// 一秒後に引数の関数を実行する関数
const asyncFunc = i => {
  setTimeout(() => {
    console.log(`output: ${i}`);
  }, 1000);
}

このように定義しています。

Array.prototype.forEach

Array.prototype.forEach documentation

arr.forEach(element => {
  asyncFunc(element)
});

/**
 * 一秒後にまとめて結果が返ってくる
output: 1
output: 2
output: 3
 */

Array.prototype.forEach はまさにノンブロッキングの形になっていて、forEach内の要素が順にイベントキューに積まれて行くといったイメージでしょうか
ちなみに、 Array.prototpye.forEach はcontinueは可能(returnするだけ)ですが、breakは不可能です
また、要素を同期的に実装するのも厳しいです。(自分の知る限りでは)

lodash forEach

lodash documentation

const _ = require('lodash');
_.forEach(arr, element => {
  asyncFunc(element)
});

/**
 * 一秒後にまとめて結果が返ってくる
output: 1
output: 2
output: 3
 */

こちらも Array.prototype.forEach と同様に要素がすべてイベントキューに積まれていく形です。
ただちょっと違うのは、 breakができる という点です

const _ = require('lodash');
_.forEach(arr, element => {
  console.log(`start: ${element}`)
  asyncFunc(element)
  if (element === 1) return false;
});

/**
 * 1だけ出力してbreakされている
start: 1
output: 1
 */

このように、イベントキューに1,2,3それぞれの非同期処理が積まれていても、1の処理でbreakされれば後続の処理はされずに終了します。
また、 Array.prototype.forEach と同様に同期的に実行するのも厳しいです
小ネタとしてはdocumentにも書かれていますがObjectに対しても実行可能です。

_.forEach({ 'a': 1, 'b': 2 }, function(value, key) {
  console.log(`${key}:${value}`);
});
/**
 * objectに対しても利用可能
a:1
b:2
 */

async.js each

async documentation

async.each(arr, (elem, next) => {
  asyncFunc(elem)
  next();
}, (err) => {
  console.log(err);
});

/**
 * 一秒後にまとめて結果が返ってくる
output: 1
output: 2
output: 3
 */

こちらも同様に要素がすべてイベントキューに積まれていく形です。
breakの機能はありませんが、callbackにエラーを投げることが出来ます。

// 実行要素の途中でエラーが発生した場合
async.each(arr, (elem, next) => {
  console.log(`start: ${elem}`)
  asyncFunc(elem)
  if (elem >= 2) return next('err occured');
  else return next();
}, (err) => {
  if (err) console.log(err)
});

/**
 * エラーが発生したタイミングでcallbackが呼び出されるが、asyncFuncは全て実行される
start: 1
start: 2
err occured
start: 3
1
2
3
 */

// 実行要素がすべてエラーを返さなかった場合
async.each(arr, (elem, next) => {
  console.log(`start: ${elem}`)
  asyncFunc(elem)
  return next();
}, (err) => {
  if (err) console.log(err)
  else console.log('error not occured')
});

/**
 * すべての処理をキューに詰め込んだあとでcallbackが呼び出される
start: 1
start: 2
start: 3
error not occured
1
2
3
 */

↑のように、イベントキューに積まれている非同期処理はその途中でエラーが発生(nextの第一引数にエラー内容を指定)しても関係なくすべて実行されます。また、エラーが発生した際はその直後にcallback関数(第三引数)が呼び出されます。
一度もエラーが発生しなかった際は、すべての処理をイベントキューに詰め込んだあとでcallbackが呼び出されます。
ちょっと特殊っぽい。

async.js eachSeries

async.eachSeries(arr, (elem, next) => {
  asyncFunc(elem)
  return next();
}, (err) => {
  if (err) console.log(err)
});

/**
 * 一秒後にまとめて結果が返ってくる
output: 1
output: 2
output: 3
 */

先程の async.js each の同期的バージョンです。 each では処理の順番は保証されていませんでしたが、 eachSeries では実行順が保証されています。

またそのような性質から このように next を活用することで同期的にすることも可能です。

const _asyncFunc = (i, cb) => {
  setTimeout(() => {
    console.log(i);
    return cb();
  }, 1000);
}

async.eachSeries(arr, (elem, next) => {
  _asyncFunc(elem, next)
}, (err) => {
  if (err) console.log(err)
});

/**
 * 一秒ごとに出力される
output: 1
output: 2
output: 3
 */

まとめ

まとめてみると、意外とそれぞれで違いがあることがわかりました。
それでは

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

JavaScriptの関数について学んでみた

Javascriptの関数の初歩的な部分です!
Rubyを先に学んだので、Rubyとの比較などが多くなります!

関数定義(Rubyでいうところのメゾット)

Rubyでメソッドを定義する際はdef メソッド名 ~ endと記述しました。
JavaScriptではfunction 関数名(){ 処理 }と記述することで関数を定義することができます。

functionの使い方

hoge.js
function sayHello(){
  console.log("やっほー!")
}

sayHello()//=>やっほー!が出力されます//

*超基礎的な文章です!

戻り値について

Rubyの場合

hoge.js
def calc(num1,num2)
  num1*num2
end

num1 = 5
num2 = 9
puts calc(num1,num2)//メソッドの戻り値である45が表示される//

JavaScriptの場合

hoge.js
function calc(num1,num2){
  return num1*num2
}

const num1 = 5
const num2 = 9
console.log(calc(num1,num2))//45が表示される//

*戻り値をreturnを用いて明示する必要があるところがいちばんの大きな違いです!

いろいろな関数定義について

関数式と関数宣言の違い

関数宣言は上記のfunctionの使い方で記述したやり方のことを言います。
関数式は
変数 = function( 引数 ){
// 関数内の処理
}

と記述します。違いとしては読み込まれるタイミングが異なります。

hoge.js
hello()
const hello = function(){
  console.log('hello') //Uncaught ReferenceError...というエラーが出ます!//
}

理由としては1行目の時点で関数helloは定義されていないためです。
これを関数宣言の書き方に直してあげると

hoge.js
hello()
function hello(){
  console.log('hello')//helloが出力されます//
}

JavaScriptにおいては関数宣言は先に読み込まれるので、このような事象が発生します。一方で、関数式であれば先に読み込まれることはありません。コードを書く上での実害はあまり無いかもしれませんが、このような特徴があるということを覚えておきましょう!

名前の無い関数

無名関数

hoge.js
const calc = function(num1,num2){
  return num1*num2
}

const num1 = 5
const num2 = 9
console.log(calc(num1,num2))

関数名なしで関数を定義することができるのでより簡潔なコードが記述できるというメリットがあります!
簡潔なってるかな?と思ったんですけど記述が増えれば増えるほどその恩恵が感じるのかなと感じています。
関数名の重複を避けることができますしね。

即時関数

hoge.js
//無名関数//
const countNum = function(num) {
  console.log(num)
}
countNum(4)

// 即時関数//
(function countNum(num) {
  console.log(num)
})(4)

()の中にfunctionからはじまる関数定義そのものを配置することで、その関数を即実行するということができるようになります。その結果、関数を呼び出すという手間が省けるというメリットがあります!

アロー関数

hoge.js
// 無名関数
const 変数名 = function(){
  処理
}

// アロー関数
const 変数名 = () => {
  処理
}

functionの記述を省略し、その代わりに() => という記述によって関数を定義する構文です。無名関数または即時関数において、より省略した記述をしたい時に使用します!

まとめ

正直みて覚えられないなーと感じると思います。僕もそうでした。何回も書いて、考えて使うことによって覚えられるよう一緒に頑張りましょう!

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

TypeError: Failed to execute 'fetch' on 'Window'について

fetch apiを使おうとして躓いたのでメモ

問題

createItems() {

  var opt = {
    "method": "POST", 
    "headers": {"Content-Type": "application/json"}, 
    "body": {
      "title": "テストタイトル",
      "content": "テストコンテンツ"
    }
  }

  fetch("/create", JSON.stringify(opt))
  .then((res) => res.json())
  .then((res) => alert("ok"))
  .catch((err) => alert(err));

}
TypeError: Failed to execute 'fetch' on 'Window': parameter 2 ('init') is not an object.

解決方法

"init"はfetch関数の第2引数のことである。
見ると引数の値がobjectではなくstringになっていた。
なので正しい書き方は

fetch("/create", opt)

である。Content-Typeがjsonなのでoptもjsonにしなければいけないと勘違いしていたことが原因。

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

JavaScriptの超基礎的な文法を覚えておこう

変数定義の代表3つ const, let, varについて

constとは

後から書き換えることができない変数を定義する書き方です。constの特徴として、再代入、再定義ともに不可という制約があります。
その分、一番保守性が高く3つの中で一番使われる変数と言えます!

letとは

後で書き換えることができる変数を定義する書き方です。letの特徴として、再代入は可能ですが、再定義は不可という制約があります。
constの次に使われており、どうしてもconstではダメなときだけ使います。あくまでconstが最優先です。

varとは

再定義、再代入可能な古い書き方です。varの特徴として、再代入、再定義ともに可能です。現在は開発現場においてvarという書き方はあまり使われないです。使ったら負け!と思うぐらいでいいと思います!

配列の繰り返し処理をする関数

forEach関数

配列に格納されている要素1つひとつに対して、繰り返し処理を行う場合に用いられる関数です。なお、「関数」とはRubyでいうとメソッドと同様で思っていれば大丈夫です。
sports = ["soccer", "sumou", "baseball"]
sports.forEach( function(item) {
console.log(item) // 配列に格納されている要素の数だけ実行されます
})

上記のコードをコンソール上で実行すると、すべての要素が順番に出力されます。
引数itemには、1回目の繰り返しで「soccer」、2回目で「sumou」、3回目で「baseball」が代入されます。
そして、要素の数だけ繰り返し処理を実行すると、この繰り返し処理は終了します。
結果はこんな感じです。
soccer
sumou
baseball

テンプレートリテラルについて

テンプレートリテラルは、JavaScriptの構文です。ダブルクォートやシングルクォートの代わりにバッククォートで囲むことで、文字列内に挿入することができます。記述が圧倒的に見やすくなるので必ずJavaScriptを勉強していくなら覚えましょう!

テンプレートリテラルを使うとできることまとめ

文字列の中に変数を埋め込むことができる
改行を入れることができる
HTMLの要素を作成することができる

使った場合と使わなかった場合を見比べてください
使用しないパターン

card.js
const name = "Entaku"
const age = 23

const introduction = "私の名前は" + name + "" + age + "歳です"
console.log(introduction)
// => 私の名前はEntaku、23歳です と出力される
      }

使ったパターン

card.js
const name = "Entaku"
const age = 23

const introduction = `私の名前は${name}${age}歳です`
console.log(introduction)
// => 私の名前はEntaku、23歳です と出力される

テンプレートリテラルを用いることで、記述量が少なくなったことがわかります。開発では、記述量の大小はプロダクトの質に大きな差を生む事になるので、テンプレートリテラルを積極的に使用するようにしましょう!!

まとめ

JavaScriptで変数定義をする場合は、原則constとletを使用します!
変数に値を定義する際にテンプレートリテラルを使用することで、文字列の中に変数を含めることができます!
JavaScriptにおいて、配列の繰り返し処理を行う場合は、for文またはforEach()関数を使用します!

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

ソフトウェアやパッケージとバージョン管理に関するセンテンス

PATH

PATHとは、「環境変数」と呼ばれるOS用の変数のこと。PATHには、複数の絶対パスの情報が保存されています。
コマンドが入力されたときに、シェルはPATHに記述されたパスのディレクトリ内のファイルを検索します。つまり、PATHに絶対パスを保存してアプリケーションのありかを示せば、どこからコマンドを実行してもアプリケーションのコマンドを打つことができます。

# rbenvのパスを通す
% echo 'eval "$(rbenv init -)"' >> ~/.zshrc

# MySQLのパスを通す
% echo 'export PATH="/usr/local/opt/mysql@5.6/bin:$PATH"' >> ~/.zshrc

echoコマンドは、>>に続けてファイル名を指定することで、ファイルに文字を追加できるLinuxコマンドです。.zshrcにrbenvの設定を反映するためのコマンドを記述することで、PATHを通しています。

コマンドとアプリケーションの関係

コマンドが実行されると、シェルがそれを読み取ってOSに指示を出す。
OSに初めから入っていてすぐに動作できるアプリケーションとは別に、後からPCに追加する必要があり、よく利用されるアプリケーションは、コマンドラインツールとしてまとめられています。

コマンドラインツール

コマンドで操作するアプリケーションのまとまりです。
コマンドラインツールを導入することで、OSが初めからコマンドで操作できるアプリケーション以外のアプリケーションをPCにインストールできます。

Command Line Tools

macOS専用のコマンドラインツールのことです。
macOSでは、元々Linuxコマンドで操作できるアプリケーションや機能を標準搭載しています。Linuxコマンド以外で操作するアプリケーションの多くはCommand Line Toolsのインストールによって、まとめてPCに導入できます。

パッケージとバージョンの管理

パッケージ管理

パッケージとは、プログラムや処理をひとまとめにしたもののことです。ライブラリとも言えますが、パッケージは複数のライブラリをまとめていることもあります。
パッケージ管理とは、パッケージやパッケージが持つライブラリなどの依存関係を考慮してインストールやバージョンアップを行う管理のことです。
1つのパッケージを利用したい場合、そのパッケージと依存関係にあるパッケージも合わせてインストールしてくれます。

パッケージ管理ツール

Homebrew(macOSのパッケージ管理ツール)
Yarn(JavaScriptのパッケージ管理ツール)

パッケージ

Gem(Rubyのパッケージ)
Node.js(サーバーサイドで利用できるJavaScriptのパッケージ)

バージョン管理

rbenv(Rubyのバージョン管理ツール)

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

画面で入力された注文数をグラフに反映するWebアプリ(仮)

入力された注文数をグラフに反映してみた

movie

今日の注文数を入力したらすぐ反映されますね!
DBとか使ってなくてすぐ表示させてるだけだから!

URLはこちらです!→今日までの売上を可視化しよう

使い方

 一日の終わりに、今日注文された数を入力
 →こんだけ増えたよって可視化される

苦労した点 :smile:

今回は、裏のロジックというより簡単な情報をうまく表示するところを意識しました。

・グリッドの理解をしないままbootstrapを使い始めて、表が右に行ったり、めちゃくちゃ高さが低くなったり、小さくなったり。
 それもあって、ちゃんと理解をするためにこちらのサイトで勉強させていただきました。↓
 bootstrapの構造について学ぶ

・最初Chart.jsを使おうとしていましたが、new Chart.jsをするところで、
 Vue.jsから呼び出したかったが、どう記述すればよいのかわからなかったので今回はgstaticを使いました。

今回やりたかったけどできなかったこと :astonished:

・DBを使ってデータの出し入れ
・月を選ぶ。今回のは10月にしか追加されない。。。
・「数値をクリア。」どこのだよ!と思うのを解消する。 今は10月の数値が根こそぎ消える。
・目標の数値の設定や、そことの差の描画
・描画方法の選定 絶対もっときれいでおしゃれなやつあるやろ!

環境

エディタ:codepen
CSS:bootstrap
使用ライブラリ
  vue.js
  loader.js gstatic

ソース

HTML部分はこちら↓

<!DOCTYPE html>
<html lang="en" >
<head>
  <meta charset="UTF-8">
  <title>CodePen - chart</title>
  <link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.min.css'>

</head>
<body>
<!-- partial:index.partial.html -->
<head>
  <script src="https://www.gstatic.com/charts/loader.js"></script>
</head>
<!-- 全体をVue.js有効にする -->
<div class="container text-center text-white bg-dark" id="app">

  <!-- タイトル行 -->
  <div class="row my-3">
    <div class="col-sm-6 mx-auto"><h1>一日の終わりに注文数を入力</h1></div>
  </div>

  <!-- タスク入力行 -->
  <div class="row my-3">
    <div class="col-sm-6 mx-auto">
      <input v-model:value="task" placeholder="注文数を入力" class="form-control"><br>
      直前に追加した注文数:{{ previous_num }} <button v-on:click="addTask" class="btn btn-primary">追加</button>
    </div>
  </div>

  <!-- 全てのタスクをクリアするボタン -->
  <div class="row my-3">
    <div class="col-sm-6 mx-auto">
      <button v-on:click="clearAll" class="btn btn-danger">数値をクリア</button>
    </div>
  </div>

  <!-- タスク追加されると表示される部分 -->
  <div class="row my-3">

    <div class="col-sm-6 mx-auto">
    </div>
  </div>
      <div class="col-lg-10 mx-auto" id="stage"></div>  

</div><!-- 全体ここまで -->
<!-- partial -->
  <script src='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.3/js/bootstrap.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/vue-chartjs/3.5.1/vue-chartjs.min.js'></script><script  src="./script.js"></script>

</body>
</html>




JS部分はこちら↓

//必要なパッケージの読み込み
google.charts.load('current', {packages: ['corechart']});
// google.charts.setOnLoadCallback(drawChart);


const app = new Vue({
  el: '#app', // Vueが管理する一番外側のDOM要素
  data: {
    // Vue内部で使いたい変数は全てこの中に定義する
    task: '',
    todoList: [['', '数量'],
        ['1月', 65],
        ['2月', 59],
        ['3月', 80],
        ['4月', 81],
        ['5月', 56],
        ['6月', 55],
        ['7月', 55],
        ['8月', 55],
        ['9月', 55],
        ['10月', 0],
        ['11月', 0],
        ['12月', 0]], // これは配列
    chart:"",
    previous_num:0,
  },
  methods: {
    // 関数はここ
    addTask: function() {
      console.log('次のタスクが追加されました:', this.task);
      // 配列の先頭に現在のタスク内容を追加する(最後尾の場合はpush)
      this.todoList[10][1] = Number(this.todoList[10][1]) + Number(this.task);
      console.log('現在のToDo一覧:', this.todoList);
      this.previous_num = this.task;
      this.drawChart();
    },
    // 以下を追加、関数名はなんでもよい
    clearAll: function() {
      this.todoList = [['', '数量'],
        ['1月', 65],
        ['2月', 59],
        ['3月', 80],
        ['4月', 81],
        ['5月', 56],
        ['6月', 55],
        ['7月', 55],
        ['8月', 55],
        ['9月', 55],
        ['10月', 0],
        ['11月', 0],
        ['12月', 0]];
      console.log('全てのToDoが消去されました');
        this.drawChart();
    },
    drawChart: function() {
      console.log('drawChart');
      //オプション設定
      var options = {
        'title': '注文数',
      };

      //月別データ
      var array = this.todoList;
      var data = google.visualization.arrayToDataTable(array);

      var stage = document.getElementById('stage');

      //グラフの種類を設定
      var chart = new google.visualization.ColumnChart(stage);

      //データとオプションを設定
      chart.draw(data, options);
    }

  },
});

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

VSCodeでファイル保存時にPrettierを走らせる方法

はじめに

macOSを新しく買ってVsCodeをインストールし直した時にファイル保存時にPrettierを走らせる設定方法を忘れてしまい半日以上溶かした。

ググってみたけど体系的にまとまった情報がなくてなかなか時間を溶かしたので

はじめての方でも、この記事を見れば「VSCodeユーザーなら誰でもファイル保存時にPrettierを走らせることができる」ようにいちから丁寧に記事を書いた。

対象読者

  • VSCodeユーザー
  • ファイル保存時にPrettierが走らない人

目次

  1. PrettierをVSCodeにインストール
  2. defaultFormatterの設定を変更する
  3. Format On Saveにチェックを入れる
  4. Settings及びPluginを反映させる為にいったん画面をReloadする

1. PrettierをVSCodeにインストール

VSCodeからインストールする方法とブラウザからインストールする方法がある。

VSCodeを立ち上げて、Prettier -Code formatter の拡張機能タブが表示されたら、[インストール]ボタンをクリックする。これでPrettierが有効になります。

VSCodeからではなく以下のリンクから「install」ボタンをクリックしてもいけます。
Prettier

下の画像のように、VSCodeのインストール済みのところにあればOK。
スクリーンショット 2020-10-26 16.04.58.png

2. defaultFormatterの設定を変更する

まず、VSCodeの設定画面を開く

開くには画面左下の:gear:アイコンから「Settings」を選択すればいい

ちなみにショートカットキーを使えば一発で開けるので参考まで
(macOSの場合 : 「 command + , 」 windowsの場合 : 「 ctrl + , 」)
設定画面.png

次に「defaultformatter」と検索する
null.png

最後に「Default Formatter」を「null」から「esbenp.prettier-vscode」に変更する
defaultformatter.png

3. Format On Saveにチェックを入れる

VSCodeの設定画面を開き「save」と検索する
スクリーンショット 2020-10-26 16.28.50.png

次に「Format On Save」にチェックマークを入れる
スクリーンショット 2020-10-26 16.28.55.png

4 Settings及びPluginを反映させる為にいったん画面をReloadする

これをやらないと設定が反映されないから注意!

「command + shift + P」でコマンドパレットを開き
「Reload Window」をクリックして画面をリロードさせる

(windowsの場合「ctrl + shift + P」でコマンドパレットを開ける)

まとめ

これでファイル保存時にPrettierが走るはず。Just do it!

参考文献

Vscode上でファイル保存時にPrettierを走らせる
デフォルト整形ツールにPrettierを使う
Prettier入門 ~ESLintとの違いを理解して併用する~

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

Tabulatorを使ってみた

AM送信所マップを作ってみたのjsonをTabulatorで表にしてみました。

ローカルファイルからgistのrawデータを読むとCORSのエラーが出るのでTabulatorのajaxは使わず、一旦jqueuryでデータを取得してから、Tabulatorに渡すようにしてみました。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>AM DX LIST</title>
    <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
    <link href="https://unpkg.com/tabulator-tables@4.4.3/dist/css/tabulator.min.css" rel="stylesheet">
    <script type="text/javascript" src="https://unpkg.com/tabulator-tables@4.4.3/dist/js/tabulator.min.js"></script>
    <script>
        function init() {
            $.getJSON("https://gist.githubusercontent.com/yamori813/16065bbff4473e8ec3430570fcf7da7f/raw/radio.json", null, function(data,status){
    //取得成功したら実行する処理
            var tableData = data.radio;
            var table = new Tabulator("#example-table", {
            data: tableData,
            columns: [
            {
                title: "略称",
                field: "name"
            },
            {
                title: "会社",
                field: "corporation"
            },
            {
                title: "コールサイン",
                field: "callsign"
            },
            {
                title: "周波数",
                field: "frequency"
            },
        ]
    });
    });
};

    </script>
</head>
<body onload="init()">
<div id="example-table"></div>
</body>
</html>

Screenshot_2020-10-26 AM DX LIST.png

周波数でソートできるのは便利です。

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

【JS】やっと分かった、クロージャー。

はじめに

【対象】
・クロージャーについて何度も調べて、実践しているが、なかなか理解できない人

・グローバル変数とローカル変数の違いはわかる人

・グローバル変数を出来るだけ使わないようにするのは分かるけど、やり方が分からない人

そもそも、クロージャーとは?

クロージャは、関数と、その関数が宣言されたレキシカル環境の組み合わせです。
MDNより

要は、

「関数を囲むスコープにある変数を参照できる関数」の事。

クロージャーの特徴

①関数が呼ばれるたびに結果が変わる事
②グローバル変数を減らすことで、他のプログラムからの干渉を受けずらく出来る
③処理回数が減らすことが出来る(※重要)
④レキシカルスコープの言語で使う

クロージャーの特徴について説明する前に。

まずは、レキシカルスコープとダイナミックスコープの違いからの説明です。
(分かる人は飛ばしてください。)

・レキシカルスコープとダイナミックスコープ

①レキシカルスコープ

→関数を定義した時点でスコープが決まる。

↓コードで説明

sample.js
var x = 10; 
function A(){
  console.log(x);
}
function B(){
  var x = 1000;
  A();
}
A();  // 10
B(); // 10

function A(){}として関数定義した時点で参照していたxが代入されている。

・メジャーな言語(JSなど)で採用される。

②ダイナミックスコープ(Perl等の言語)

実行時>定義時

→関数定義時にもスコープは決まるが、実行時のスコープ優先。

sample.js
var x = 10; 
function A(){
  console.log(x);
}

function B(){
  var x = 1000;
  A();
}
A();  // 10
B(); // 1000

クロージャーについて1からの説明

先程、記載した通り、

クロージャーとは、
関数を囲むスコープにある変数を参照できる関数の事です。

コードで説明すると、

sample.js
function outer() {
  let place = '大阪';
  console.log(place);

  function inner() {
    alert(place);
  }
  return inner;
}

let Func = outer();

Func();

上から順に説明していきます。
(基本的な所まですべて記述します)

let placeouter(){}のローカル変数。(※outer()実行時以外は参照できない)

console.log(place);outer();で実行される

function inner() {}:これが、クロージャーです。

alert(place);Func();で実行される。

let Func = outer();:ここで、outer();が実行され、また、inner()がクロージャーとなって、代入されている。

ここで分かった、クロージャーの性質は、

outer()が実行されていないのに、outer()内の変数placeが参照される事です。

【上記コード:ここまでの流れのまとめ】

let Func = outer();Func();で関数の実行を行っている

let Func = outer();の定義でのみ、outer()が実行され、console.log(place)が実行される

let Func = outer();の定義でinner()がクロージャーとして代入される

Func();inner()の中身を実行

【特徴①】関数が呼ばれるたびに結果が変わる事について

~【今回行う事】~
関数を呼び出すたびに、数が増えていく処理を行います。

通常の関数では?

【通常】

sample.js
function outer() {
    let count = 1;

    count++;
    console.log(count);
}

outer(); // 2
outer(); // 2
outer(); // 2

上記のコードでは、outer();を何度実行しても呼び出されるたびにcountには1が代入されるため同じ結果しか得れません。

【クロージャー使用】

sample.js
function outer() {
  let count = 1;
  console.log(count);

  function inner() {
    count++;
    console.log(count);
  }
  return inner;
}

let sum = outer(); // 1

sum(); // 2
sum(); // 3
sum(); // 4

実現できました。

【特徴②】グローバル変数を減らすことで、他のプログラムからの干渉を受けずらく出来るについて

【特徴①】のコードと数字の増えるプログラムで説明すると、

下のようなコードでも動くのでは?

sample.js
let count = 1;
console.log(count);

function outer() {
    count++;
    console.log(count); // 1
}

outer(); // 2
outer(); // 3
outer(); // 4

let count = 1;を関数の外で定義しています。

グローバル変数として定義した場合も、思い通りの処理が完成しました!

しかし、

これでは、クロージャーを勉強している意味はありません。

そもそも、クロージャーを使う目的の一つとして

グローバル変数を使わないようにすることで、他のプログラムからの干渉を受けずらくという事があげられます。

グローバル変数を使うことで、変数名が被った際に参照先の違いで思わぬエラーが発生する可能性もあります。

上記のコードでは、グローバル変数countにアクセスしています

それをしないために、クロージャーを使ってカプセル化を行います。

【クロージャー使用】

sample.js
function outer() {
  let count = 1;
  console.log(count);

  function inner() {
    count++;
    console.log(count);
  }
  return inner;
}

let sum = outer(); // 1

sum(); // 2
sum(); // 3
sum(); // 4

何となくメリットが分かってきたような??

【特徴③】処理回数を減らす

→【特徴】の部分でも記載した通り、クロージャーを使うことで、処理回数を減らすことにもつながります。

「クロージャーについて1からの説明」にあるコードを少し変えたのコードで説明します。

【クロージャーなし】

sample.js
function outer() {
  let place = '大阪';
  let word;
  // console.log(place);

  if(place === '大阪') {
    word = 'せやな!';
  } else {
    word = 'ちゃうやん';
  }

  window.alert(word)
}

outer();

上記のコードだと、outer();が呼び出されるたびに毎度ifで処理しないといけません。

しかし、、、

【クロージャーを使うと?】

sample.js
function outer() {
  let place = '大阪';
  let word;
  // console.log(place);

  if(place === '大阪') {
    word = 'せやな!';
  } else {
    word = 'ちゃうやん';
  }

  function inner() {
    window.alert(word)
  }
  return inner;
}

let Func = outer();

Func();

let Func = outer();で一度if分の条件分岐の処理を行った後は、Func();を何度実行しても呼び出されるのは、inner()内部だけです。

見事に、クロージャーの使用で処理回数を減らすことを実現できました!

【おまけ】これは無理なん?と思ったもの

①returnではなく、関数定義内で関数実行

sample.js
function outer() {
  let count = 1;
  console.log(count); // 1

  function inner() {
    count++;
    console.log(count);
  }

  inner(); // 2
}

outer(); // 1 2
outer(); // 1 2
1
2
1
2

上記のコードでは、outer()実行時にouter()内でinner()が実行されてしまっています。

関数を実行するたびにcount = 1count++;しているだけです。

②少し書き方をかえると?

sample.js
function outer() {
  let count = 1;
  console.log(count);

  return function inner() {
    count++;
    console.log(count);
  }
}

let sum = outer(); // 1

sum(); // 2
sum(); // 3
1
2
3

何が変わったか?・・・returnを移動させて、function inner()の前に付加しただけです。

勿論、変わらず動きます。

続いてこちら、

sample.js
let outer = (function() {
  let count = 1;
  console.log(count);

  return function inner() {
    count++;
    console.log(count);
  }
})();

outer(); // 2
outer(); // 3

9行目の最後の()outerを実行。

その後、outer()inner()を実行しています。

こちらも、outer()と`inner()で切り離して実行できます。

まとめ

クロージャーを使うことで、

グローバル変数を減らすことで、他のプログラムからの干渉を受けずらく出来る

・処理回数が減らすことが出来る

参考

【JavaScriptの基礎】レキシカルスコープとクロージャを理解する

私が今までクロージャを理解できなかった理由

【JavaScript入門】クロージャって一体何?使い方まで徹底解説

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

[Java] 配列からランダムに値を取得する

Collections.shuffleを使用

import java.util.Arrays;
import java.util.Collections;
import java.util.List;


List<String> list = Arrays.asList("AA", "BB", "CC");
System.out.println(list); // =>  [AA, BB, CC]

Collections.shuffle(list);
System.out.println(list);  // => [CC, AA, BB]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

babel-loader、eslint-loader、ts-loader比較

webpackを使ってJavascriptとTypescriptのどちらも自由に使える開発環境を作ろう!となったとき、この3つのローダーは機能が重複していたりして「どう使い分けたら…」と悩んだため比較しました。

欲しい機能

  • .ts、.jsファイルそれぞれlintしてほしい
  • TSをJSにコンパイルしてほしい
  • ECMAScript5に変換してほしい

結論から言うと

eslint-loader(2020年10月現在非推奨。代替としてeslint-webpack-pluginを推奨)
- .jsファイルの静的コード分析

ts-loader
- JavascriptとTypescriptをECMAScript5にコンパイル
- .tsファイルの静的コード分析

以上2つのローダーを使用することにしました。

ローダーの特徴を比較

babel-loader

  • JavascriptのES2015、ES2016、ES2017、ES2019、ES2020の構文をECMAScript5に変換
  • プリセット「@babel/preset-typescript」によってTypescriptもコンパイル可能

eslint-loader(eslint-webpack-plugin)

  • Javascriptを静的コード分析

ts-loader

  • TypescriptをECMAScript5に変換
  • Typescriptの静的コード分析
  • tsconfigで"allowJs":trueとした場合Javascriptもあわせてコンパイル(ECMAScript5に変換)可能

導入の際に迷った要素を太字にしました。
こう見ると、ts-loaderとbabel-loaderどちらを選んでも機能の差をあまり感じない気がします。(あくまでTSとJSどちらも使用したい、という環境構築が目的の場合)

各設定のコードはこんなかんじ。

webpack.config.js
module.exports = {

  module: {
    rules: [
      {
        test: /\.(js|ts)$/, //ts-loaderでは拡張子.js.tsどちらも監視
        exclude: /node_modules/,
        loader: "ts-loader"
      },
      {
        test: /\.(js)$/,
        exclude: /node_modules/,
        loader: 'eslint-loader',
      },
  },
  resolve: {
    extensions: [
      '.ts', '.js',
    ],
  },
};

tsconfig.json
{
  "include": [
    "src/scripts/**/*" //対象にしたいディレクトリを指定
  ],
  "compilerOptions": {
    /* Visit https://aka.ms/tsconfig.json to read more about this file */

    /* Basic Options */
    "target": "es5",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
    "module": "es2015",                     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
    "lib": [
      "es2018",
      "dom"
    ],                             /* Specify library files to be included in the compilation. */
    "allowJs": true,                       /* Allow javascript files to be compiled. */
    // "checkJs": true,                       /* ここをtrueにするとTypescriptのlintが.jsにもかかるので注意。Report errors in .js files. */
    "sourceMap": true,                     /* Generates corresponding '.map' file. */
    "downlevelIteration": true,            /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */

    /* Strict Type-Checking Options */
    "strict": true,                           /* Enable all strict type-checking options. */

    /* Source Map Options */
    "inlineSources": true                 /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
  }
}

.eslint.json
{
  "extends": ["eslint:recommended"],
  "plugins": [],
  "parserOptions": {
    "sourceType": "module",
    "ecmaVersion": 2015
  },
  "env": {"browser": true},
  "globals": {},
  "rules": {}
}

重要なのはtsconfig.jsonallowJs: trueと、webpack.config.jseslint-loaderでjsをlintしてからts-loaderが走るような順番で記述することだと思います。
eslintの設定は好みに応じて変更してください。

完成してから思ったのは、babelは開発が活発そう、@babel/preset-typescriptも安心して使えそう、ということでした。。
とはいえ上記組み合わせでも十分求めていた動きをしてくれたので、試しに使ってみてください!

JS初学者が試行錯誤の末行き着いた結果です。色々ご容赦いただければと思います。

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

ShortcutsによるiOSのSafari向けブラウザ拡張機能作成

概要

iOSでもBookmarkletによってSafariで閲覧中のページのDOMなどにアクセス可能なことはよく知られていますが、Shortcutsでも似たようなことが可能ですので、Bookmarkketと比較した場合のメリット・デメリットと併せて、簡単なサンプルを紹介します。

Bookmarkletとの比較

メリット

  • 外部アプリとの連携が可能
    • JavaScriptの実行だけに留まらず、Shortcutsに対応している様々なアプリケーションとの連携が可能です
  • 配布が容易
    • 共有リンクなどを利用して、作成したShortcutを他のユーザに配布することが出来ます
  • Safariの共有メニューから実行可能
    • Bookmarkの一覧からではなく、一般的なアプリと同様に共有メニューから選択して実行することが出来ます
  • Safariだけでなく、TwitterやDiscordなどの内蔵ブラウザ(WebView?)からでも実行可能

デメリット

  • Shortcutsアプリのインストールが必要
  • 閲覧中のページ内JSで定義された変数へアクセスできない? (DOMへのアクセスは可能ですが、ページ内のJSとは別のスコープで実行されているのかも知れません)

※ 適宜追記します

準備

  • Shortcutsアプリをインストールしていない場合はAppStoreからインストールする。
  • 自身が作成したShortcut以外を実行する場合は、信用されていないShortcutの実行を事前に許可する

IMG_0738.PNGIMG_0739.PNG

実践

Safariで閲覧中のWebページのタイトルとURLをTwitterへ投稿するShortcutを作成してみます。

  1. Shortcutsアプリを開き右上の「+」をタップ

image.png
2. 右上の「…」をタップ


3. 「Show in Share Sheet」をタップし、ブラウザの共有メニューから実行可能にする


4. 「Share Sheet Type」から「Safari web pages」のみを選択して、右上の「Done」をタップ


5. [+ Add Action]をタップし、「Run JavaScript on Web Page」を選択


6. 「Web Page」をタップし、「Shortcut Input」を選択


7. 実行するJavaScriptの内容を編集

let result = {};
result.title = document.title;
result.href = document.location.href;
completion(result);
  1. 「+」をタップして「Twitter」を選択


9. 「Tweet」を選択


10.ツイート時の本文を編集して「Next」をタップ

  1. 作成したShortcutのタイトルを設定して「Done」をタップ


12. My Shortcutsの一覧に保存されたことを確認する


13. Safariで任意のページを開き、共有ボタンをタップし、作成したShortcutを選択

  1. 初回実行時にページへのアクセス許可を求められるので許可する

  1. 閲覧中ページのタイトルとURLが入力された状態のツイート画面に遷移する

まとめ

  • ShortcutsアプリからでもBookmarkletの様に、閲覧中のWebページに対してJavaScriptの実行が可能
  • 外部のアプリケーションと連携したブラウザ拡張機能を、iOS端末単体で作成可能
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DOM操作系 まとめ

DOM(Document Object Model)とは、マークアップ言語(HTML,CSS)で書かれたドキュメントにアクセスするための標準的な仕組み。
DOMではドキュメントを文書ツリー(ドキュメントツリー)と言う、ツリー構造として扱う。1つ1つの要素をノードと呼び、要素ノード、属性ノードなど種類別に呼称する。

あるHTMLのdivタグの文章を変えたいとなると

  • 要素ノードを取得
    • 直接取得
    • 文書ツリー間の行き来
    • イベントドリブンモデル
  • 属性値やテキストを取得・設定
    • 取得・設定
    • フォームタグ
  • ノードを追加・置換・削除

と言う順序をもって、HTMLに対して更新していく。

要素ノードの取得

取得のためには、id,タグ名、class,セレクター方式があり、取得するデータが単数か複数かで扱い方やメソッドが複数になるかが変わるので注意!

直接取得

メソッド 概要
getElementById(id) id値をキーとして要素を取得
querySelector(selector) セレクター※後述参照でより複雑な条件で検索する。戻り値は1つで、複数ある場合は一致した最初の値を返す。
querySelectorAll(selector) querySelectorの複数版、一致した全ての要素を返す
getElementsByTagName(name) タグ名をキーとして一致したものを返す。複数出る可能性があるので複数形になっている             メンバー:length(要素数),item(i)(i番目の要素),namedItem(name)(id,name属性が一致する要素)
getElementsByClassName(class) class値をキーとして要素を取得複数ある場合があるため複数形
セレクター 概要
* 全ての要素を取得
#id 指定したid要素を取得
.class 指定したclass要素を取得
* 全ての要素を取得

など多くあるので、都度調べた方がいいと思います。

getXxxxxとqueryXxxxxメソッドの違い

getXxxxxは高速、queryXxxxxは低速と言う処理速度の違いはあるものの、試行回数が多い場合にしか差は出てこず、場合によってはqueryXxxxxの方が簡潔に記述できるので、都度必要に応じて使い分ける。

文書ツリー間の行き来

文書全体から指定のノードを探すことはパフォーマンスの低下になります。そこでDOMではある起点から相対的な位置で探すことができます。これがノードウォーキングと言われるノード間を移動していくやり方です。
コードは長くなりますが、小回りが効くので
getXxxxx/queryXxxxxで特定した後、近接するノードはノードウォーキングで取得する
ことが一般的である。

基準となるノードに対して

 プロパティ 概要
parentNode  親ノード
previousSibling/nextSibling  兄/弟ノード
previousElementSibling/nextElementSibling  兄/弟の要素オブジェクトを返す
firstChild/lastChild  子の最初のノードから最後のノードを順次に取得する
firstElementChild/lastElementChild  子の最初の要素オブジェクトをから最後の要素オブジェクトを順次に取得する
childNodes 子のノード郡を返す

Elementが付いていないものは要素以外の改行や空白のテキストノードも取得してしまう。
そのため、 nodeType プロパティで調べる必要がある。

nodeTypeの返り値の数値と対応表

戻り値 概要 戻り値 概要
1 要素ノード 7 処理命令ノード
2 属性ノード 8 コメントノード
3 テキストノード 9 文書ノード
4 CDATAセクション 10 文書型宣言ノード
5 実体参照ノード 11 文書の断片
6 実体宣言ノード 12 記法宣言ノード

イベントドリブンモデル

HP上では、イベントを基準として特定の処理を開始する。このモデルのことをイベントドリブンモデルと言う。イベントには、click,load,scrollなど多くの種類がありこのイベントが処理の発火基準となる。そして、このイベントをもとに発火される処理のことをイベントハンドラー・イベントリスナーと呼ぶ。違いは書き方で以下参照
定義の仕方は以下の手順となる。

  • どの要素で発生した
  • どのイベントを
  • どのイベントハンドラー・イベントリスナーに紐付けるか

となり、書き方は以下のよ3つがある

  • タグ内に直接記入 (イベントハンドラー)
  • 要素オブジェクトのプロパティとして宣言 (イベントハンドラー)
  • addEventListenerメソッドを使う (イベントリスナー)

タグ内に直接記入

タグ内に直接記入するのは、本当に簡単な処理のみの場合となるが、基本的にコードの可読性から「ページ構成と処理とは明確に分離させる」と言う考えからあまりしない方がいい。

<input type="button" value="ダイアログ表示" onclick="window.alert('ボタンが表示されました。');"/>

イベントをonイベント名="JSのコード" と書く。

要素オブジェクトのプロパティとして宣言

HTMLとJSのファイルに分けて、ページ構成と処理を分離させる。

<input id="btn" type="button" value="ダイアログ表示" />
window.onload = function(){
  document.getElementById('btn').onclick = function(){
    window.alert('ボタンがクリックされました');
  };
};

getElementByIdにおいてid指定で要素を取得、onclickでクリックイベントに =function で処理の関数と定義している。
onloadはページが全て読み込まれた際に実行されるイベントハンドラーを登録している。

addEventListenerメソッドを使う

上記まででかけるのでは?addEventListenerは何のためにあるのか?と思うが、その違いは
・onxxxxは同一要素/同一のイベントに対して、複数のイベントハンドラーを紐付けられない
・addEventListenerは複数のイベントリスナーを紐付けられる
と言う違いがあります。イベントに複数のイベントリスナーを定義したい場合はこちらを使う。
書き方は、少し変わってくるので注意

<input id="btn" type="button" value="ダイアログ表示" />
document.addEventListener('DOMContentLoaded',function(){
  document.getElementById('btn').addEventListener('click',function(){
    window.alert('ボタンがクリックされました');
  },false);
},false);

となる。「DOMContentLoaded」はonloadと同じだが、
・onloadは全てのページを読み込んでから処理を実行
・DOMContentLoadedはコンテンツ本体がロード(画像のロードを待たない)されたところで実行
と言う違いがあり、後者の方が処理が早くなる。基本は後者を使う。

addEventListener('イベント名','処理','イベントの方向')と書く。

以上が、DOMにおいての要素の特定方法となる。これをもとに次は値の取得や設定を行う。

→ 次回:属性値やテキストの取得

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

Transform Feedbackの勉強の一環で、弊社ロゴをパーティクルにしてみた。

初めに

初めまして。ディップ株式会社に20新卒で入社したWebフロントエンドエンジニアの@tato_lolです。

さて、皆さんは弊社の公式サイトを見たことがあるでしょうか?
https://www.dip-net.co.jp/
サイトにアクセスすると、真っ先に目に入るのが、パーティクルで形成されたディップのロゴマークだと思います。

実はこのサイトは皆さんもおなじみのWebGL総本山で紹介されたこともあるサイトで、独特の粘性のあるパーティクルを持ち、websocketを利用して利用者が動かしたパーティクルの座標が他の利用者の画面にも反映されるようになっています。

現状でも中々に魅力的な実装だと思いますが、
僕みたいな頭空っぽマンは
パーティクルいっぱい!!!」とか
すっごい動く!!!」とか
60FPS!!!
みたいな分かりやすくてパワーみのある指標が欲しいので(???)、
WebGL2から追加されたTransform Feedbackを使っていい感じに弊社のロゴを動かしてみようと思います。

Transform Feedback とは?

僕が説明するまでもなく、諸先輩方が素晴らしい解説・サンプルを残してくださっているので、
まずはこちらをご覧になっていただくのが良いと思います。

自分なりの説明

私の説明は、「とりあえず記事を読むためになんとなくの概要だけ教えてくれ!」という方向けの説明にとどめます。

まず、ざっくり説明すると、
Transform Feedback =「GPUの計算結果を使い、頂点データを更新することが出来る機能」です。

これがどういうことなのかというと、今までWebGLにおけるレンダリングの流れは一方通行で
以下のような形になっていました。

従来のWebGLにおけるレンダリング
1. CPUからGPUに頂点データを送信
2. 頂点シェーダ―で頂点データの計算
3. 計算結果をラスタライズ(ピクセルに起こす)
4. フラグメントシェーダでピクセル計算

これが、Transform Feedbackを使うことにより、
以下のような形になりました。

WebGL2におけるTransformFeedbackを使ったレンダリング
1. CPUからGPUに頂点データを送信
2. 頂点シェーダ―で頂点データの計算
3. 頂点データを更新
4. 計算結果をラスタライズ(ピクセルに起こす)
5. フラグメントシェーダでピクセル計算

さらに、頂点データ更新以降の処理をスキップできるようになったため、
このような形にもできます。

WebGL2におけるTransformFeedbackを使ったレンダリング(短縮)
1. CPUからGPUに頂点データを送信
2. 頂点シェーダ―で頂点データの計算
3. 頂点データを更新

Transform Feedbackを使うメリット

従来までは、頂点データは最後まで一方通行に描画されたグラフィックスを更新する形で処理を行う必要がありました。
まず、これがラスタライズ以降の処理をスキップできるようになったことで描画を行わず計算のみを行うことができ、より効率的に処理できるようになりました。
加えて、ここでは深く説明しませんが、VBOの中身を直接GPUで変更できるようになったため、よりシンプルに頂点を扱えるようになりました。

本題

では、実際に作成したものがこちらになります。
DEMO:https://yoheisuda.github.io/my-transform-feedback-sample/

my transform feedback sample - Google Chrome 2020_10_10 15_17_18 (2).png

こちらは、h_doxasさんのwgld.org | WebGL2: Transform Feedback で GPGPU |を参考に作成したものです。

Transform Feedbackを使うことにより、Intel UHD Graphics 620というクソザコGPUの私のPCでも、
60FPSを保ちつつ、160,000パーティクルを飛ばすことが出来ました。

実装

参考にさせていただいたサンプルとそこまで変わりません(大きな違いとしては座標のリセットを加えた部分ぐらいかと思います)が、

var VBOArray = [
create_vbo(position),
create_vbo(velocity),
create_vbo(color),
create_vbo(resetPos)
];

このようにVBOを定義し、

set_attribute(VBOArray, attLocation, attStride);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, VBOArray[0]);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 1, VBOArray[1]);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 2, VBOArray[2]);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 3, VBOArray[3]);
gl.enable(gl.RASTERIZER_DISCARD);
gl.beginTransformFeedback(gl.POINTS);

gl.uniform1f(uniLocation[0], nowTime);
gl.uniform2fv(uniLocation[1], mousePosition);
gl.uniform1f(uniLocation[2], mouseMovePower);
gl.uniform1f(uniLocation[3], resetFlg);
gl.drawArrays(gl.POINTS, 0, imageWidth * imageHeight);

gl.disable(gl.RASTERIZER_DISCARD);
gl.endTransformFeedback();
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 1, null);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 2, null);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 3, null);

render関数の中で、Transform Feedback を実行し、
処理が終わった後、閉じるという形で実装しています。

より詳しい中身については、下記のサンプルを見て頂ければと思います。
https://github.com/YoheiSuda/my-transform-feedback-sample

まとめ

Transform Feedback使うと、軽量に多くのパーティクルを動かせるので良いですね。
まだまだ自分で理解できていない部分も多いため、これからも勉強していきます。

お読みいただきありがとうございました。

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

連想配列をJavaScriptで要素を簡易的に追加する

メモ用。
連想配列のに要素をforEach等で配置する

const arr = [
{name: 'aaa', image: "aaa.JPG", disc: 'text~'},
{name: "bbb", image: "bbb.JPG", disc: 'text~'},
];

function viewList(content, cls) {
const list = document.createElement('li');
const img = document.createElement('img');
if (cls === 'toImage') {
img.classList.add(${cls})
img.src = content;
} else {
list.classList.add(${cls});
list.textContent = content;
}
list.appendChild(img);
ul.appendChild(list);
}

arr.forEach(ar => {
viewList(ar.name, 'class名');
viewList(ar.image, 'class名');
viewList(ar.disc, 'class名');
})

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

JSONをconsole.logで整形して表示する(JSON.stringifyでインデント付き展開)

はじめに

JSONデータをインデントと改行で整形してconsole.log()で出力します。
方法は、JSON.stringify()に3つの引数を指定するだけで簡単です。

  • PHPのvar_dumpやprint_rのようなことをしたい。

のような場合に便利に使えます。

JSON.stringify()を使う

構文
JSON.stringify(JSONオブジェクト, null, インデント数);

まず、JSON.stringify()で指定できる3つの引数を簡単に確認しておきましょう。

第1引数:jsonオブジェクトを指定します。
第2引数:replacerといってコールバックを指定できますが、今回は整形用にnullを指定します。
第3引数:数値を指定すると、指定数の空白文字でjsonオブジェクトをインデント整形します。

サンプル

インデントで整形

javascript
// JSONオブジェクト
var obj = {
    "name": "taro",
    "date": "2020-10-25 12:30:18",
    "item": [
        "ラーメン",
        "チャーハン",
        "餃子"
    ]
}

// 空白2文字でインデントして整形出力(インデントの個数:2)
console.log(JSON.stringify(obj, null, 2));

インデントの個数は「2」にしています。

console.log()
{
  "name": "taro",
  "date": "2020-10-25 12:30:18",
  "item": [
    "ラーメン",
    "チャーハン",
    "餃子"
  ]
}

タブで整形

javascript
// JSONオブジェクト
var obj = {
    "name": "taro",
    "date": "2025-10-20 12:30:18",
    "item": [
        "ラーメン",
        "チャーハン",
        "餃子"
    ]
}

console.log(JSON.stringify(obj, null, '\t'));

第3引数に'\t'と指定すれば、タブによるインデントも可能です。

console.log()
{
    "name": "taro",
    "date": "2020-10-20 12:30:18",
    "item": [
        "ラーメン",
        "チャーハン",
        "餃子"
    ]
}

整形をしない場合

第1引数のみ指定
// JSONオブジェクト
var obj = {
    "name": "taro",
    "date": "2020-10-25 12:30:18",
    "item": [
        "ラーメン",
        "チャーハン",
        "餃子"
    ]
}

// 第1引数のみ指定
console.log(JSON.stringify(obj));

// 出力結果
{"name":"taro","date":"2020-10-25 12:30:18","item":["ラーメン","チャーハン","餃子"]}

整形の必要がない場合は、第1引数のみで指定OK。

オブジェクトの中身を確認したい

javascript
var obj = {
    "name": "taro",
    "date": "2020-10-25 12:30:18",
    "item": [
        "ラーメン",
        "チャーハン",
        "餃子"
    ]
}

console.dir(obj);

[object Object]で出力される、オブジェクトの中身を確認したい場合はconsole.dir()を使うと便利です。

console.dir()
Object
date: "2020-10-25 12:30:18"
item: Array(3)
0: "ラーメン"
1: "チャーハン"
2: "餃子"
length: 3
__proto__: Array(0)
name: "taro"
__proto__: Object

コンソールで展開して、このように表示できます。

参考

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify

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

windowオブジェクトを超ミニマムに解説してみる

超ミニマムシリーズとは

様々な事柄に関して超短く解説するシリーズです。
これを読んであなたの興味が沸き、その飽くことのない知識欲を満たすことを願っています。

windowオブジェクトとは

ブラウザに標準で用意されているグローバルなオブジェクト

意識していないだけで必ずwindowオブジェクトは使っている

皆さんが何気なく使っている関数やメソッドは、windowオブジェクトの中のものを使っている場合が多いです。
例えば次のコード

main.js
alert('hello');

↑のalert()は文字通り、ブラウザ上で警告文を出すためのメソッドです。
実はこのalert()の先頭には隠れてるコードがあって、それがwindowオブジェクトです。
つまり、

main.js
window.alert('hello');

↑のように書いても同じ結果になります。
普段はこのwindow.の部分を省略してるということですね。

main.js
console.log(window);

試しにconsole.log(window)でwindowオブジェクトを見てみると
image.png
このように多くの関数やプロパティなどが出てきます。その中にalertもあります。

おまけ(グローバルなオブジェクトとは)

どこからでもアクセスできるオブジェクト
windowオブジェクトはどこからでもアクセスできるので省略して書けるということです。

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