- 投稿日:2019-09-15T20:50:02+09:00
送信フォームで二重送信が起きる場合に疑う場所
比較的レアな状況だと思うが2度ほど同じ過ちに出会ったので記録。
フォームを送信する場合になぜかバリデーションが誤作動した。
原因を辿ると以下のようなコードが。<body> <form action="testresult.php" method="POST" name="testform"> <input type="text" name="test1"> <input type="submit" onclick="submit_form()"> </body> <script> function submit_form(){ document.testform.submit(); } </script>
testresult.php
にフォームを送信したくて、そのためにinput type="submit"
としている。
その時点でフォームは送信できているが、更にJavaScriptでもsubmitする関数をonClickで呼び出してしまっている。結果的にこのフォームは二重で送信されてしまう。
これを防ぐには、input type="submit"
をinput type="button"
に変えてしまうか、JavaScriptの関数を削除する。後者の方がすっきりする。
- 投稿日:2019-09-15T20:50:02+09:00
送信フォームで二重送信が起きる場合には
比較的レアな状況だと思うが2度ほど同じ過ちに出会ったので記録。
フォームを送信する場合になぜかバリデーションが誤作動した。
原因を辿ると以下のようなコードが。<body> <form action="testresult.php" method="POST" name="testform"> <input type="text" name="test1"> <input type="submit" onclick="submit_form()"> </body> <script> function submit_form(){ document.testform.submit(); } </script>
testresult.php
にフォームを送信したくて、そのためにinput type="submit"
としている。
その時点でフォームは送信できているが、更にJavaScriptでもsubmitする関数をonClickで呼び出してしまっている。結果的にこのフォームは二重で送信されてしまう。
これを防ぐには、input type="submit"
をinput type="button"
に変えてしまうか、JavaScriptの関数を削除する。後者の方がすっきりする。
- 投稿日:2019-09-15T20:02:34+09:00
WebアプリSHISHOWを開発したときの備忘録④firestore設定編
Firestoreの設定
前回でfirebaseの基本的な設定を終えたので,今回はfirestoreに関する設定を行っていきます。
まず,`
~/plugins/firestore.js
に追加で書き込みをします。firestore.jsimport firebase from 'firebase'; // Your web app's Firebase configuration var firebaseConfig = { apiKey: "自分のAPIキー", authDomain: "project1-aae93.firebaseapp.com", databaseURL: "https://project1-aae93.firebaseio.com", projectId: "project1-aae93", storageBucket: "project1-aae93.appspot.com", messagingSenderId: "598499664640", appId: "1:598499664640:web:fdeefdfb150f0d823009e5" }; // Initialize Firebase firebase.initializeApp(firebaseConfig); export default firebase;
firestore.js
をあとで違うファイルから読み込みたかったので,最後にexport default firebase
を追加しました。そして,次は
~/pages/index.vue
に移動してfirestoreに関する記述を書いていきます。index.vue<template> <div class="container"> <div> <logo /> <h1 class="title"> shishow </h1> <h2 class="subtitle"> My legendary Nuxt.js project </h2> <div class="links"> <a href="https://nuxtjs.org/" target="_blank" class="button--green" > Documentation </a> <a href="https://github.com/nuxt/nuxt.js" target="_blank" class="button--grey" > GitHub </a> </div> </div> </div> </template> <script> import Logo from '~/components/Logo.vue' import firebase from '~/plugins/firestore'; import firestore from 'firebase/firestore'; export default { components: { Logo } } </script> <style> .container { margin: 0 auto; min-height: 100vh; display: flex; justify-content: center; align-items: center; text-align: center; } .title { font-family: 'Quicksand', 'Source Sans Pro', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; display: block; font-weight: 300; font-size: 100px; color: #35495e; letter-spacing: 1px; } .subtitle { font-weight: 300; font-size: 42px; color: #526488; word-spacing: 5px; padding-bottom: 15px; } .links { padding-top: 15px; } </style>
import firestore from 'firebase/firestore'
という記述を加えただけですが,これでfirestoreが使えるようになっているはずです。実際に記述してみる
今のindex.vue
の状態は
こんな感じですが,
このshishowという文字をクリックしたらfirestoreに"shishow"という文字列が"SHISHOW"コレクションの中の自動生成されるドキュメントの中に登録してみましょう。まずは,クリックイベントを発生させます。
index.vue<h1 class="title" @click="addSHISHOW()"> shishow </h1>これで文字をクリックした時にaddSHISHOW()関数が呼び出されるようになりました。
次は,クリックイベントで呼び出されるaddSHISHOW関数を作成しましょう。
index.vue<script> import Logo from '~/components/Logo.vue' import firebase from '~/plugins/firestore'; import firestore from "firebase/firestore"; export default { components: { Logo }, methods: { addSHISHOW: function() { console.log('shishow'); } } } </script>これで,Webコンソールにshishowと表示されたらクリックイベントの呼び出しには成功しているので,次はfirestoreの記述を書いていきましょう。
index.vue<script> import Logo from '~/components/Logo.vue' import firebase from '~/plugins/firestore'; import firestore from "firebase/firestore"; const db = firebase.firestore(); export default { components: { Logo }, methods: { addSHISHOW: function() { db.collection('SHISHOW') .doc() .set({ key: 'shishow', }) } } } </script>これで,shishowの文字のところをクリックすると
このような感じでデータが追加されていると思います。
これでfirestoreが利用できるようになったかと思います。
- 投稿日:2019-09-15T19:15:25+09:00
WebAssemblyでお絵描きチャット"8bitpaint chat"を作った
公開先
https://minordaimyo.net/8bitpaintchat/
(現在デバッグ中)8bitpaint chatの主な特徴・使い方
- 高解像度のキャンバス(A4 600dpi相当)で軽快な描き味
- 筆圧と傾き検知に対応
- 参加人数は8人まで(ROMは現状16人まで)
- 使える色は入室時に割り当てられた1色のみ
- 任意の色をミュート(非表示)にできる機能
- 2本指のタッチ操作でキャンバスのスクロール・拡縮
- 2本指タップでUndo、3本指タップでRedo
- キャンバスのダウンロード機能(pngとpsd形式)
動作環境
Windows上のChromeとペンタブレット・液晶タブレット
iPad上のSafariとApple-pencil
Android上のChrome
等々ぎじゅつてきなこと
クライアントサイドはjs+WebAssembly(主にc言語)
サーバーサイドはnode.js+WebAssembly(クライアントと共通のコード)
で作成。キャンバスサイズは、7016x4961pixel(A4 600dpiと同じ)。
クライアント側の消費メモリは300MBくらい、
サーバー側の消費メモリは150MBくらい。
サーバー側にもキャンバスを保持しているため、メモリ消費が大きくなっています。ユーザーインターフェース周りを中心にjsを使用。
お絵描き機能部分はほぼc言語で記述(WebAssembly)。苦労したこと
c言語は割と得意だけどWebプログラミングは全くの素人。
そのためjsでの記述部分はかなりの糞コードになってしまった…
あとまだデバッグ中なので、不具合は結構あります。その他
初めてQiitaで発信しました。
必要なことは後から書き足す予定です。
- 投稿日:2019-09-15T18:31:21+09:00
【Nuxt.js】WebアプリSHISHOWを開発したときの備忘録③firebase基本設定編
もう一度プロジェクトを作成して確認しながら書きました。
Firebaseの導入
データベースやらホスティングやらが便利なので,firebaseの機能を導入します。
まずはfirebaseのページでプロジェクトを作成し,
そのプロジェクトにWebアプリを追加します。そしてCLIに
npm install -g firebase-toolsそしてgoogleのアカウントでログインをします。
firebase loginログインが完了したら,
firebase initを実行します。
? Which Firebase CLI features do you want to set up for this folder? Press Space to select features, then Enter to confirm your ch oices. Database: Deploy Firebase Realtime Database Rules, Firestore: Deploy rules and create indexes for Firestore, Functions: Con figure and deploy Cloud Functions, Hosting: Configure and deploy Firebase Hosting sites === Project Setup First, let's associate this project directory with a Firebase project. You can create multiple project aliases by running firebase use --add, but for now we'll just set up a default project. ? Please select an option: Use an existing project ? Select a default Firebase project for this directory: project1-aae93 (project1) i Using project project1-aae93 (project1) === Database Setup Firebase Realtime Database Rules allow you to define how your data should be structured and when your data can be read from and written to. ? What file should be used for Database Rules? database.rules.json ? File database.rules.json already exists. Do you want to overwrite it with the Database Rules for project1-aae93 from the Firebas e Console? Yes ✔ Database Rules for project1-aae93 have been downloaded to database.rules.json. Future modifications to database.rules.json will update Database Rules when you run firebase deploy. === Firestore Setup Firestore Security Rules allow you to define how and when to allow requests. You can keep these rules in your project directory and publish them with firebase deploy. ? What file should be used for Firestore Rules? firestore.rules Firestore indexes allow you to perform complex queries while maintaining performance that scales with the size of the result set. You can keep index definitions in your project directory and publish them with firebase deploy. ? What file should be used for Firestore indexes? firestore.indexes.json === Functions Setup A functions directory will be created in your project with a Node.js package pre-configured. Functions can be deployed with firebase deploy. ? What language would you like to use to write Cloud Functions? JavaScript ? Do you want to use ESLint to catch probable bugs and enforce style? No ✔ Wrote functions/package.json ✔ Wrote functions/index.js ✔ Wrote functions/.gitignore ? Do you want to install dependencies with npm now? Yes > protobufjs@6.8.8 postinstall /Users/kunosouichirou/Desktop/polp/functions/node_modules/protobufjs > node scripts/postinstall npm notice created a lockfile as package-lock.json. You should commit this file. added 233 packages from 180 contributors and audited 662 packages in 7.21s found 0 vulnerabilities === Hosting Setup Your public directory is the folder (relative to your project directory) that will contain Hosting assets to be uploaded with firebase deploy. If you have a build process for your assets, use your build's output directory. ? What do you want to use as your public directory? public ? Configure as a single-page app (rewrite all urls to /index.html)? No ✔ Wrote public/404.html ✔ Wrote public/index.html i Writing configuration info to firebase.json... i Writing project information to .firebaserc... ✔ Firebase initialization complete!これでfirebaseの初期設定はできたはずです。
そして,npmでfirebaseをインストールします。
npm install --save firebaseこのとき,インストールのコマンドを実行するディレクトリを間違えるとうまく動かないかもしれません。
次は,firebaseのキーを取得してきます。
この歯車のアイコンのところの「プロジェクトの設定」を選択し,Firebase SDK snippetのところにあるスクリプトをコピーします。// Your web app's Firebase configuration var firebaseConfig = { apiKey: "自分のapiキー", authDomain: "project1-aae93.firebaseapp.com", databaseURL: "https://project1-aae93.firebaseio.com", projectId: "project1-aae93", storageBucket: "project1-aae93.appspot.com", messagingSenderId: "598499664640", appId: "1:598499664640:web:fdeefdfb150f0d823009e5" }; // Initialize Firebase firebase.initializeApp(firebaseConfig); firebase.analytics();そして,Nuxtのプロジェクトの中のpluginフォルダの中にfirestore.jsを作成し,その中にコピーしていきます。
firestore.jsvar firebaseConfig = { apiKey: "AIzaSyBbyxTMzjGkPhSyKDl_-gAEsBjSPoqWukM", authDomain: "project1-aae93.firebaseapp.com", databaseURL: "https://project1-aae93.firebaseio.com", projectId: "project1-aae93", storageBucket: "project1-aae93.appspot.com", messagingSenderId: "598499664640", appId: "1:598499664640:web:fdeefdfb150f0d823009e5" }; // Initialize Firebase firebase.initializeApp(firebaseConfig);僕は今回analyticsは使っていないのでfirebaseConfigのところからその部分は削除してあります。
これでfirebaseの基本的な設定はできました。
次回はfirestoreなどの設定について書いていきます。
- 投稿日:2019-09-15T18:24:32+09:00
【Vue.js】ミックスインでコンポーネントオプションを追加する
ミックスインとは
- オプションを他のコンポーネントに混ぜ込む(マージする)機能
- 複数のコンポーネントで共通の処理を実行したい場合に使える
- ミックスインオブジェクトとコンポーネントの定義がコンフリクトした場合は、コンポーネントのデータが優先される
- フック(createdなど)は、ミックスインに定義されているものから先に呼び出される
- 1つのコンポーネントから複数のミックスインオブジェクトを呼び出すことも可能
- 複数のミックスインオブジェクト同士で定義がコンフリクトしている場合は、後で呼び出されているオブジェクトの定義が優先される
- 上記の場合、createフックは先に呼び出されているミックスインオブジェクトから実行される
補足
extendとdataオプション
extendを使用する場合、extendしたいオブジェクトのdataオプションの記述方法が少し異なります。
この記述方法だとエラーが発生します。
vue_mixin_test.html<script> data: { foo: 'abc', message: 'hello' }, </script>[Vue warn]: The "data" option should be a function that returns a per-instance value in component definitions.メッセージの通り、dataオプションをfunctionで返せば問題ありません。
以下の記述方法はextendを使用しない場合も使えるので、こちらの方が無難かもしれません。vue_mixin_test.html<script> data: function () { return { foo: 'abc', message: 'hello' } }, </script>公式サイトにも説明があります。
ライフサイクルフック
createdフックなど、インスタンス生成時に初期化として実行される関数のことです。
ミックスインオブジェクトに定義されているフック関数は、コンポーネントのフック関数より先に呼び出されます。
ミックスインオブジェクトはこの時点で既にマージされているので、定義がコンポーネントとコンフリクトしている場合は、コンポーネントのデータが呼び出されます。フック関数は複数あり、それぞれ実行されるタイミングが異なります。
ソースコード
公式サイトからお借りしました。
vue_mixin_test.html<!DOCTYPE> <head> <meta charset="UTF-8"> <title>Vue.js_mixin_test</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <script> var mixin = { data: function () { return { hoge: 'hoge', foo: 'abc', // 重複するプロパティ message: 'hello' } }, methods: { fooMethod: function () { console.log('foo') }, // 重複するメソッド conflicting: function () { console.log('from mixin') } }, // this.$dataはmixinがマージされた後の状態で表示される created: function () { console.log('mixin', this.$data) this.fooMethod() this.conflicting() } } var mixin2 = { data: function () { return { foo: 'abc2', message: 'hello2' } }, methods: { fooMethod: function () { console.log('foo2') }, conflicting: function () { console.log('from mixin2') } }, created: function () { console.log('mixin2', this.$data) this.fooMethod() this.conflicting() } } // 複数のmixinを呼び出すことも可能 var Component = Vue.extend({ mixins: [mixin, mixin2] }) new Component() var vm = new Vue({ // mixinをマージする mixins: [mixin], // mixinと定義が重複している場合、コンポーネントのデータが優先される data: function () { return { bar: 'def', // 重複するプロパティ message: 'goodbye' } }, methods: { barMethod: function () { console.log('bar') }, // 重複するメソッド conflicting: function () { console.log('from self') } }, created: function () { console.log('self', this.$data) this.barMethod() this.conflicting() } }) vm.fooMethod() vm.barMethod() vm.conflicting() </script> </body>
- 投稿日:2019-09-15T17:28:03+09:00
東工大ポータルに自動ログインする
内容
Tampermonkey
Chrome版 https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo?hl=ja
Firefox版(スマホ対応) https://addons.mozilla.org/ja/firefox/addon/tampermonkey/
Opera版 https://addons.opera.com/ja/extensions/details/tampermonkey-beta/を使ってパソコン・スマホから1クリックで自動ログインさせる
(最終編集日 2019/09/15)
やり方
※パソコン・スマホ版もやり方は同じ
手順1
・上のURLからTampermonkeyをインストールする
・以下のURLからTampermonkey用のスクリプトをインストールする
https://greasyfork.org/ja/scripts/390145-tokyotech-portal-login-%E6%9D%B1%E5%B7%A5%E5%A4%A7%E3%83%9D%E3%83%BC%E3%82%BF%E3%83%AB%E3%83%AD%E3%82%B0%E3%82%A4%E3%83%B3・スクリプト内の
(document,window,location,0,'00B00000','password',['1111111111','2222222222','3333333333','4444444444','5555555555','6666666666','7777777777']);の部分を自分の学籍番号,パスワード,マトリックスコードに書き換える
手順2
・以下のログイン画面をブックマークに追加する
https://portal.nap.gsic.titech.ac.jp/GetAccess/Login?Template=userpass_key&AUTHMETHOD=UserPassword・ブックマークより自動ログインできるか確認する
おわり
補足
Tampermonkeyに対応していないブラウザを使っている場合には以下のスクリプトをブックマークレットとして追加する
該当部分は学籍番号,パスワード,マトリックスコードに書き換える
ただし3回もクリックしないといけないのでめんどくさいjavascript:( function(d,w,n,j,i,p,m){ var l=d.login,f=d.getElementsByTagName('input'), t, c; switch(n.search.replace(/[&?]Template=([^&]*)(&.*)?/,'$1')){ case 'userpass_key': l.usr_name.value=i; l.usr_password.value=p; l.submit(); break; case 'idg_key': while(++j-4){ t=f.item(j); c=t.parentNode.parentNode.parentNode.parentNode.getElementsByTagName('th')[0].innerHTML; t.value=m[c.match(/[1-7]/)[0].charCodeAt(0)-'1'.charCodeAt(0)].charAt(c.match(/[A-J]/)[0].charCodeAt(0)-'A'.charCodeAt(0)); } l.submit(); break; default: if(n.host+n.pathname=='wlanauth.noc.titech.ac.jp/fs/customwebauth/login.html'){ d.getElementById('username').value=i; d.getElementById('password').value=p; submitAction(); }else{ w.open('https://portal.nap.gsic.titech.ac.jp/GetAccess/Login?Template=userpass_key&AUTHMETHOD=UserPassword', '_blank'); } } } ) (document,window,location,0,'00B00000','password',['1111111111','2222222222','3333333333','4444444444','5555555555','6666666666','7777777777']);参考
東工大ポータルとtitech-pubnetのログイン自動化のスクリプト(とブックマークレット)書いた
http://l1048576.blogspot.com/2014/05/titech-automatrix1.htmlChrome拡張「Tampermonkey」で閲覧ウェブサイトをカスタマイズ!
https://www.lisz-works.com/entry/chrome-tampermonkey
- 投稿日:2019-09-15T16:44:02+09:00
Vue.js で Moment.js を使ってお手軽に日付フォーマットする
Vue.js のフィルタでフォーマット
Moment.js を使ったフィルタを用意すると以下のようにお手軽に日付のフォーマットが可能です。
<template> <h1>{{ new Date() | moment('LTS') }}</h1> </template>JavaScript の日付処理は罠が多い
Date#getYear
で1900年からの経過年数が返るDate#getMonth
で 0 ~ 11 が返る。JavaScript の日付処理ライブラリである Moment.js を使えばそういった罠を回避し、お手軽に JavaScript の日付を扱うことができます。
現在、Github のスターは 40000 を超え JavaScript の日付処理ライブラリでは最もメジャーなものの一つです。
フィルタの作り方
サンプルプロジェクトを作成します。
既存のプロジェクトに組み込む場合、ここは必要ありません。# Vue CLI のインストール ※すでにインストール済みなら必要なし $ npm install -g @vue/cli $ npm install -g @vue/cli # yarn global add @vue/cli $ cd 任意のディレクトリ $ vue create moment-filter $ cd moment-filterMoment.js をインストールします。
# Moment.jsをインストールする $ npm install moment --save # yarn add momentMoment.js を使ったフィルタを作成します。
import moment from "moment"; /* 中略 */ filters: { /** * @param {Date} value - Date オブジェクト * @param {string} format - 変換したいフォーマット */ moment(value, format) { return moment(value).format(format); } }上記で完成です。簡単。
テストの表示
テストデータを用意して試してみましょう。
data() { const formats = [ "MMMM Do YYYY, h:mm:ss a", "dddd", "MMM Do YY", "YYYY [escaped] YYYY", "LTS" ]; return { tests: new Array(formats.length) .fill(new Date()) .map((date, i) => ({ date, format: formats[i] })) }; }今回はとりあえず以下のように並べて表示してみます。
<template> <div id="app"> <h2 v-for="({date, format}, index) in tests" :key="index">{{date | moment(format)}}</h2> </div> </template>ローカルサーバーを立ち上げます。
$ npm run serve # yarn serveフォーマットされた日付が表示できました。
今回作成したコンポーネントの全体は以下のとおりです。
./src/App.vue<template> <div id="app"> <h2 v-for="({date, format}, index) in tests" :key="index">{{date | moment(format)}}</h2> </div> </template> <script> import moment from "moment"; export default { name: "app", data() { const formats = [ "MMMM Do YYYY, h:mm:ss a", "dddd", "MMM Do YY", "YYYY [escaped] YYYY", "LTS" ]; return { tests: new Array(formats.length) .fill(new Date()) .map((date, i) => ({ date, format: formats[i] })) }; }, filters: { moment(value, format) { return moment(value).format(format); } } }; </script> <style> #app { font-family: "Avenir", Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
- 投稿日:2019-09-15T16:15:32+09:00
【JavaScript】分割代入はネストできる
分割代入 (Destructuring) は下のように配列やオブジェクトを分解して代入することができる。
const [x, y] = [1, 2, 3] // x = 1, y = 2 const {a, b, c} = {a: 1, b: 2} // a = 1, b = 2, c = undefinedさらに destructuring は下のようにネストできる。
const [[x, y], [z, w]] = [[3, 2, 5], [4]] // x = 3, y = 2, z = 4, w = undefined const {a: {a1, a2}, b: {b1}} = {a: {a1: 1}, b: {}} // a1 = 1, a2 = undefined, b1: undefinedオブジェクトと配列を組み合わせることもできる。
const [{x, y}, [z, w]] = [{x: 1}, [2, 3]] // x = 1, y = undefined, z = 2, w = 3エラーと回避
以下の場合エラーが発生するので注意する必要がある。
undefined
やnull
が オブジェクトとして destructure されるとき- iterable1 でない値が配列として destructure されるとき
// プロパティ a の値 undefined がオブジェクトとして destructure されている const {a: {b}} = {} // Uncaught TypeError: Cannot destructure property `b` of 'undefined' or 'null'. // 2番目の要素 5 が配列として destructure されている const [a, [b, c]] = [3, 5] // Uncaught TypeError: undefined is not a function上記のケースのうち、「存在しない要素/プロパティを destructure するとき」と「
undefined
を destructure するとき」に発生するエラーはデフォルト値{}
[]
を指定することで回避できる。const {a: {b} = {}} = {} // b: undefined const [a, [b, c] = []] = [3] // a = 3, b = undefined, c = undefinedこれは API の JSON レスポンスなどから値を取り出すときに非常に便利である。
Twitterの例const { user: { name, entities: { url: { urls = [] } = {} } = {} } = {} } = tweetObject
- 投稿日:2019-09-15T14:30:53+09:00
【Vue.js】computedとmethodとwatchの使い分け
以下3つのオプションの区別が曖昧だったので、メモしておきます。
ソースコードは公式サイトよりお借りしました。computedオプション
- 算出プロパティ
- リアクティブな依存関係にもとづきキャッシュされる
- リアクティブな依存関係が更新されたときにだけ再評価されるので、逆に言えばリアクティブな依存がない場合は二度と更新されない
- ゲッター(getter関数)とセッター(setter関数)の両方が利用できる(デフォルトはゲッターのみ)
ゲッターとセッター
computedでセッターを使用した場合、dataオプションで設定されたプロパティを更新することもできます。
ゲッター
特定のプロパティ値を取得するためのメソッド
セッター
特定のプロパティ値を設定するためのメソッド
ソースコード
firstNameプロパティやlastNameプロパティを変更すると再描画が起こりますが、nowプロパティはリアクティブな依存関係にないため、最初に読み込まれた以降は変化しません。
vue_test01.html<!DOCTYPE> <head> <meta charset="UTF-8"> <title>Vue.js_test01</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <input v-model="firstName" placeholder="firstName"> <input v-model="lastName" placeholder="lastName"> <p>firstName: {{ firstName }}</p> <p>lastName: {{ lastName }}</p> <p>fullName: {{ fullName }}</p> <p>now: {{ now }}</p> </div> <script> var vm = new Vue({ el: '#app', data: { firstName: '', lastName: '' }, computed: { // fullName/now: 算出プロパティ名 fullName: function () { return this.firstName + ' ' + this.lastName }, now: function () { return Date.now() } } }) </script> </body>methodsオプション
- メソッド
- 再描画が起きると常に関数を実行する
ソースコード
firstNameプロパティやlastNameプロパティを変更すると再描画が起こるので、それに合わせてnowプロパティも変化します。
vue_test01.html<!DOCTYPE> <head> <meta charset="UTF-8"> <title>Vue.js_test01</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <input v-model="firstName" placeholder="firstName"> <input v-model="lastName" placeholder="lastName"> <p>firstName: {{ firstName }}</p> <p>lastName: {{ lastName }}</p> <p>fullName: {{ fullName() }}</p> <p>now: {{ now() }}</p> </div> <script> var vm = new Vue({ el: '#app', data: { firstName: '', lastName: '' }, methods: { fullName: function () { return this.firstName + ' ' + this.lastName }, now: function () { return Date.now() } } }) </script> </body>watchオプション
- 監視プロパティ
- 既にセットされているプロパティを監視する
監視するプロパティの名前
と、そのプロパティに変化(トリガー)があった場合に実行する関数(ハンドラ)
を対にして指定する- 関数は、更新後・更新前のプロパティの値を引数に取ることができる
- 処理は実行するが、データは返さない
- computedでは行えないコストの高い処理を実行できる
以下、公式サイトより。
この場合では、watch オプションを利用することで、非同期処理( API のアクセス)の実行や、処理をどのくらいの頻度で実行するかを制御したり、最終的な answer が取得できるまでは中間の状態にしておく、といったことが可能になっています。これらはいずれも算出プロパティでは実現できません。
ソースコード
fullNameプロパティの表示はwatchオプションで描画することもできますが、逆に冗長なコードになってしまいます。
vue_test01.html<!DOCTYPE> <head> <meta charset="UTF-8"> <title>Vue.js_test01</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <input v-model="firstName" placeholder="firstName"> <input v-model="lastName" placeholder="lastName"> <p>firstName: {{ firstName }}</p> <p>lastName: {{ lastName }}</p> <p>fullName: {{ fullName }}</p> <p>now: {{ now }}</p> </div> <script> var vm = new Vue({ el: '#app', data: { firstName: '', lastName: '', // プロパティはあらかじめセットしておく fullName: '', now: '', }, watch: { // firstName/lastName/now: 監視対象のプロパティ名 // newValue: 更新後のプロパティの値 // oldValue: 更新前のプロパティの値 firstName: function (newValue, oldValue) { console.log(newValue, oldValue); this.fullName = newValue + ' ' + this.lastName }, lastName: function (newValue, oldValue) { console.log(newValue, oldValue); this.fullName = this.firstName + ' ' + newValue }, // 監視しているが、値に変化がないので変更されることはない now: function () { return Date.now() } } }) </script> </body>参考サイト
- 投稿日:2019-09-15T14:30:53+09:00
【Vue.js】computedとmethodsとwatchの使い分け
以下3つのオプションの区別が曖昧だったので、メモしておきます。
ソースコードは公式サイトよりお借りしました。computedオプション
- 算出プロパティ
- リアクティブな依存関係にもとづきキャッシュされる
- リアクティブな依存関係が更新されたときにだけ再評価されるので、逆に言えばリアクティブな依存がない場合は二度と更新されない
- ゲッター(getter関数)とセッター(setter関数)の両方が利用できる(デフォルトはゲッターのみ)
ゲッターとセッター
computedでセッターを使用した場合、dataオプションで設定されたプロパティを更新することもできます。
ゲッター
特定のプロパティ値を取得するためのメソッド
セッター
特定のプロパティ値を設定するためのメソッド
ソースコード
firstNameプロパティやlastNameプロパティを変更すると再描画が起こりますが、nowプロパティはリアクティブな依存関係にないため、最初に読み込まれた以降は変化しません。
vue_test01.html<!DOCTYPE> <head> <meta charset="UTF-8"> <title>Vue.js_test01</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <input v-model="firstName" placeholder="firstName"> <input v-model="lastName" placeholder="lastName"> <p>firstName: {{ firstName }}</p> <p>lastName: {{ lastName }}</p> <p>fullName: {{ fullName }}</p> <p>now: {{ now }}</p> </div> <script> var vm = new Vue({ el: '#app', data: { firstName: '', lastName: '' }, computed: { // fullName/now: 算出プロパティ名 fullName: function () { return this.firstName + ' ' + this.lastName }, now: function () { return Date.now() } } }) </script> </body>methodsオプション
- メソッド
- 再描画が起きると常に関数を実行する
ソースコード
firstNameプロパティやlastNameプロパティを変更すると再描画が起こるので、それに合わせてnowプロパティも変化します。
vue_test01.html<!DOCTYPE> <head> <meta charset="UTF-8"> <title>Vue.js_test01</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <input v-model="firstName" placeholder="firstName"> <input v-model="lastName" placeholder="lastName"> <p>firstName: {{ firstName }}</p> <p>lastName: {{ lastName }}</p> <p>fullName: {{ fullName() }}</p> <p>now: {{ now() }}</p> </div> <script> var vm = new Vue({ el: '#app', data: { firstName: '', lastName: '' }, methods: { fullName: function () { return this.firstName + ' ' + this.lastName }, now: function () { return Date.now() } } }) </script> </body>watchオプション
- 監視プロパティ
- 既にセットされているプロパティを監視する
監視するプロパティの名前
と、そのプロパティに変化(トリガー)があった場合に実行する関数(ハンドラ)
を対にして指定する- 関数は、更新後・更新前のプロパティの値を引数に取ることができる
- 処理は実行するが、データは返さない
- computedでは行えないコストの高い処理を実行できる
以下、公式サイトより。
この場合では、watch オプションを利用することで、非同期処理( API のアクセス)の実行や、処理をどのくらいの頻度で実行するかを制御したり、最終的な answer が取得できるまでは中間の状態にしておく、といったことが可能になっています。これらはいずれも算出プロパティでは実現できません。
ソースコード
fullNameプロパティの表示はwatchオプションで描画することもできますが、逆に冗長なコードになってしまいます。
vue_test01.html<!DOCTYPE> <head> <meta charset="UTF-8"> <title>Vue.js_test01</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <input v-model="firstName" placeholder="firstName"> <input v-model="lastName" placeholder="lastName"> <p>firstName: {{ firstName }}</p> <p>lastName: {{ lastName }}</p> <p>fullName: {{ fullName }}</p> <p>now: {{ now }}</p> </div> <script> var vm = new Vue({ el: '#app', data: { firstName: '', lastName: '', // プロパティはあらかじめセットしておく fullName: '', now: '', }, watch: { // firstName/lastName/now: 監視対象のプロパティ名 // newValue: 更新後のプロパティの値 // oldValue: 更新前のプロパティの値 firstName: function (newValue, oldValue) { console.log(newValue, oldValue); this.fullName = newValue + ' ' + this.lastName }, lastName: function (newValue, oldValue) { console.log(newValue, oldValue); this.fullName = this.firstName + ' ' + newValue }, // 監視しているが、値に変化がないので変更されることはない now: function () { return Date.now() } } }) </script> </body>参考サイト
- 投稿日:2019-09-15T14:23:18+09:00
webpack事始め
webpackとは
webpackとは各種ファイルを指定のファイルの中に取り込んで、関連の機能を一つのファイルにバンドルする(=統合する)機能を提供しています。
ファイルのフォーマットはJS以外にもStyleSheetや画像データ、jsonもバンドルできます。
用語の整理
モジュール
機能ごとに分割されたファイル。
バンドル(バンドルファイル)
統合されたファイルの事。
・バンドルを生成する = まとめられたファイルを生成する。
・モジュールをバンドルする = モジュールを統合する。webpackを導入する事のメリット
ブラウザ上でデータを取得するにはhtmlを取得した後に、再度、JSや画像をサーバーに要求しなければいけません。(=取得するデータ数が多いほど、画面の描画に時間がかかる事になります)
その為、個々のファイル群を1つのJSファイルにバンドルすると、ファイルのリクエスト数を減らす事が出来る=ページの読み込み速度の問題も改善し、UXの向上にも良い影響をもたらす事ができます。
webpackはモジュールをかき集めて、
一つのJSファイル
として、出力する事ができます。
モジュールの価値は他のファイルに必要なモジュール機能を取り入れて、利用できる点にあります。導入してみる
まず、webpackを管理するための準備として、
package.json
を生成する。
(package.jsonはインストールしたパッケージを管理するjson形式で記録されたファイル)$npm init -y次にwebpackをインストールする
$npm info webpack //最新バージョンを確認してみる $npm install --save-dev webpack@4.40.2 //現時点での最新バージョンをインストールさらにwebpack-cliをインストールする
$npm info webpack-cli //最新バージョンを確認してみる $npm install --save-dev webpack-cli@3.3.8 //現時点での最新バージョンをインストール
package.json
を確認すると、上記インストールされた事が確認できる。package.json{ "name": "webpackStudy", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", "url": "git+https://github.com/10mi8o/webpack.git" }, "keywords": [], "author": "", "license": "ISC", "bugs": { "url": "https://github.com/10mi8o/webpack/issues" }, "homepage": "https://github.com/10mi8o/webpack#readme", "devDependencies": { "webpack": "^4.40.2", "webpack-cli": "^3.3.8" } }簡単なファイル構成でバンドルを試してみる
まず、バンドルしたファイルの出力先になるdistディレクトリを作成。
$mkdir distバンドルするjsファイルを作成
$mkdir src $touch src/index.js $touch src/utilities.js検証用のhtmlファイルを作成。
この時点では、main.js
は生成されていません$touch dist/index.htmlindex.html<!doctype html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> WebPackを勉強しましょう!! <script src="main.js"></script> //バンドルされたjsファイルを読み込む </body> </html>ブラウザ上で変更をリアルタイムに確認したい場合に便利な
live-server
をインストールしておきます。$npm install --save-dev live-server //インストール $npx live-server //サーバー起動 npxコマンドでは、ローカルにインストールしたnpmのバイナリを相対パスを指定する事なく使えるようになります次にモジュールを作成しましょう。先ほど作成した
utilities.js
には与えた引数の2乗を返させましょう。utilities.js// ある数字を引数として与えるとその数字の2乗の数を返す function double(num) { return num**2; }モジュールの価値は他のファイルにそのモジュールの機能を取り入れて利用できる事にあります。
他のファイルで利用できるようにする為には、exportを実装します。
関数だけでなく、定数も定義できます。utilities.js// ある数字を引数として与えるとその数字の2乗の数を返す export function double(num) { return num**2; } //定数もexportできる export const NAME = '10mi8o';続いて、src/index.jsで利用できるようにします。
(importする関数名ピンポイントで指定したい場合は{}で囲って指定)
(jsの場合は.jsは省略可能)index.jsimport { NAME, double } from './utilities'; console.log(double(2)); console.log(NAME);
as
を利用して名前をつけたい場合は以下のように書きます。index.jsimport * as utilities from './utilities'; console.log(utilities.double(2)); console.log(utilities.NAME);
as
は名前の衝突を防ぐ際にも使えます。index.jsimport { NAME as NAME_OF_10mi8o } from './utilities'; console.log(NAME_OF_10mi8o);webpackに関する設定を定義できるファイルを作成します。
$touch webpack.config.jswebpack.config.js// 出力は絶対pathで指定しなければいけない為、node.jsのpathモジュールを使用する const path = require('path'); const outputPath = path.resolve(__dirname, 'dist'); module.exports = { // バンドルするファイルを指定 entry: './src/index.js', output: { // バンドルしてmain.jsとして出力 filename: 'main.js', path: outputPath } }最後にmain.jsを生成します。
$npx webpack
以上ざっくりですが、簡単にwebpackに入門しました。
- 投稿日:2019-09-15T13:59:38+09:00
vue/dist/vue.esm.js って何~【とりあえず動くからいいや】からの卒業~
- イチロー 功労セレモニーでの英語スピーチ - YouTube をBGMにモチベーション爆上がり中のsukezaneです。
本記事執筆の経緯
- こちらの素晴らしいチュートリアル記事の中で筆者さんがわからないって言っていたモノに対して調べようと思ったことがきっかけ
- Vue.jsとRailsでTODOアプリのチュートリアルみたいなものを作ってみた
- ※本記事のタイトルは決して上記の筆者さんを否定するものではないですし、むしろ自分で調べなかったら絶対気にすることなかったので感謝です!!
何についての記事か
vueを読み込む際に使っているコレ
import Vue from 'vue/dist/vue.esm.js'
対象読者
- vueの入門者(わたし)
どうやって使っているか
app/javascript/packs/todo.jsimport Vue from 'vue/dist/vue.esm.js' import Header from './components/header.vue' var app = new Vue({ el: '#app', components: { 'navbar': Header, } });こんな感じで意味もわからずなんとなく使っている
でも時々
import Vue from 'vue'
って書いてるやん?何が違うか
import Vue from 'vue/dist/vue.esm.js'
: 完全ビルド(vue.esm.js)import Vue from 'vue'
: ランタイム限定ビルド(vue.runtime.esm.js)って呼ぶらしい。
何が違うかPart2
公式より抜粋
ランタイム + コンパイラとランタイム限定の違い
もしクライアントでテンプレートをコンパイルする必要がある (例えば、 template オプションに文字列を渡す、もしくは DOM 内の HTML をテンプレートとして利用し要素にマウントする) 場合は、コンパイラすなわち完全ビルドが必要です。
hoge.js// これはコンパイラが必要です new Vue({ template: '<div>{{ hi }}</div>' }) // これはコンパイラは必要ありません new Vue({ render (h) { return h('div', this.hi) } })どうすればいいのか1
html側にテンプレートの参照を書かずに単一ファイルコンポーネント側にまとめ、html側ではマウントするDOMの情報だけを書くようにする
例)クリックした時に何かするやつ(修正前)
click.html<div id=#app> <button v-on:click="hoge">Click me!</button> </div>click.jsimport Vue from 'vue/dist/vue.esm.js' const app = new Vue({ el: "#app", methods: { myClick() { alert("click") } } })
- 例)クリックした時に何かするやつ(修正後)
click.html<div id=#app></div>click.jsimport App from '../app.vue' document.addEventListener('DOMContentLoaded', () => { const app = new App app.$mount('#app') })click.vue<template> <button v-on:click="hoge">Click me!</button> </template> <script> import Vue from 'vue' export default Vue.extend({ methods: { myClick() { alert("click") } } }) </script>疎結合になってメンテもしやすそう!!
どうすればいいのか2
- 一応エイリアスを作成すれば完全ビルドの書き方のままでも動くんだけど
ランタイム限定ビルドは完全ビルドに比べおよそ 30% 軽量
と公式にあるため基本は1がいいと思うが
30%くらい別にいいわ
っていうときはエイリアスを作成すればimport Vue from 'vue'
としてimportできるようになります。(実態は完全ビルドなんですけどね
)エイリアスを作成する(パフォーマンス30%減)
webpack
の場合webpack.config.jsmodule.exports = { // ... resolve: { alias: { 'vue$': 'vue/dist/vue.esm.js' // 'vue/dist/vue.common.js' webpack 1 用 } } }2年目も後半になったので
とりあえず動くからいいや
は卒業しようと思います。なんかあったらコメントくださいませ
以上。
- 投稿日:2019-09-15T13:37:25+09:00
jQueryのcssをJavaScriptで書き換えたらこうなる一例
自分自身の基礎力向上のために、jQueryで作ったコードがJavaScriptで書いたらどのようになるか書いてみています。もし、他にも方法がありましたら、ご教示いただけると嬉しいです。
結果
jQueryのhover
jQuery
$('button').hover(function () { //マウスカーソルが重なった時の処理 $('button').css('background-color', '#f00'); }, function () { //マウスカーソルが離れた時の処理 $('button').css('background-color', 'yellowgreen'); })JavaScriptのみ
const target = document.getElementById('target'); target.addEventListener('mouseenter', () => { target.classList.add('background-color', '#f00'); }, false); target.addEventListener('mouseleave', () => { target.classList.remove('background-color', 'yellowgreen'); }, false);classListは読み取り専用ですが、addやremoveでオブジェクトを変更することが可能
Element.classList
- 投稿日:2019-09-15T12:04:01+09:00
Node.jsとは?
目的
- Slackでアプリを作成したときにNode.jsというものに触れたが不明なことが多かったため調べたことをまとめる。
特徴
- サーバで動くJavaScript
- 小さい計算が得意で速い
- マイクロソフトやYahooが推奨している
- リアルタイムWebの分野に強い
- 小規模で機動性の高いWebアプリで、リアルタイム性が求められるアプリに最適
ちょっと深堀した特徴
非同期処理で動く(動作が高速な理由)
データベースからデータを取得するプログラムで同期処理の場合はデータベースからの反応を待つが、非同期処理だとデータベースからの反応を待っている間に別の処理を実行することができる。前述の方法をとっているため大量のアクセスを高速に処理することが可能。シングルスレットでメモリの消費効率化(小さい計算が得意な理由)
本来サーバに複数のアクセスがあった時にそれぞれにメモリを割り当てて実行する(マルチスレッド)が、1万人規模のアクセスがあるとメモリが上限にあたり効率が悪くなる。Node.jsでは一つのメモリでひとつずつアクセスを処理する方法をとることでメモリ効率を向上させている。
一見一つのメモリで一つの処理しかできないため処理が遅そうに感じるが前述した非同期処理のおかげで、メモリでの処理を待たずして処理を実行できるため、少ないメモリ消費量を抑えて大量のアクセスを高速に処理できる。
- 投稿日:2019-09-15T11:50:26+09:00
PureJSでExcelライクなセル編集UIを実装
※本記事の実装コードはこちら:https://jsfiddle.net/jnado3g5/1/
はじめに
個人的な興味がきっかけですが、Excelライクなセル編集UIをJavaScriptで作成する際の課題として、「キー押下と同時にセル編集を開始する」という仕様をどう実現するか、少々難しい問題があります。
厄介なのが日本語入力で、実際、(おそらく)有名な商用ライブラリであるWijimo FlexGridでも、日本語入力を含むキー押下とセル編集開始は同時に行えないみたいです。
しかし、Googleスプレッドシートではそれが実現されているため、技術的に不可能ではなさそうです。そこで、「キー押下と同時にセル編集を開始する方法」を調べて、実際に検証を行った結果を記します。
仕様
メインは「キー押下と同時にセル編集を行う方法」ですが、せっかくなので編集に関わるUIを一通り検証実装してみました。
セル選択イメージ:
なお、編集機能に焦点を当てているため、それ以外(行列追加削除等)は一切考えていません。
入力イメージ:
- セル対してmousedownするとセルが選択されます
- セル選択された状態で次の動作をします
- F2キーで既存の値の編集開始します
- 通常の入力(Backspace、数値、アルファベット、日本語等)で新規入力開始します
- セルをダブルクリックすると既存の値の編集開始します
- Delキーで既存の値をクリアします(編集開始しません)
- セルからフォーカスが外れる(セル部分以外をmousedownする)と、上記のようなセル編集開始が行われなくなります(編集開始には再度フォーカスが必要)
- セル編集中、次の動作をします
- Enterキーを押されると入力終了して、入力された値をセルに反映します
- Escapeキーを押されると入力終了して、入力された値を破棄します
- フォーカスが外れると入力終了して、入力された値をセルに反映します
動作確認環境
次の環境で動作確認を行いました。なおOSはWindows10です。
- Chrome
- FireFox
- Edge
- IE11
- Opera
キー押下と同時にセル編集を開始する方法
わかりやすく、実際は隠れている(y座標位置が-10000px)input要素を目に見えるようにすると、セル選択時は次のような状態になっています。
input要素にフォーカスが当たっているので、要素に対するinputイベントが拾えます。
初回inputイベント発生時、input要素をセルの位置に移動させれば、あたかも「キー押下と同時にセル編集を開始する」ようなUIが出来上がります。
ただし、Backspaceキーではinputイベントが拾えないので、別途keydownイベントを拾って制御します。
その他工夫
上記の方法で実装を行う場合、input要素に対して「編集前の待機状態 → 編集中 → 編集完了」と状態が遷移し、各状態で必要なイベントが異なります。
そのため、「次の状態に移ったら前の状態で登録していたイベントは破棄する」といった実装を組み込んでみました。
結果、ソースコードの見通しが良くなったかなという個人的な感想を持っています。おわりに
目指せ!個人開発でWijimo FlexGrid越え!(厳しい)
- 投稿日:2019-09-15T08:24:40+09:00
Reactでreact-router-domを使おうとするとページのロードが終わらない問題を解決する
※この記事は、プログラミング歴五ヶ月程度の初心者によって書かれています。ベテランの方は大目に見てもらえると助かります。同じように初心者の人は、私の言っていることを鵜呑みにしすぎず「?」と感じた点はすぐに検索するようにしてください。
今回抜け出せなくなったこと
バックエンドはRails、フロントエンドはReactにTypeScriptを使って簡単なアプリを作ろうとしていました。
その時に、フロントエンド側でルーティング設定をしようと思い
$ npm install --save @types/react-router-domをターミナルで実行しました(セーブフラッグは無くても大丈夫だと思います。@typesというのはTypescript向けに型が付いたものなので、普通のJSでReact開発をしている人は必要ありません)。
そして、普通にコードで必要なものをインポートして使っていたのですが
Module not found: Can't resolve 'react-router-dom' in
というエラーに出くわします。
エラー文をそのまま検索したところ、TypeScript用だけじゃ無くて普通のreact-router-domもインストールした方が良いとのことだったので下のコマンドを実行しました。
$ npm install --save react-router-domそして、普通にコードを書き進め、npm startをしてlocalhostにアクセスすると画面のロードが終わらなくなりました。真っ白い画面で、コンソールにエラー表示もないまま、ずっとこのままです。
この問題を解決するのに、10時間以上のリサーチが必要になりました。
環境
内容的にバージョンは関係ないので、JSONファイルを貼っておきます。
{ "name": "react_front_end", "version": "0.1.0", "private": true, "dependencies": { "@types/jest": "24.0.18", "@types/node": "12.7.5", "@types/react-router-dom": "^4.3.1", "axios": "^0.19.0", "react": "^16.9.0", "react-dom": "^16.9.0", "react-router-dom": "^4.3.1", "react-scripts": "3.1.1", "typescript": "^3.5.3" }, "scripts": { "start": "PORT=8000 react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" }, "eslintConfig": { "extends": "react-app" }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] }, "devDependencies": { "@types/react": "^16.9.2", "@types/react-dom": "^16.9.0" } }解決策
原因は、Routeを使って、Routeを使っているコンポーネント自身を無限ループさせていたことでした。言葉だとうまく説明できないので、コードを表示します。
これが、ブラウザで表示されなかった時のコードです(長いので簡易化しています)。
import * as React from 'react'; import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; class App extends React.Component<{}, {}> { render() { return( <div> <Router> <Switch> <Route path="/" component={App} /> <Route path="/count" render={() => <Count name="fantastic!" />} /> <Route path="/history" component={History} /> </Switch> </Router> <h1>サインイン</h1> <form onSubmit={e=>this.handleSubmit(e)} > <input onChange={e=>this.handleEmailChange(e)} /> <input onChange={e=>this.handlePassChange(e)} /> <button type="submit">ログイン</button> </form> </div> ) }; }このコードで問題となっていたのは、Appコンポーネントの中でAppコンポーネント用のルートを設定している下のコードでした。
<Route path="/" component={App} />解決策は単純でこの一行を削除してあげるだけです。
仕組みとしては最初にAppコンポーネントを読み込むのに、その中でそれ自身であるAppコンポーネントが読み込まれていて、それを読み込もうとするとまたAppコンポーネントが出てきて読み込んで…エラーになるということだと思います。
そもそも、元からAppコンポーネントは表示されているのでわざわざ、そこから設定しなくても大丈夫ですね。
解決までの道のり
久しぶりに思いっきりハマってしまい、しかも一切エラーメッセージのないエラーだったので原因を特定するのにかなり時間がかかってしまいました。
最初は、アプリを閉じたり色々アップデートしてみたり、再起動したり、タブ閉じたりキャッシュ削除したりと色々していました。が、もちろんダメでした。
最終的に出てきた「page unresponsive」とReactを組み合わせて検索すると、ようやく解決策にたどり着けました、
エラーにハマった時は、とにかく違うアプローチを色々試してみること、そしてChromeのエラーであったとしても一緒に検索してみることが大事だなと思いました。
- 投稿日:2019-09-15T08:22:15+09:00
window.mediaMatchで3デバイス判定
- mediaMatchで3デバイス判定と切替時のイベントをまとめる
- メディアクエリはサイトによって書き換え
サンプルコード
// メディアチェック let medias = { winobj:[], query:[ 'screen and (max-width: 559px)', //SP 'screen and (max-width: 959px) and (min-width: 560px)', //TABLET 'screen and (min-width: 960px)', //PC ], removeSpace: function(s) { // スペースを除去するやつ return s.replace(/\s/g,''); } }; // 切り替えイベント function checkBreak(query){ if(medias.removeSpace(query.media) === medias.removeSpace(medias.query[0]) && query.matches === true){ // spの処理 console.log( 'sp' ); } if(medias.removeSpace(query.media) === medias.removeSpace(medias.query[1]) && query.matches === true){ // tabの処理 console.log( 'tab' ); } if(medias.removeSpace(query.media) === medias.removeSpace(medias.query[2]) && query.matches === true){ // pcの処理 console.log( 'pc' ); } } // ロード時実行 document.addEventListener('DOMContentLoaded',function(){ for(let i=0;i<medias.query.length;i++){ medias.winobj[i] = window.matchMedia(medias.query[i]); medias.winobj[i].addListener(checkBreak); } });ちょっと課題点
スペースを含む以下のif部分。
もっと簡易なmatchを生成してよりメディアクエリ部分に特化したifのがブラウザの変更とかにはつよそーだなーと思ったり。medias.removeSpace(query.media) === medias.removeSpace(medias.query[0]) && ~ブラウザ確認
Chrome 76.0
Firefox 69.0
Safari 12.1偉大なる参考
window.matchMedia をそろそろ活用してもいい頃
【続々】window.matchMedia を用いたブレイクポイントイベント
Matching multiple CSS media queries using window.matchMedia()
- 投稿日:2019-09-15T03:48:10+09:00
JavaScript の Array を特定の数に分割する関数(chunk)を生やしてみる
- 投稿日:2019-09-15T03:42:23+09:00
JavaScript の Array に zip 関数を生やしてみる
TL;DR
Array.prototype.zip = function(...args) { const new_array = []; for(let i = 0; i < this.length; i ++) { new_array.push([this[i], ...args.map(arg => arg[i])]); } return new_array; }詳細
やっていることは単純で同じインデックスの値同士の組み合わせを作って新しい配列に push しているだけですね。もともとは Ruby の Array#zip のようなことを JavaScript でもできないかなと思って探していたんですが、どうやらネイティブでは実装されていないようなので今回作ってみた次第です。
実際に使ってみるとこんな感じになります。
const array0 = [0, 1, 2]; const array1 = [3, 4, 5]; const array2 = [6, 7, 8]; array0.zip(array1, array2) // => [[ 0, 3, 6 ], [ 1, 4, 7 ], [ 2, 5, 8 ]]参考文献
- 投稿日:2019-09-15T01:47:16+09:00
イケてるマウスカーソルをPure JavaScriptで実装する
結果
See the Pen Cool UI - Mouse Chaser 01 by Loki (@loki__codepen) on CodePen.
追従するマウスカーソルについて
ここ最近、マウスカーソルに装飾をするサイトが増えてきている気がします。
自作するのは面倒なので、多くの制作者は以下のサイトのコードをコピーすることも多いでしょう。イケてるマウスカーソルを簡単に実装する | 株式会社 エヴォワークス -EVOWORX-
https://www.evoworx.co.jp/blog/mouse-stoker-gsap/jQueryを利用して書かれています。
Webサイトの制作であればjQueryを利用しない機会はあまりないと思いますが、記述をPure JavaScriptに調整したものを作成しましたので、共有いたします。ソースコード
const cursor = document.getElementById('js-cursor'), chaser = document.getElementById('js-chaser'), target = document.querySelector('a'); let delay = 10, cursorPosX = 0, cursorPosY = 0, chaserPosX = 0, chaserPosY = 0; // カーソルの遅延アニメーション TweenMax.to({}, .001, { repeat: -1, onRepeat: function() { chaserPosX += (cursorPosX - chaserPosX) / delay; chaserPosY += (cursorPosY - chaserPosY) / delay; TweenMax.set(cursor, { css: { left: cursorPosX - (cursor.clientWidth / 2), top: cursorPosY - (cursor.clientWidth / 2) } }); TweenMax.set(chaser, { css: { left: chaserPosX - (chaser.clientWidth / 2), top: chaserPosY - (chaser.clientWidth / 2) } }); } }); // マウス座標を取得 document.onmousemove = function(event) { cursorPosX = event.pageX; cursorPosY = event.pageY; }; // マウスオーバー時の処理 target.onmouseover = function() { cursor.classList.add('is-active'); chaser.classList.add('is-active'); }; // マウスアウト時の処理 target.onmouseout = function() { cursor.classList.remove('is-active'); chaser.classList.remove('is-active'); };その他に調整した点
以下の点を修正しております。
- セレクタ名を.cursor
から#js-cursor
に変更
- セレクタ名を.follower
から#js-chaser
に変更
-cWidth
やfWidth
など、JavaScript側でもサイズを入力する必要があった値を、CSSのみで認識するように変更さいごに
マウスカーソルの視認性が良くなるので、個人的には好きな実装です!
十数年前の流行りが回帰していますが、アニメーション系のライブラリと合わさって現代バージョンとして流行っていることがなんだか面白いですね。
やりすぎには注意(笑)
Twitterのフォロワーを募集しております!
すでにWeb業界にいらっしゃる方や、業界未経験の方、どんな方でも募集しておりますので、どうぞよろしくお願いいたします(*^^*)Twitter - https://twitter.com/loki__tweet
- 投稿日:2019-09-15T01:20:20+09:00
便利ページ:Javascriptでカラーピッカー
便利ページ:Javascriptでちょっとした便利な機能を作ってみた のシリーズものです。
今回は、色の選択とRGB値の表示です。
HTMLにカラーピッカーがあるので、それを使います。また、プリセットカラーで選択できるようにします。こんな感じのページです。
毎度の通り、デモページとGitHubです。
GitHub
https://github.com/poruruba/utilitiesデモページ
https://poruruba.github.io/utilities/(2019/9/15 追記)
一番近い色の名前を検索できるようにしました。色差を計算するのに以下を使わせていただきました。
https://gka.github.io/chroma.js/
一口に色と言っても奥が深いですね。カラーピッカーを表示する
HTMLは以下の通りです。
index.html<div class="form-inline"> <input type="color" v-on:change="color_change" v-model="color_value"> <label>RGB:</label> <input type="text" size="7" class="form-control" v-model="color_value"> </div> <br> <table class="table table-borderless"> <td v-bind:bgcolor="color_value"> </td> <td><label>R:</label><input type="range" min="0" max="255" v-model.number="color_r" v-on:input="color_range"><input type="number" class="form-control" v-model.number="color_r" v-on:change="color_range"></td> <td><label>G:</label><input type="range" min="0" max="255" v-model.number="color_g" v-on:input="color_range"><input type="number" class="form-control" v-model.number="color_g" v-on:change="color_range"></td> <td><label>B:</label><input type="range" min="0" max="255" v-model.number="color_b" v-on:input="color_range"><input type="number" class="form-control" v-model.number="color_b" v-on:change="color_range"></td> </table>カラーピッカーは、type="color"のinputです。
あとは、color_valueに格納されたRGB値をテーブルのセルの背景色として表示しています。
type="range"のinputのゲージでもRGBの各値を変更できるようにしました。
ここらへんは、Vueのデータバインディング機能が大活躍です。基本16色などのテーブルがありますが、プリセットとしてcolor.jsに配列として格納しておいたものを、テーブル表示しているだけです。
単純ですね。。。
以上
- 投稿日:2019-09-15T00:36:46+09:00
フリーランスになるために
初めまして
まず「フリーランスになりたい」なんて記事をQiitaで書いていいの?なんて気もしますが、
自分が学んだ技術を書き残す場所が欲しいなってことでQiitaブログを初めてみます。何をゴールとするか
小目標
私は現在本業で8時間フルタイム働いているので、副業として動くことを前提として、
2019年のうちにWebコーダーとして月10万売り上げることを目標とします。中目標
小目標が達成できたら、本業をやめる、つまり退職してフリーランス一本にします。
数字としては、月30万売り上げたい。大目標
今の年収と同額をフリーランスで稼ぐこと。
どのタイミングで知り合いに公開するか不明なので、現段階では具体的な金額は伏せます。
2020年までにできるといいなあ・・・現状のスキル
つい最近までECサイト構築の会社にいてエンジニアとして働いていたので、
HTML、CSS、JavaScriptの知識は多少あります。
あとは、.Net(VB、C#)がかけるのと、JavaとかRubyは調べながらなら読めるくらい。これから
実際に1から10まで作れるかというと微妙なので、実務に必要な流れを一通り学びたい。
そして流行りや効率的な書き方を全然知らないので、学びたい。具体的に
- サイトの模写コーディング
- ポートフォリオ作成
がんばるぞい!
- 投稿日:2019-09-15T00:14:50+09:00
JavaScriptで綺麗にリンク一覧を取得する
スクレイピングするまでもないけど、サイトのリンク一覧をさっと取得したいシーンがあったのでスクリプトを書きました。
実用的なスクレイピングやもっと高度なことを求めている方はChrome拡張のScraper等の利用を検討して、どうぞ。仕様
「綺麗に」の内訳。
- リンクのテキストとURLを並べて表示、クリップボードにコピー
- URLの重複は除去する
- テキストの無駄な改行は除去する
- 特定のドメインに絞れるようにする
- For文は使わない
コード
copy()コマンドを利用しているので、Chrome前提です。
ブラウザのConsoleに貼って実行してください。// 検索ワードは適宜変更してください。 const targetLinkWords = ['www.bbc.com']; const createLinkList = (el) => { let existsList = []; let res = ''; Array.prototype.filter.call(el, (node) => { // hrefの値重複とtargetLinkWordsに登録されたワードを含まない場合、除外 if (existsList.indexOf(node.href) === -1 && targetLinkWords.find((val) => {return node.href.indexOf(val) !== -1;})) { existsList.push(node.href); res = `${res}\r\n` + (node.text.trim() === '' ? 'テキストなし':node.text.replace(/\r?\n/g, '')) + `||${node.href}`; } }); return res; }; const result = createLinkList(document.querySelectorAll('a')); console.log(result); copy(result);結果
試しにBBC NEWS Techのページで実行してみました。
Homepage||https://www.bbc.com/ Skip to content||https://www.bbc.com/news/technology#skip-to-content Accessibility Help||https://www.bbc.com/accessibility/ Sign in||https://session.bbc.com/session?ptrt=https%3A%2F%2Fwww.bbc.com%2Fnews%2Ftechnology&context=news_gnl&userOrigin=news_gnl Notifications||https://www.bbc.com/news/technology# News||https://www.bbc.com/news Sport||https://www.bbc.com/sport Reel||https://www.bbc.com/reel Worklife||https://www.bbc.com/worklife Travel||https://www.bbc.com/travel Future||https://www.bbc.com/future Culture||https://www.bbc.com/culture Music||https://www.bbc.com/culture/music Weather||https://www.bbc.com/weather More||https://www.bbc.com/news/technology#orb-footer Video||https://www.bbc.com/news/video_and_audio/headlines World||https://www.bbc.com/news/world Asia||https://www.bbc.com/news/world/asia UK||https://www.bbc.com/news/uk Business||https://www.bbc.com/news/business TechTech selected||https://www.bbc.com/news/technology Science||https://www.bbc.com/news/science_and_environment Stories||https://www.bbc.com/news/stories Entertainment & Arts||https://www.bbc.com/news/entertainment_and_arts Health||https://www.bbc.com/news/health World News TV||https://www.bbc.com/news/world_radio_and_tv In Pictures||https://www.bbc.com/news/in_pictures Reality Check||https://www.bbc.com/news/reality_check Newsbeat||https://www.bbc.com/news/newsbeat Special Reports||https://www.bbc.com/news/special_reports Explainers||https://www.bbc.com/news/explainers Long Reads||https://www.bbc.com/news/the_reporters Have Your Say||https://www.bbc.com/news/have_your_say Africa||https://www.bbc.com/news/world/africa Australia||https://www.bbc.com/news/world/australia Europe||https://www.bbc.com/news/world/europe Latin America||https://www.bbc.com/news/world/latin_america ・・・ちゃんとコピーされました。
- 投稿日:2019-09-15T00:13:29+09:00
JavaScript の Array で undefined などの値を除外する方法
TL;DR
const array = [0, 1, 2, "", undefined, null, NaN, "hoge"] array.filter(Boolean) // => [1, 2, "hoge"]詳細
Array.prototype.filter
を使用しています。引数に入るコールバックの処理に対して true を返す値のみに絞る関数です。例えば、以下のように偶数である値のみを抽出したいときなんかにも使えます。
[...Array(10).keys()].filter(n => n % 2 == 0) // => [0, 2, 4, 6, 8]上記の例の場合は偶数かどうかの処理をはさみましたが、ここに
Boolean
を入れるとBoolean()
を実行してくれます。関数呼び出しですね。Boolean()
を実行すると値の真偽を判定してくれます。Boolean の仕様によると、以下の値はすべてfalse
と判定されるようです。false と判定される値
[0, -0, null, false, NaN, undefined, ""]If the value is omitted or is 0, -0, null, false, NaN, undefined, or the empty string (""), the object has an initial value of false.
備考
蛇足ですが、
new Boolean()
でオブジェクトを作るときはfalse
と判定された値を持つ Boolean オブジェクトも生成されるようです。しかも if 文とかで判定するときも常に true が返ってきます。不思議。const bool = new Boolean(false) if(bool) console.log("false is true!") // => "false is true!"参考文献
- 投稿日:2019-09-15T00:02:18+09:00
[GAS] GoogleSpreadSheet のGASで日本企業上位1000社の時価総額を取得
Google SpreadSheet はとても便利にデータをまとめることができます
今回、はじめてGASを利用してデータの取得をしたので、ソースコードとともに使い方の解説を載せておきますスプレッドシートの準備
GASの利用にあたり、スプレッドシートを用意します
ツールを押し、スクリプトエディタを起動します
起動させたら、このような画面になっているはずです
ここからGASを利用してデータの取得をしていきます
GASの利用
今回用意したスプレッドシートとGASのプログラムを先に載せておきます
簡単な作業の流れの説明の後に、ソースコードの説明をしていきますスプレッドシートの中身
順位、業界、コード、市場、名称、時価総額
の順に並んでいます
時価総額以外のところはすでに用意していました
今回やりたいこと
毎日時価総額を自動で更新してくれるスプレッドシートの作成
早速コードの解説と作成の仕方へ
基本的な解説はコメントの中に書いているので、GASでスクレイピングをする際に使用したライブラリの使い方を説明していきます
今回使用したのはスクレイピングを簡単にするライブラリのParserというものです
※詳しい説明はこちらをご覧ください
https://www.kotanin0.work/entry/2019/01/06/200000まずは リソース > ライブラリ の順に開きます
開いたら、下に書いてあるキーを入力します
M1lugvAXKKtUxn_vdAG9JZleS6DrsjUUV
キーの入力をし、ライブラリを追加したら、バージョンを選んで完了です
前準備が完了したので、コードの解説に入ります
基本的にコード内にコメントとして解説を書いていきます
スクレイピングをするにあたり重要なのは、どこのwebサイトから情報を引っ張ってくるか、です
今回はみんかぶさんから情報を引っ張ってきています
理由として、Yahoo!ファイナンスはスクレイピングを禁止しているため、こちらのサイトを選びましたスクレイピングをやりすぎたら罪に問われることもあるので注意!!
では、コードと一緒に解説をしていきます
//今回は二つの関数を使用しています //まずはJikaを実行します function Jika() { // 取得した株価を出力するBookを定義 const book = SpreadsheetApp.getActiveSpreadsheet() // 出力先のBookのシートを定義 const sheet = book.getSheetByName("シート1"); //シート名を間違えるとエラーが出るので注意 //証券コードの読み込み var bango = sheet.getRange('c2:c1001').getValues(); //今回はc列に証券コードを集めているので、証券コードが書かれている1行目から範囲指定しています var array = []; //1000社分のループを回す for(var i = 0; i < 1000; i++) { //下のurlと繋げるため、文字列にする var num = bango[i].toString(); //stokPrice関数を呼び出す var marketValue = [stockPrice(num)]; //二次元配列として格納していく array.push(marketValue);<img width="1255" alt="スクリーンショット 2019-09-14 23.47.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/362795/1350d33f-d410-a42f-31cc-d96d18fccd24.png"> } //最後にf列に時価総額を反映させる sheet.getRange('f2:f1001').setValues(array); } function stockPrice(code) { //証券コードごとにurlを作る var url = 'https://minkabu.jp/stock/' + code //ページ全てを取得する var response = UrlFetchApp.fetch(url); var html = response.getContentText('UTF-8'); //fromとtoにタグを書き、その間にあるデータを取得する //今回はテーブル内のデータだったのでfromとtoの中身に日本語が入っていますが、これで大丈夫です var data = Parser.data(html).from('時価総額</th><td class="tar wsnw">').to('百万円</td>').build(); return data; }完成したシートがこちらになります
これで毎日更新される!!!
(...手作業ならね)
これを自動化したい!!!
どうにか簡単にいかないものか!!そんな悩める子羊たちもGASは救ってくれます
自動化の流れ
GASエディタに戻り、
左上のタイマーボタンを押します
すると、このような画面が出てくるはずです
トリガーを作成しましょう!!
トリガーの作成を押して出てきた画面で次のように選択します
これでようやく、
「Jikaを毎日、朝の3~4時に自動で実行して時価総額を引っ張ってくる」
ようになりました!!!!ぱちぱちぱち
最後に
こちらのサイトには大変お世話になりました
1日でGASを使えるようになったのもこのサイトのおかげです
ありがとうございます!
https://tonari-it.com/gas-script-editor/注意
ソースコードは、1000社分の証券コードを抜いて実行するぞ!!
という形になっていますが、GASさんは6分たつと自動でプログラムを終了してしまいます僕のコードだと、100社で1分ほどかかります
なので、僕はスクリプトエディタを二つ作り、1~500と501~1000に分けて使っていますもし、1000社分一気にいっても余裕!!という案があればぜひ教えていただきたいです