- 投稿日:2020-01-12T23:25:26+09:00
TypeScriptを使って嬉しかったこと
新人「先輩、TypeScriptのコーディングできました!」
先輩社員「どれどれ」
先輩社員「いやそこら中コンパイルエラーだらけ...なのは型システムが働いてる証拠だ!」
先輩社員「そうだろ?」先輩社員「型は...ガードレールだ」
先輩社員「進むべきじゃない場所へ進もうとしたら、ちゃんとブロックしてくれる...」
(ぺこぱ風)ってことで、静的型付言語って良いですよね。
今回はTypeScriptを使ってみて嬉しかったことを書いてみます。登場人物
ワイ・・・ワイ(36歳)
社長・・・社長
ハスケル子・・・インターンの中学2年生今日から新しいプロジェクト開始
社長「おーい、やめ太郎、ハスケル子ちゃん」
社長「新しいお仕事を獲得してきたで」
社長「技術記事投稿サイトを作る案件や」ワイ「おお〜、さすが社長はんや」
社長「おおきにやで」
社長「ほんで、そのお仕事の話なんやけど」
社長「クライアントはんから一つ要望があんねん」
社長「Reactを使って作ってくれ、って言われてんねや」
社長「やめ太郎、ハスケル子ちゃん、Reactはイケるか?」ワイ「やった事なくはないですわ」
ハスケル子「私も大丈夫です」TypeScriptはどうする?
社長「TypeScriptはどうしよか?」
社長「そこはクライアントはんからは何も言われてへんねやけど」ワイ「TSはよう分からんから、今回はやめときまひょ」
ワイ「型とかよう読まれへんし」ハスケル子「私は絶対TypeScript有りでやりたいです」
ハスケル子「型がないとコード読むの面倒なので...」型がないと面倒、とは
ワイ「いや、型があるほうが型情報を読まなあかんから面倒やん」
ハスケル子「いえ、型が書いてあるほうがコードを把握しやすいです」
ワイ「ええ...何で...?」
例えば:マイページ
ハスケル子「例えば、技術投稿サイトのマイページみたいなのを作るとしますね」
ハスケル子「↑こんな感じのやつです」
ワイ「ほうほう」
ハスケル子「で、ユーザ情報を表示するための
UserInfo
っていうコンポーネントを作るとします」
ハスケル子「TypeScriptなしで書くとすると、コードは↓こんな感じです」/components/UserInfo.jsxconst UserInfo = ({ user }) => ( <div> <section> <h2>ユーザ情報</h2> <p>アカウント名:{user.account}</p> <p>名前:{user.name}</p> </section> <section> <h2>記事一覧</h2> <ol> { user.articles.map(article => ( <li> <a href={article.url}>{article.title}</a> </li> )) } </ol> </section> </div> )ワイ「なるほどな」
ハスケル子「やめ太郎さんなら、このコンポーネントのコードを見て、」
ハスケル子「どんな風に使うべきコンポーネントなのか読み取れますか?」ワイ「そら簡単やで」
ワイ「余裕で読み取れますがな」
ワイ「まず...」/components/UserInfo.jsxconst UserInfo = ({ user }) => (ワイ「↑こう書いてあるから」
ワイ「このUserInfo
コンポーネントは」
ワイ「親コンポーネントからuser
っていうpropsを受け取ってる...」
ワイ「っちゅうことが分かるわ」ワイ「他にも...」
/components/UserInfo.jsx<h2>ユーザ情報</h2> <p>アカウント名:{user.account}</p> <p>名前:{user.name}</p>ワイ「↑こんなコードがあるから」
ワイ「親から受け取ったuser
っていうpropsは、account
とname
というプロパティを持ったオブジェクトである...」
ワイ「ってこともわかるで」ワイ「あとは...」
<h2>記事一覧</h2> <ol> { user.articles.map(article => ( <li> <a href={article.url}>{article.title}</a> </li> )) } </ol>ワイ「↑こんな部分があるな...」
ワイ「せやから、user
はarticles
っちゅうプロパティも持っとんな」
ワイ「map
メソッドを使っているということは、そのuser.articles
は配列やということが分かるで」
ワイ「ほんで、その配列の中身はオブジェクトやな」
ワイ「url
とtitle
というプロパティを持ったオブジェクトっちゅうことや」ハスケル子「はい、そんな感じですよね」
ハスケル子「まとめると、どんな風に使うべきコンポーネントだと言えますか?」ワイ「ええ...?」
ワイ「まあ、まとめるならば」const user = { account: 'Yametaro', name: 'やめ太郎', articles: [ { title: "記事名1", url: "/article1.html" }, { title: "記事名2", url: "/article2.html" } ] };ワイ「propsとして↑こんなオブジェクトを渡して使うべきやな」
ハスケル子「そうですね」
TypeScriptの場合はどうか
ハスケル子「次に、TypeScriptを使った場合のコードを見てみます」
/components/UserInfo.tsxtype Props = { user: User } type User = { account: string, name: string, articles: Article[] } type Article = { title: string, url: string } const UserInfo: React.FC<Props> = ({ user }) => ( <div> <section> <h2>ユーザ情報</h2> <p>アカウント名:{user.account}</p> <p>名前:{user.name}</p> </section> <section> <h2>記事一覧</h2> <ol> { user.articles.map(article => ( <li> <a href={article.url}>{article.title}</a> </li> )) } </ol> </section> </div> )ハスケル子「↑こんな感じです」
ワイ「ほら、やっぱ型が書いてあるぶん長いやん...」
ハスケル子「そりゃあコード量は少し増えますけど」
ハスケル子「可読性の面でメリットもありますよ」
ハスケル子「例えば...」/components/UserInfo.tsxtype Props = { user: User }ハスケル子「↑ここを見ると」
ハスケル子「このコンポーネントはuser
というpropsを受け取る」
ハスケル子「そしてそのuser
はUser型の値である」
ハスケル子「...ということが分かります」ワイ「なるほどな、受け取るprops一覧がここで分かるわけやな」
ワイ「今回はuser
一個だけってことか」
ワイ「でも、なんやのUser型って」
ワイ「number型とかstring型なら知っとるけど」ハスケル子「それは↓この部分に書いてあります」
/components/UserInfo.tsxtype User = { account: string, name: string, articles: Article[] }ハスケル子「User型の値は、
account
という文字列と」
ハスケル子「name
という文字列...」
ハスケル子「そしてarticles
という配列を持っているよ、ってことがわかります」ワイ「ほうほう、つまり...」
ワイ「User型っていうのはハスケル子ちゃんの自作の型ってこと?」ハスケル子「そうです」
ハスケル子「自分で新しい型を定義して名前を付けることができるんです」ワイ「ほー、なんか...」
ワイ「Userってのはこんなやつですよ、みたいな」
ワイ「世界観的なものが分かりやすいな」ハスケル子「そうですね」
ワイ「ほんで、
User
が持ってるaccount
とname
がstring型なのは分かったけど」
ワイ「Article[]
ってのはどういう型なん?」ハスケル子「
Article[]
っていうのは」
ハスケル子「Article型の値が入ってる配列だよ、って意味です」ワイ「Article型ってのは、また自作の型やな?」
ハスケル子「はい」
/components/UserInfo.tsxtype Article = { title: string, url: string }ハスケル子「↑この部分でArticle型を定義しています」
ワイ「なるほど」
ワイ「Article型の値は、title
とurl
というプロパティを持ってますよ...」
ワイ「ほんで、title
もurl
も文字列ですよ...」
ワイ「っちゅうことやな」ハスケル子「そうです」
型が書いてあると、何が嬉しいか
ハスケル子「TypeScriptなしの場合は」
ハスケル子「コンポーネントの中身を全部読んで...」「なるほど、このコンポーネントには
user
というオブジェクトを渡して使うんだな」
「そのuser
は、account
とname
とarticles
というプロパティを持ってるんだな」
「articles
はmap
メソッドを持っているようだから、配列だな・・・!」ハスケル子「...っていうことがようやく分かったじゃないですか」
ワイ「せやな」
ハスケル子「でもTypeScriptありの場合だと」
/components/UserInfo.tsxtype Props = { user: User } type User = { account: string, name: string, articles: Article[] } type Article = { title: string, url: string }ハスケル子「↑この型定義の部分を見ただけで...」
const user = { account: 'Yametaro', name: 'やめ太郎', articles: [ { title: "記事名1", url: "/article1.html" }, { title: "記事名2", url: "/article2.html" } ] };ハスケル子「↑こんなpropsを受け取るコンポーネントだな!」
ハスケル子「ってことが分かるじゃないですか」
ハスケル子「コンポーネントの実装部分を読まなくても!」ワイ「おお...」
ワイ「やっぱリーダブルやなぁ...」社長「(何がやっぱやねん...)」
ワイ「仕事やと、一つの案件のコーディングを複数人で担当することも多いから」
ワイ「人の作ったコンポーネントを使う機会とかも多いしな」
ワイ「せやから、読み取りやすいのはええよなぁ」ワイ「あと思ったんやけど」
ワイ「型ってなんか、コメントみたいな効果もあるんやね」
ワイ「このプロパティには文字列を入れてくださいな、みたいな」
ワイ「コード内に仕様書が書いてある感じがええな」ハスケル子「そうですよね」
ハスケル子「しかも、ちゃんと守らなきゃ前に進めない...」
ハスケル子「強制力を持ったコメントですよね」
ハスケル子「型定義の通りにpropsを渡さないとエラーが出て、コンパイルが通らない...」
ハスケル子「だから、マウスでぽちぽちページを見て回る前にミスに気付けるんです」ワイ「ああ、普通はコードを実行してみるまで」
ワイ「エラーが出るかどうか分からへんもんなぁ...」
ワイ「それはええかも...」他にも嬉しいことが
ハスケル子「そういえば、やめ太郎さんって」
ハスケル子「name
っていう単語をよくnamae
ってスペルミスするじゃないですか」ワイ「まぁ、比較的するな」
ハスケル子「そういうタイポ対策にもTypeScriptが役立ったりしますよ」
ハスケル子「例えば...」/components/UserInfo.tsx<p>名前:{user.namae}</p>ハスケル子「↑こんな風にタイポしちゃったとしたら...」
Property 'namae' does not exist on type 'User'. Did you mean 'name'?
(プロパティ「namae」はタイプ「User」に存在しません。 「name」という意味ですか?ハスケル子「なんて教えてくれるんです」
ワイ「おお、まじか...」
ワイ「まさにワイのための機能やん...」ハスケル子「そもそもタイポしなくなるようなメリットもありますよ」
ハスケル子「↑こんな風に、
user.
まで打つと入力候補が出てくるので」
ハスケル子「その中から選択すればいいんです」ワイ「おお...」
ワイ「もう、使わん理由ないやん...」ワイ「ハスケル子先生...!!」
ワイ「TypeScriptがしたいです......」社長「ほな、決まりやな!」
そんなこんなで終業時刻
ワイ「ハスケル子ちゃん、今日も色々教えてくれてありがとうやで...」
ハスケル子「教えるの楽しいから全然いいですよ」
ワイ「ハスケル子ちゃんは凄いなぁ...まだ中学生なのに色々知ってて...」
ワイ「チート過ぎて、見てると自分が情けなくなってくるわ...」
ワイ「負け過ぎてるんやもん...」ハスケル子「私だって同じですよ」
ハスケル子「いつも友達に嫉妬してます...」
ハスケル子「でも...誰かに負けてても、誰かより劣っていても」
ハスケル子「今の自分から精一杯生きるしかないんです」
ハスケル子「自分なりの精一杯をやりましょう」ワイ「ハスケル子ちゃん...!」
ハスケル子「...もしくは、死ぬとか...」
ワイ「いや死を提案すな!」
〜おしまい〜
続編もよろしくやで
4歳娘「パパ、実行時エラーの出ないフロントエンド言語ってなーんだ?」
おまけ:簡単にReact + TypeScriptを始めるには
React + TypeScriptを触ってみたい人は、
npx create-next-app --example with-typescript my-app-name
cd my-app-name
npm run dev
↑この3つのコマンドを打ってから
http://localhost:3000/
にアクセスするだけやで!
(もちろんNode.jsは先にインストールしといてや!)
- 投稿日:2020-01-12T23:10:18+09:00
初心者による DOM イベントを使った処理
概要
ブラウザ上では、さまざまなイベントが発生します。クライアントサイドのjavascriptでは、それらのイベントに応じて処理をします。これをイベント駆動型モデルという。
イベント一覧
javascriptがキャッチするイベントを一覧にしてみる
読み込み関係
abort : 画像の読み込みを中断した時 load : ページ/画像の読み込みが終了した時 unload : ほかのページに移動するときマウス関係
click : クリック時 dbclick : ダブルクリック時 mousedown : マウスボタンを押したとき mouseup : マウスボタンを離したとき moousemove : マウスポインターが移動した時 mouseover : マウスポインターが要素に乗った時 mouseout : マウスポインターが要素から外れた時 mouseenter : マウスポインターが要素に乗った時 mouseleave : マウスポインターが要素から外れた時 contextmenu : コンテキストメニューが表示される前要素がネストしている場合に注意!
mouseover/mouseroutは親要素から子要素への出入りで反応する。
mouseenter/mouseleaveは親要素から出ない限り反応しない。キー関係
keydown : キーを押した時 keypass : キーを押している時 keyup : キーを離した時フォーム関係
change : 内容が変更されたとき reset : リセットボタンが押されたとき submit : サブミットボタンが押されたときその他
resize : 要素のサイズを変更した時 scroll : スクロールした時イベントドリブンモデル(イベント駆動型モデル)
イベント駆動型モデルをなすためには、
- どの要素で発生したのか
- どのイベントが発生したのか
- どのハンドラー/リスナーに関連付けるか
を定義しておく必要がある。
javascriptでは3の関連付けを行うために以下の方法を提供している。
- タグ内の属性として宣言する
- 要素オブジェクトのプロパティとして宣言する
- addEventListenerメソッドを使う
タグ内の属性として宣言する
一番シンプルな実装方法。宣言方法は
<タグ名 onイベント名="Javascriptのコード" />となる。一般的にはコードを直接書くのではなく関数を別で定義して呼び出す形となる。
sample.html<input type="text" value="" onClick="on_click()" />sample.jsfunction on_click() { alert('クリックされました!') }なるべく、htmlファイルとjsファイルで分けたほうが良い。
要素オブジェクトのプロパティとして宣言する
htmlファイルには一切書かず、jsファイルで設定する方法。
sample.jsobj.onEvent = function() { //コード } //obj : windowオブジェクトもしくは要素オブジェクト //Event : イベント名 obj.onEvent = func //func : 関数の定義はべつにしてある場合関数の定義をその場でするか、別にしてあるかの違いで二種類の書き方がある。
sample.html<button id="btn" value="">ボタン</button>sample.js//windowがロードされたときにイベントハンドラーを定義 window.onload = function() { document.getElementById("btn").onclick = function() { window.alert("ボタンがクリックされました!") } }この書き方には注意点が3つある。
1つ目は、onClickのようにイベント名を大文字にするのではなく、onclickのように小文字にする必要があるということ。
2つ目は、プロパティとして登録するのは関数オブジェクトである必要があるため、window.load = get のように関数呼び出しにしてはいけない。
3つ目は、要素を取得する場合、window.loadの配下ではないといけない。ページが読み込まれる前に探してしまう場合がある。addEventListeneメソッドで宣言する
onClickのようなイベントハンドラーでは、「同一の要素/同一のイベントに対して複数のイベントハンドラーを紐づけられない」という問題があります。
ここで登場するのが、イベントリスナーです。イベントリスナーは「同一要素の同一イベントに対して、複数のイベントハンドラーを紐づけできるイベントハンドラー」です。
イベントリスナーを登録するのは、addEventListenerの役割です。sample.jselem.getElementById(type, listener, capture) //elem : 要素オブジェクト type : エベントの種類 //listener : イベントに対して実行すべき処理 //capture : イベントの方向で実装することができる。
sample.html<input id="btn" type="button" value="ダイアログ表示" />sample.js//ページがロードされたタイミングで処理しなさい document.addEventListener('DOMContentLoad', function() { document.getElementById('btn').addEventListener('click', function() { window.alert('ボタンがクリックされました') }, false) }, false)ここで使われている、onloadイベントハンドラーはコンテンツ本体とすべての画像がロードされたところで実行、DOMContentLoadedイベントリスナーはコンテンツ本体がロードされたところで実行、という違いがある。
画像が使われているというような特別な理由がない限り、DOMContentLoadedイベントリスナーで表すのが基本となっている。参考資料
山田祥寛様 「javascript本格入門」
- 投稿日:2020-01-12T22:56:30+09:00
JavaScript で現在開いているページのドメイン・ホスト名を取得する
- 投稿日:2020-01-12T22:26:46+09:00
JavaScriptのイベント
イベントリスナー
イベントリスナーの追加
jsイベントターゲット.addEventListener(イベント名,リスナー,[オプション])イベントリスナーの削除
jsremoveEventListener(イベント名,リスナー,[オプション])イベントリスナーを一度だけ呼び出す
addEventListenerのオプション 内容 capture キャプチャーフェーズで取得するかどうか(真偽値) once リスナーの呼び出しを一回にするか(真偽値) passive パッシブイベントかどうか(真偽値) イベントの情報自体を取得する
MouseEventなどイベント自体の情報を取得する
eventは任意の文字列です。
jsbutton.addEventListener("click",(event)=>{ console.log(event); })イベントが発生した要素を取得する
eventは任意の文字列です。
jsbutton.addEventListener("click",(event)=>{ console.log(event.tareget); })js//オプション指定 const option = { once: true } const clickAlart = () => { alert("クリックされた"); } const button = document.querySelector("button"); button.addEventListener("click", clickAlart, option)クリックイベント
イベント 内容 click 左ボタンシングルクリック dblclick 左ボタンダブルクリック contextmenu 右ボタンクリック mousedown マウスのボタンを押した時 mouseup マウスのボタンを離した時 html<button>ボタン</button>jsconst button = document.querySelector("button"); button.addEventListener("click",()=>{ console.log("クリックされた"); })マウスイベント(クリックイベント以外)
イベント 内容 mousemove 対象エリア内でマウスを動かした時 mouseenter 対象エリアにカーソルが入った時 mouseleave 対象エリアからカーソルが出た時 mouseover 対象エリアにカーソルが入った時(バブリングあり) mouseout 対象エリアからカーソルが出た時(バブリングあり) mousewheel マウスホイールを回転させた時 バブリング:イベントが親要素や先祖要素に伝わることです。
js対象エリア.addEventListener("mousemove",()=>{ message.innerHTML="マウスが動いた";} )html<div class="target""></div> <p class="message"></p>css.target { height: 100px; background-color: red; }jsconst message = document.querySelector(".message"); const target = document.querySelector(".target"); target.addEventListener("mousewheel", () => { message.innerHTML = "赤い領域内でマウスホイールが動いた"; })マウスの座標を取得
マウスの座標 内容 event.clientX ブラウザ左上を基準としたX座標 event.clientY ブラウザ左上を基準としたY座標 event.offsetX 要素左上を基準としたX座標 event.offsetY 要素左上を基準としたY座標 event.pageX ページ左上を基準としたX座標 event.pageY ページ左上を基準としたY座標 event.screenX 画面左上を基準としたX座標 event.screenY 画面左上を基準としたY座標 js対象エリア.addEventListener("mousemove",(event)=>{ console.log(event.clientX,event.clientY); });html<div class="target"></div>css.target { height: 100px; background-color: red; }js//ターゲットの領域を取得 const target = document.querySelector(".target"); //ターゲットの領域内でマウスカーソルが動くたびに座標を表示 target.addEventListener("mousemove", (event) => { console.log(event.clientX, event.clientY); })タッチイベント
イベント 内容 touchstart タッチ開始時 touchmove タッチポイント移動時 touchend タッチ終了時 html<p class="target"></p> <p class="message"></p>css.target { height: 100px; background-color: red; }jsconst text = document.querySelector(".target"); const message = document.querySelector(".message"); text.addEventListener("touchstart", () => { message.innerHTML = "タッチ開始"; }); text.addEventListener("touchmove", () => { message.innerHTML = "タッチムーブ"; }); text.addEventListener("touchend", () => { message.innerHTML = "タッチ終了"; });タッチイベントの情報を取得する
プロパティ 内容 changedTouches タッチ情報の配列 clientX ブラウザ左上基準のX座標 clientY ブラウザ左上を基準としたY座標 offsetX 要素左上を基準としたX座標 offsetY 要素左上を基準としたY座標 pageX ページ左上を基準としたX座標 pageY ページ左上を基準としたY座標 screenX 画面左上を基準としたX座標 screenY 画面左上を基準としたY座標
タッチイベントとマウスイベントの違い 内容 タッチイベント 複数同時に起こる場合がある マウスイベント 複数同時に起こらない html<p class="target"></p> <p class="message"></p>css.target { height: 100px; background-color: red; }jsconst target = document.querySelector(".target"); const message = document.querySelector(".message"); target.addEventListener("touchmove", () => { const touch = event.changedTouches; message.innerHTML = `${touch[0].pageX.toFixed(2)}<br>${touch[0].pageY.toFixed(2)}`; });toFixed(2)で小数点第2位までになるように四捨五入しています。
テキスト選択時
イベント 内容 selectstart テキスト選択開始時 html<p class="text">ああああああああああああああ</p>js//ターゲットの取得 const text = document.querySelector(".text"); text.addEventListener("selectstart", () => { console.log("文字列の選択が開始された"); });キーボードイベント
イベント 内容 keydown キーが押された時 keypress キーが押された時(文字キーのみ=修飾キー以外) keyup キーが離された時 1つのキーを押して離した時、keydown→keypress→keyupの順番に発生します。
html<textarea></textarea>htmlconst textarea = document.querySelector("textarea"); textarea.addEventListener("keypress", () => { console.log("キーが押された"); });入力されたキーを判定する
プロパティ 内容 key ボタンの値 keyCode ボタンのコード code キーの名前 repeat キーを押しっぱなしにしているかどうか(真偽値) isComposing 入力が未確定かどうか(真偽値) html<textarea></textarea>jsconst textarea = document.querySelector("textarea"); const keytype1 = (event) => { console.log(`値:${event.key}`); console.log(`コード:${event.keyCode}`); console.log(`名前:${event.code}`); console.log(`未確定?:${event.isComposing}`); console.log(`alt?:${event.altkey}`); } textarea.addEventListener("keyup", keytype1) const keytype2 = (event) => { console.log(`押しっぱなし?:${event.repeat}`); } textarea.addEventListener("keydown", keytype2)イベントを発火させる
メソッド 内容 dispatchEvent(イベント) イベントを発生させる new Event("イベント名",[{detail:値}])
イベントを生成する クリックしなくても、3秒後にクリックイベントを発火させる
html<p class="target"></p>jsconst target=document.querySelector(".target"); target.addEventListener("click",()=>{ target.innerHTML="クリックされた"; }); setTimeout(()=>{ target.dispatchEvent(new Event("click")); },3000);デフォルトのイベントをキャンセルする
メソッド 内容 preventDefault() イベントのデフォルトの挙動をキャンセル マウスホイールを無効化
jsdocument.querySelector(".target").addEventListener("wheel",(event)=>{ event.preventDefault(); })タッチ開始を無効化
jsdocument.querySelector(".target").addEventListener("touchstart",(event)=>{ event.preventDefault(); })ドラッグ&ドロップ
ドラッグする要素のイベント 内容 dragstart 要素のドラッグ開始時 drag ドラッグしている時 dragend ドラッグ終了時
ドラッグを受ける要素のイベント 内容 dragenter ドラッグしながら要素に入った時 dragover ドラッグしながら要素に存在するとき dragleave ドラッグしながら要素から出た時 drop 要素をドロップしたとき
ドラッグのイベント情報 内容 event.dataTransfer.files ドロップされたファイル情報 html<div id="startArea"> <div id="target" draggable="true"> <p id="targetword">Drag me!</p> </div> </div> <div id="goalArea"> </div>cssbody { position: relative; } #target { width: 98px; height: 98px; background-color: darkcyan; border: solid 1px #000; box-sizing: border-box; } #targetword { width: 100px; height: 100px; display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; margin: 0; } #startArea { width: 100px; height: 100px; margin: 10px 0; border: solid 1px black; position: fixed; top: 10px; left: 10px; box-sizing: border-box; } #startword { display: flex; align-items: center; justify-content: center; color: orange; font-weight: bold; width: 100px; height: 100px; margin: 0; } #goalArea { width: 100px; height: 100px; margin: 10px 0; border: solid 1px black; position: fixed; bottom: 10px; right: 10px; box-sizing: border-box; } .dragging { display: none; } .dragged { background-color: orange; }js//ターゲットの取得 const target = document.querySelector('#target'); //スタートエリアの取得 const startArea = document.querySelector('#startArea'); //ゴールエリアの取得 const goalArea = document.querySelector('#goalArea'); //ターゲットのドラッグが開始された時 target.addEventListener('dragstart', () => { console.log("dragstartイベント"); }); //ドラッグ中の時 target.addEventListener('drag', () => { console.log("dragイベント"); //ターゲットを非表示にする target.classList.add("dragging"); }); //ドラッグが終了した時 target.addEventListener('dragend', () => { console.log("dragendイベント"); //ターゲットを表示にする target.classList.remove("dragging"); }); //スタートエリアに入った時 startArea.addEventListener('dragenter', () => { console.log("dragenterイベント"); //スタートエリアに色を着ける startArea.classList.add("dragged"); }); //ゴールエリアに入った時 goalArea.addEventListener('dragenter', () => { console.log("dragenterイベント"); //ゴールエリアに色を着ける goalArea.classList.add("dragged"); }); //スタートエリアから離れた時 startArea.addEventListener('dragleave', (event) => { console.log("dragleaveイベント"); //スタートエリアの背景色を戻す startArea.classList.remove("dragged"); }); //ゴールエリアから離れた時 goalArea.addEventListener('dragleave', (event) => { console.log("dragleaveイベント"); //ゴールエリアの背景色を戻す goalArea.classList.remove("dragged"); }); //スタートエリア上にある時 startArea.addEventListener('dragover', (event) => { console.log("dragoverイベント"); //dropの前処理 event.preventDefault(); }); //ゴールエリア上にある時 goalArea.addEventListener('dragover', (event) => { console.log("dragoverイベント"); //dropの前処理 event.preventDefault(); }); //スタートエリアでドロップされた時 startArea.addEventListener('drop', () => { console.log("dropイベント"); //ターゲットをスタートエリアに追加 startArea.appendChild(target); }); //ゴールエリアでドロップされた時 goalArea.addEventListener('drop', () => { console.log("dropイベント"); //ターゲットをゴールエリアに追加 goalArea.appendChild(target); });印刷
イベント 内容 afterprint 印刷設定を閉じた時 beforeprint 印刷直前 例:印刷時に印刷用クラスを適用する
html<p class="normal" id="sample">Lorem, ipsum dolor sit amet consectetur adipisicing elit. Rerum doloribus ex nostrum sunt magni, itaque veniam sapiente provident velit quo vero, quaerat vitae porro cumque. Sunt dolore impedit saepe praesentium!</p>css.normal { color: red; } .print { color: blue; }jsconst sample = document.querySelector("#sample"); window.addEventListener("afterprint", () => { //印刷用クラス削除 sample.classList.remove("print"); }); window.addEventListener("beforeprint", () => { //印刷用クラス追加 sample.classList.add("print"); });読み込み完了
イベント 内容 load リソース含め全ての読み込み完了時 DOMContentLoaded DOMツリーの構築完了時 jswindow.addEventListener("load", () => { console.log("ページ読み込み完了"); }) window.addEventListener("DOMContentLoaded", () => { console.log("DOMツリーが準備できた"); })読み込みエラー
イベント 内容 error 読み込み時にエラーが発生した時 html<img src="xxxxxxxxxxxxxxx.jpg">jsconst img = document.querySelector("img"); img.addEventListener("error", () => { console.log("画像表示エラー"); });
- 投稿日:2020-01-12T22:03:12+09:00
docsifyで数式を書けるようにする
はじめに
docsifyは、ドキュメントWebページを作成するツールです。静的なHTMLをジェネレートするのではなく、Markdownをパースして表示するSPAのように動作します。
セットアップ
macOS Catalina 10.15.2 で作業しました。docsifyは、npmでインストールできます。
$ npm i docsify-cli -ginitコマンドで、ドキュメントの雛形を作ります。
$ dosify init hoge
serveコマンドで、ローカルにサーバーが立ち上がりプレビューが見ることができます。もととなるMarkdownファイルが変更されると、スグに反映されます。
$ docsify serve hoge Serving /Users/yoshoku/hoge now. Listening at http://localhost:3000
数式を書けるようにする
数式の表示には、docsify-katexを利用します。KaTeXは、WebページでTeXによる数式をレンダリングするライブラリで、有名なMathJaxよりも速く動作するそうです。docsify-katexは、このKaTeXをdocsifyで利用できるようにしたものです。
セットアップ
利用は簡単で、initコマンドで作成されたindex.htmlに、scriptタグをbodyタグ内の末尾などに追加するだけです。
... 省略 <!-- CDN files for docsify-katex --> <script src="//cdn.jsdelivr.net/npm/docsify-katex@latest/dist/docsify-katex.js"></script> <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/katex@latest/dist/katex.min.css"/> </body> </html>これでOKです。
数式の書き方
\$でくくります。\$が一つであれば行内に、\$が二つであれば改行して、数式をレンダリングします。
改行する感じの数式 $$\mathbf{x}\in\mathcal{R}^{d}$$ です。 改行しない感じの数式 $\mathbf{x}\in\mathcal{R}^{d}$ です。これが、以下のようになります。
おわりに
docsifyは気軽に書けていいですね。
- 投稿日:2020-01-12T21:51:20+09:00
朝会のfacilitatorをランダムに決めるslack bot
これは何か
- 平日の決まった時間に、randomに1人、Daily Scrum (≒ 朝会)のFacilitatorを決めるslack botの作り方
背景
- Scrum開発を円滑に進めていく上で、Scrum Masterに依存した運用体制はScrum teamの目指すべき姿ではない
- その課題を解決する1つの手段として、Daily ScrumのFacilitatorを回すような運用を入れてみたら、メンバーが能動的になった
アウトプットイメージ
- 平日の決まった時間に、SpreadSheetから1人randomに選択し、特定のSlackにpostする
SpreadSheet
Slack Channel
ソースコード
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet() var lastrow = sheet.getLastRow(); var lastcol = sheet.getLastColumn(); var sheetdata = sheet.getSheetValues(1, 1, lastrow, lastcol); var calJa = CalendarApp.getCalendarById('ja.japanese#holiday@group.v.calendar.google.com'); function Facilitator(){ var today = new Date(); var weekInt = today.getDay(); if(weekInt == 0 || weekInt == 6){ return false; } else if(calJa.getEventsForDay(today).length > 0){ return false; } else{ var row = Math.floor(Math.random() * 7) + 2; var Name = sheetdata[row][0]; var row2 = Math.floor(Math.random() * 7) + 2; var Face = sheetdata[row2][1]; var slackAccessToken = 'xxxxx(slack workspaceで一意に持っているので、要確認)'; var slackApp = SlackApp.create(slackAccessToken); var channelId = "xxx-yyy-2020(postしたいslack channel名)"; var Message = slackApp.postMessage(channelId,"Today's facilitator is…" + Name +"-san"+ " " + "Let's GoGo!" + Face); } }ざっくり構成解説
SpreadSheetにアクセスして、記述してある内容を取得する
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet() var lastrow = sheet.getLastRow(); var lastcol = sheet.getLastColumn(); var sheetdata = sheet.getSheetValues(1, 1, lastrow, lastcol); var calJa = CalendarApp.getCalendarById('ja.japanese#holiday@group.v.calendar.google.com');
SpreadsheetApp.getActiveSpreadsheet().getActiveSheet()
でGASからアクティブなSpreadSheetにアクセスしますgetLastRow()
で、アクセスしたSpreadSheetの最後の行を取得しますgetLastColumn()
で、同様に最後の列を取得しますgetSheetValues(startrow, startcol, lastrow, lastcol)
で、アクセスしているSpreadSheet内の値全体を取得します
- 前述の
getLastRow(), getLastColumn()
で対象範囲内の最後の行と列を動的に取得できるので、メンバーが増える or 減る(行++ or 行--)、投稿したい要素が増える or 減る(列++ or 列--)場合でも大丈夫CalendarApp.getCalendarById('省略')
では、Google Calenderの 日本の祝日カレンダーにアクセスしていますSpreadSheetから、特定のNameとFaceを選択して、Slack Channelに送る
function Facilitator(){ var today = new Date(); var weekInt = today.getDay(); if(weekInt == 0 || weekInt == 6){ return false; } else if(calJa.getEventsForDay(today).length > 0){ return false; } else{ var row = Math.floor(Math.random() * 8) + 2; var Name = sheetdata[row][0]; var row2 = Math.floor(Math.random() * 8) + 2; var Face = sheetdata[row2][1]; var slackAccessToken = 'xxxxx(slack workspaceで一意に持っているので、要確認)'; var slackApp = SlackApp.create(slackAccessToken); var channelId = "xxx-yyy-2020(postしたいslack channel名)"; var Message = slackApp.postMessage(channelId,"Today's facilitator is…" + Name +"-san"+ " " + "Let's GoGo!" + Face); } }
today.getDay()
で、指定された日付の「曜日」を取得している
- 返される値は0~6で、
0 == 日曜日, 1 == 月曜日... ,6 == 土曜日
となりますweekInt == 0 || weekInt == 6
で、休日(土曜日 or 日曜日)は送らないようにしているcalJa.getEventsForDay(today).length > 0
で、祝日のイベントがカレンダー上に1つでもある時は送らないようにしているMath.floor(Math.random() * 8) + 2
では、最大で9、最小で2となる行番号を1つ取得して、1列目に適用している
Math.floor()
で、()内の値を小数点以下を切り捨てるMath.random()
で、0以上1未満 (0は含むが1は含まない)の間でrandomな値を返す- 次に出てくる
row2
の中身も同じことをしているslackAccessToken
周りは、GASとSlackではじめるチャットボット〜初心者プログラマ向け〜辺りを参考にするchannelId = "xxx-yyy-2020(postしたいslack channel名)"
ではpostしたいSlack Channel名を#抜き で記述するslackApp.postMessage(channelId,"Today's facilitator is…" + Name +"-san"+ " " + "Let's GoGo!" + Face)
で、実際に該当のSlack Channelにpostするが、textで直接記述している部分は変更してもらって大丈夫(ex."Today's facilitator is…"
,"-san"
etc)トリガー
- Daily Scrumが毎朝12:30~12:45だったので、余裕を持って、10:00~11:00の間に発火するように設定してます
参考
- 毎週のお掃除当番をSlackへランダムで通知する
- 基本構成は上記をかなり参考にさせていただきましたmm
- 掃除当番の割当を Google Apps Script で自動化して Slack の BOT として通知
- 筆者は上記のpostを参考にしていたので、先にそちらを確認するとスムーズかもしれません
- 投稿日:2020-01-12T21:46:51+09:00
【Jest】Vueのテストを書こう!
Vueのテストを書こう!
Vue.jsのコンポーネントのテストについてナレッジを貯めていきます。(随時追加)
テキスト表示についてのテスト
import { mount } from '@vue/test-utils' import HelloWorld from '@/components/HelloWorld.vue' describe('HelloWorld.vue', () => { it('renders a HelloWorld', () => { const wrapper = mount(HelloWorld) expect(wrapper.text()).toMatch("HelloWorld!!") }) })DOM要素についてのテスト
import { mount } from '@vue/test-utils' import HelloWorld from '@/components/HelloWorld.vue' describe('HelloWorld.vue', () => { it('renders a HelloWorld', () => { const wrapper = mount(HelloWorld) expect(wrapper.html()).toMatch("<div><p>HelloWorld!!</p></div>") }) })子コンポーネントをスタブにしてテスト
shallowMount
メソッドで子コンポーネントをスタブ(代替えの偽物)としてマウントします。
※mount
だと子コンポーネントまですべてマウントされます。const shallowWrapper = shallowMount(Parent) console.log(shallowWrapper.html())
- ログの結果
子コンポーネントが
<vuecomponent-stub />
とスタブとなって出力されています。<div><vuecomponent-stub></vuecomponent-stub></div>
コンポーネントのfactory関数を定義する
一番上に
values
オブジェクトをまとめてdata
にして、新しいwrapper
インスタンスを返すファクトリ関数を宣言します。このようにすると、すべてのテストで
const wrapper = shallowMount(Foo)
を複製する必要がありませんimport { shallowMount } from '@vue/test-utils' import Foo from './Foo' const factory = (values = {}) => { return shallowMount(Foo, { data () { return { ...values } } }) } describe('Foo', () => { it('welcome メッセージを描画する', () => { const wrapper = factory() expect(wrapper.find('.message').text()).toEqual("Welcome to the Vue.js cookbook") }) it('usernameが7未満のときエラーを描画する', () => { const wrapper = factory({ username: '' }) expect(wrapper.find('.error').exists()).toBeTruthy() }) it('usernameが空白のときエラーを描画する', () => { const wrapper = factory({ username: ' '.repeat(7) }) expect(wrapper.find('.error').exists()).toBeTruthy() }) it('usernameが7文字かそれ以上のとき、エラーが描画されない', () => { const wrapper = factory({ username: 'Lachlan' }) expect(wrapper.find('.error').exists()).toBeFalsy() }) })参考URL
vue-testing-handbook
➡︎より実践的な使い方がわかる
- 投稿日:2020-01-12T20:39:36+09:00
React Native (Expo) を使ってはじめてのモバイルアプリ制作 きっかけから制作、公開まで
昨年末12月28日にはじめて制作したアプリを App Store に公開しました。
"tagTimeLog Lite"タグを使って時間を記録するアプリです。かなり単純なアプリです。
モバイルアプリを開発したきっかけから制作、公開までをまとめました。きっかけ
複数の仕事が重なり、それぞれのプロジェクトごとに実際にどれだけの時間を消費しているのか計測したいと思い、既存のアプリを探していくつか試して見たのですが、自分が欲しいと思うようなシンプルで手軽なアプリが見つかりませんでした。
アプリを探すのにも疲れたので「仕方ない、自分で作るか」ということで一念発起してアプリ開発を思い立ちました。
仕事で使っている WordPress でも導入された React の勉強もしなければならなかったので、 React を使ってアプリを作れば一石二鳥ということで無駄になることはないと自分を納得させて開発を開始しました。仕様
自分が欲しいと思ったアプリの仕様。
- 操作がわかりやすい
- シンプル
- 少ないタップ数でログが取れる
- 動作が早い
- ログイン不要
- エキスポート機能
- メモを取れるようにする
- ログの時間を修正できるようにする
最初からこのようにリスト化するようなことはせず、何となくこんな感じでとぼんやり考えながら作っていきました。
制作途中であれも欲しい、これもあったらいいなと思いつくことがありましたが、元々の「シンプルで手軽」という基本コンセプトととりあえず早く公開することを優先して機能を絞り込みました。
そもそも技術がないので複雑なことはできませんし。集計自体はスプレッドシートで行う予定でしたので、CSVファイルへのエキスポートは必須でした。
ブラウザベースかモバイルネイティブか
React を使用することを前提としていましたが、React 自体を全く理解しておらず、React Native(モバイル用フレームワーク) 以前の話だったので、 React を使ってブラウザベースで制作することにしました。
JavaScript の再学習
これまで jQuery を何となく使ってきた程度の JavaScript の知識だったので、React を学習するには、 JavaScript の知識が全く足りず、まずは JavaScript の学習から始めました。
React においては ES6以降 を使っての制作が基本なので ES6 を中心に JavaScript を学習しなおしました。JavaScript の学習に使用したWebサイトや書籍
React の学習
JavaScript をある程度学習できたので React の学習です。
個人的には、React の重要なポイントは state のように思いました。
あとは JavaScript (ES6) の知識で書ける感じです。
なので、もし、React の学習に躓いている方がおられたら JavaScript の知識について少し見直してみても良いかもしれません。
少し難しく感じたのは、ルーティングのところでの props や値の受け渡しです。
コンポーネントの構成をどうすればよいのか迷いました。
あと、 JSX は慣れるしかないと思います。React の学習に使用したWebサイトや書籍
React の勉強には公式サイトが最も役に立ちました。
本などは少し古かったりするので、先ずは公式サイトで勉強し、わからないところを本で補うようにしました。React を使ってブラウザベースのアプリを制作
Create React App を使って作成しました。
勉強しながらの制作だったので、あらゆる所でつまずきましたが、公式サイトや書籍、Web 上の情報を参考にしながら一定ペースを保って完成までたどり着きました。
アプリ制作のおかげで React の概要については、おおよそ理解できたように思います。使用したライブラリー、データベース
- ライブラリー: react-router-dom
- データベース: IndexedDB
ライブラリーは 、react-router-dom を入れました。
データベースは、ブラウザ用のローカルストレージ IndexedDB を使いました。react-router-dom の勉強には「速習 React 速習シリーズ」、 IndexedDB の勉強には MDN が非常に参考になりました。というか、IndexedDB には、 MDNしかないと思います。
ブラウザベースからモバイルへ
最初に希望していた機能は、ほぼこのブラウザベースのアプリで満たしており、完成後しばらくアプリを使用していました。
しかし、使用していて少し不安になることがありました。
それは、データベースの IndexedDB です。IndexedDB は個人的には使いやすく非常によいデータベースだと思うのですが、ブラウザ依存のデータベースなので、例えば何かの理由でブラウザをアンインストールしてしまうとデータベースも消えてしまいます。
私が Web サイト制作を仕事にしていることもありブラウザの設定を変えるなど、荒い(?)使い方をする場合もあるのでブラウザ依存のデータベースを使用していることが少し不安でした。そこで、React の思想「Learn Once, Write Anywhere」を思い出し、専用のアプリであればアンインストールするようなこともないため、「仕方ない、React Native を使ってモバイルアプリを作るか。」ということでモバイルアプリの制作を開始しました。
React Native (Expo) を使ってネイティブモバイルアプリを制作
React で作成したアプリを React Native で作り直しです。
React Native であっても一部ネイティブ言語を触る必要があります。
ネイティブ言語は触りたくない(勉強したくない)ので、 Expo を利用することにしました。
Expo は、実装できる機能の範囲が狭まるようですが、最初から最後まで JavaScript だけでアプリ公開できるようになる React Native 向け支援ツールだそうです。React と React Native の違いとしては、根本的なところは React とほぼ一緒だと思いますが、見た目の構築とルーティング(画面遷移)の設定は、まったく違うように思いました。
全体的に組みなおす必要がありました。
モバイルと比較して Web のほうが自由度が高いので、もし、モバイルと Web で、見た目を同じにしようと考えているのであれば、モバイルから作成したほうが良いように思いました。React Native (Expo) の学習に使用したWebサイトや書籍
React Native については、Web 、書籍ともに日本語化されているものが少なく、基本的には英語で書かれている公式サイトを見て勉強しました。
英語がわからないので理解するのにかなり時間がかかってしまいましたが、Google 翻訳など利用しながら、とりあえず制作できるところまでは理解できました。使用したライブラリー、データベース
package.json の中身です。
- expo
- expo-ads-admob
- expo-sharing
- expo-sqlite
- papaparse
- react
- react-dom
- react-native
- react-native-elements
- react-native-gesture-handler
- react-native-keyboard-aware-scroll-view
- react-native-reanimated
- react-native-svg
- react-native-swipe-list-view
- react-native-web
- react-navigation
- react-navigation-stack
- react-navigation-tabs
- sentry-expo
ライブラリー、データベースで気になったところ
Expo
JavaScript だけでアプリの制作から申請まですべてできるのは大変ありがたいです。
ただ、ちょっと気になるのはアプリのファイルサイズが大きくなるということです。
Expo を使用すると仕方がないことらしいのですが、確かにより複雑なことができる他の Swift などで作られたであろうと思われるアプリと比較すると、ちょっと大きいなという感じがしました。
今後ブラッシュアップして小さくできるか確認していきたいと思います。react-navigation
React から React Native に移行して最も戸惑ったのがルーティング(ページ遷移)です。
React の "react-router-dom" とまったく違うように感じました。react-native-swipe-list-view
当初、"react-native-swipeout" を使っていたのですが、expo をアップデートしたところエラーが出るようになったので、"react-native-swipe-list-view" に変更しました。
ライブラリーは、大変便利でありがたいのですが、アップデートに対応されていないと代替ライブリーがない場合は、アプリ自体が更新できないことになってしまうので、依存度はなるべく低くした方が良いと思いました。
expo-ads-admob
ローカル環境でのテスト広告や publish 後の正式広告は、すぐに表示されるのですが、iOS App Store 公開、申請後はすぐに表示されず、2、3日後に表示されるようになりました。
他の方のブログなどを見ていると普通にそれぐらい待たされることがあるようです。sqlite
データベースは、 sqlite を使いました。当初は、 realm を使いたいと思っていましたが、Expo が対応していなかったので仕方なく sqlite にしました。
sqlite を使ってみたところ特に問題なく使えてますので、とりあえず良かったです。App Store (iOS) への申請と公開
担当者によって厳しさが違う?
App Store (iOS) への一度目の申請は、一発 OK で何の修正命令もなく翌日には公開となりました。
Apple の審査は、厳しいと思っていたので意外でした。
公開翌日に見た目のところを少し調整してアップデートし、審査申請に出したところエラー表示され修正命令が来ました。
修正内容は「アプリの名前がガイドライン違反してるので名前変えろ」(英語わからないのでおそらくこんな感じ)でした。ガイドラインでは「アプリの名前に金額を表すようなワードは入れるな」ということらしいです。
現在のアプリ名は "tagTimeLog Lite" なのですが、最初の申請時は "tagTimeLog Free" という名前で申請していました。
この "Free" が金額を表しているようです。
私の勉強不足だったので反省なのですが、いくらなんでも最初の申請時に気づかなかったのかなと思いました。
Apple の審査が早くなったとどこかで読みましたが、担当者によって能力に差があり、一部で審査が甘くなっているのかもしれません。名前を変更し、再審査を依頼、2、3日後に無事通過して公開となりました。
思いつきから制作、公開までの期間
思いつきから公開まで、ほぼ一年かかりました。
通常の受注仕事しながらなので、途中一ヶ月くらいまったく触れなかたということもあるので、ざっくりですが、実質4、5ヶ月くらいでしょうか。特に学習面で React については、公式含めて教材となるような、日本語で記載されている Web サイトがたくさんあるのですが、React Native では日本語がガクッと減って、ほぼ英語で記載されている Web サイトでした。英語が苦手な私は React Native の学習段階でかなり時間を取られた印象です。
アップデートの予定
今後は下記内容を予定してます。
- Android 版作成
- 他の形式による時間表示
- ダークモード (iOS)
- 有料版作成 ほか
以上
- 投稿日:2020-01-12T19:59:08+09:00
【Svelte】Svelte環境をDockerで作ってみた
概要
完成品はこちら
詳しい話はまたブログとかに書きます。
躓いたと
SvelteのみHostとContainerのlocalhostに繋ぐのにすこし手間取りました。
ReactやVueを造ったときは、localhostのままポート番号だけあわせておけばよかったのですが・・・・
ここに書いてあるとおり、sirv
の仕様でそのままだとHost側のlocalhostからContainer側のlocalhostにはつながらない。
なので、sirv
コマンドでContainerのhostを指定する必要がありました。sirv public build --host 0.0.0.0これで、Host側の
localhost
とContainer側の0.0.0.0
が繋がるので、初期画面が表示されるようになります。Sapperは他同様にport:3000同士を繋げば問題なくContainerと繋がります
まとめ
去年くらいから少し話が出てきたSvelte。
まだまだこれから感ありますが、ReactやVueを刺激するような存在になってくれると面白いです。
- 投稿日:2020-01-12T19:59:08+09:00
【Svelte】Svelte環境をDockerで作ってみた【Sapper】
概要
完成品はこちら
詳しい話はまたブログとかに書きます。
躓いたと
SvelteのみHostとContainerのlocalhostに繋ぐのにすこし手間取りました。
ReactやVueを造ったときは、localhostのままポート番号だけあわせておけばよかったのですが・・・・
ここに書いてあるとおり、sirv
の仕様でそのままだとHost側のlocalhostからContainer側のlocalhostにはつながらない。
なので、sirv
コマンドでContainerのhostを指定する必要がありました。sirv public build --host 0.0.0.0これで、Host側の
localhost
とContainer側の0.0.0.0
が繋がるので、初期画面が表示されるようになります。Sapperは他同様にport:3000同士を繋げば問題なくContainerと繋がります
まとめ
去年くらいから少し話が出てきたSvelte。
まだまだこれから感ありますが、ReactやVueを刺激するような存在になってくれると面白いです。
- 投稿日:2020-01-12T19:51:22+09:00
チームの時間管理できるWebチャートを作成したかった
初めに
前職にてヘルプデスクのリーダーを行っていた際、メンバーが今何のタスクを行っているか視覚的に把握できれば、もっとスムーズに休憩時間の調整や緊急度や重要度を意識したタスク管理ができそうだなと思っていました。
そこで時間管理チャートの作成に取り掛かったのですが、いろいろな理由で没になったため供養したいと思います。元ネタ
コピペでWebに埋め込めるシンプルなガントチャート時間割作ってみた
理想にとても近いOSSが公開されていたので、改変して利用させていただくことにしました。画面
機能
- タスクをクリックして選択
- タスクの新規作成・編集・削除
- 現在の時刻をラインで表示
- 30分単位で時間軸をスライド可能
- 画面サイズ自動変更
頑張りましたがデザインがダサいのはどうしようもなかったです。
(元ネタの否定ではありません)
(作成中)
- 投稿日:2020-01-12T19:05:38+09:00
JavaScriptで元の配列を変更せずに並び替えした配列を作る
JavaScriptで配列を並び替えするときに使用する
sort
メソッドは実行した配列自身を変更してしまう。const ary = [1, 2, 3, 4, 5]; const sorted = ary.sort((a, b) => b - a);; console.log(ary); // => [ 5, 4, 3, 2, 1 ] 元の配列まで変更されてしまう! console.log(sorted); // => [ 5, 4, 3, 2, 1 ]元の配列は変更せずに並び替えした配列を作成したい場合は、以下のようにすればいい。
concat
メソッドで配列を複製してからsort
メソッドを行うイメージ。const ary = [1, 2, 3, 4, 5]; const sorted = ary.concat().sort((a, b) => b - a);; console.log(ary); // => [ 1, 2, 3, 4, 5 ] console.log(sorted); // => [ 5, 4, 3, 2, 1 ]参考
- 投稿日:2020-01-12T18:20:38+09:00
エンジニア研修
概要
今回弟がエンジニアデビューするということで、macbookAir(gold)を買ってあげました。
これから9か月間、私の方で研修をおこない、それなりのエンジニアになってもらおうかと思います。そこで弟のように、エンジニアを始めたばかりの人の参考になるように、「研修内容」、「弟が躓いた箇所」や「参考文献など」を記事にしていこうかと思います。
普段私は自分のブログサイトで発信していましたが、誰も見てくれないので、qiitaに書いていくことにしました。
https://campbel.love/が私のサイトです。たまには見てくださいな(笑)弟はそこまで忍耐力もなく、また、ほとんど初心者なので「挫折をしないカリキュラム」、「スムーズな理解」に重点を置くつもりです。まさにゆとり!
なので、少し遠回りをしてしまうかもしれません。
例えば、Dockerを用いてもらいたいけど、初学者の弟には難しいのでMampから始めてもらう。みたいな。(最終的にはdockerにしてもらいますが。)偉そうにカリキュラムは組みましたが、私自身も勉強途中で全部を理解はしていません。弟と一緒に勉強して知識を高めていきたいと思っています。
*もし、こんなやり方の方がいい、これは間違っているなどありましたら、コメントをお願いいたします。弟と私のために。私は文章を書くのがすこぶる苦手なので、わかりづらい事や誤字脱字もあるかと思いますが、どうぞよろしくお願い致します。
予定のカリキュラム
文法の理解などは基本はdotinstallをやってもらいます。(有料なので私が支払うことになりそう。。。)
わからないことがあれば随時きくように。1ヶ月~2ヶ月
・php、html、css、js、mysqlを理解する。(jsから初めてもらおうかなと)
・mampの設定
・macの操作、mampになれる。3ヶ月〜5ヶ月
・jqueryもしくはvueをつかう。vueが使えるようになったら最高。ちなみに私は使用したことがない。
・gitを使えるようにする。
・local環境下でいいのでlaravelでlaravelでサイトを作れるようにする。
「掲示板的なやつ」もしくは「画像投稿サイト」でいい。
デザインパターンはしっかり行う。
リレーション、トランザクションもしっかり行こなう。
ログイン認証もしっかり行う
時間があればrest api作成も行う。
・hostsの理解などここらへんで一区切り。。。
6ヶ月〜7ヶ月
・cicdを使う(cercleciでいいかと)
・awsを使う
・laravelのテスト仕様書も書けるようにする。8ヶ月
・localとawsでdockerを使う。9ヶ月
kubernetesを使用する??これは未定。。。これでかなりレベルの高いエンジニアであると僕は思うが、参考にしているエンジニアチャンネルでは、これが最低ラインだと言っていました。。。(kubernetesは除く)
エンジニアの敷居が上がってきているのかしら?٩( ᐛ )و
しかし、専門学生が2年通うよりも全然最強だと思います!!
ちなみに私はこれらのことを、もちろんこれだけでなく他の勉強もしながらですが、数年かけました。それを弟は9ヶ月で行うため、厳しい道のりになるとおもわれます。弟よ!がんばれ〜!!( ^ω^ )私の課題として、「awsでdockerを使用する」、「kubernetesを使用する」はまだやったことがないので、できるようにしておきます!
なぜmacなのか
一言で言うと使っている人が多いから。
他には、サーバーではlinuxを使用することが多いが、macはwinよりもlinuxに近いからとか、bashが使えるからとか、かっこいいから。賛否両論だと思いますが、とにかくmacを買うことをお勧めします。
高いなと思うかもしれませんが、分割での購入も可能で、2年の分割だと利子はなんと0円です。
あと学割があって最大22000円引きで購入が可能。2020年2月からは新学期キャンペーンで3万ほどのヘッドフォンがついてきますよ!お得です!
参考1日目はマックの設定
1 appleidの作成、osが最新かどうかのチェック
2 アプリのダウンロード(詳細は以下)
3 chromeの拡張のダウンロード(詳細は以下)macにダウンロードするアプリ
ブラウザ
「こんなにいらないやろ」と思うかもしれないが、”エンジニア”には必要。(アプリ欄を見てこれらがないと笑われてしまう。)
chrome
firefox
opera開発に必要なアプリ
サイバーダッグ: サーバーのファイルをいじれる
iterm2: ターミナルの拡張版
sequel pro: mysqlをいじる
vs code: エディタ(高級なメモ帳)
postman: apiを可視化
xcode: 主にアイフォンのアプリを作成するのに使用。アイフォンのアプリを作成しなくとも必要。
mamp: mac、apache、mysql、phpの略。windowsだとxamp。パソコン内に仮想のサーバーを立てることができる。
mampでの開発は時代遅れのゴミで、本来ならば(というよりできるエンジニアは)Dockerを使うべきだが設定はmampの方が断然楽なので、初め(6ヶ月くらい)はmampで対応する。
composer: phpのライブラリ環境コマンド。これは必須。なくても開発はできるが、使用していないのはゴミエンジニアである。チャットアプリ
line: line
chatwork: よく使われるチャットツール
slack: よく使われるチャットツールその他便利なアプリ
app cleaner: アプリをきれいに削除できる
cd to: フォルダから直で移動できる。
clipy: 有料。コピーしたものの履歴が残る。数百円なので絶対に入れた方がいいと思われるアプリの一つ。使ったら世界が変わったように感じた。弟にも買ってあげた。これらはまだ使用しないと思われるので、まだインストールしなくていい
brew cask
git
anyenv: phpしか使わないならば、いらない。python、node.jsをやる場合は入れた方がいい。chromeにダウンロードする拡張の機能
開発はおもにchromeで行うため、chromeの設定はとても大事です。
ダウンロード先はここ
https://chrome.google.com/webstore/category/extensions?hl=ja開発に必要なアプリ
Clear Cache: キャッシュの削除
EditThisCookie: クッキーの編集
Page load time: ページが表示されるまでの時間計測
Quick Javascript Switcher: jsのオンオフ切替。1クリックでできるため、めちゃめちゃ便利です。あると便利なアプリ
Wappalyzer: 自分が閲覧したサイトがどの技術で作られているか確認ができる。
Google のクロム ™ のための時計: chromeに時計を表示させる。
ブックマークサイドバー: ブックマークがマウスオーバーで開ける。お勧めの設定は「マウスを左に持っていき右クリックで表示させる。」個人的にお勧めなアプリ
Douga Getter: 動画のダウンロード
ストリームレコーダー: 動画のダウンロードとりあえず1日目はここまで。お疲れ様です。
- 投稿日:2020-01-12T17:43:54+09:00
[Nuxtjs] base64エンコードする画像のファイルサイズの上限を変更する
TL; DR
nuxt.config.js
のbuildプロパティで、base64エンコードできる画像のファイルサイズの上限を変更できる。概要
- nuxtjsでwebサービスの自社プロダクトの開発と運用をやっているエンジニアです。
- ある日、ロゴの画像を変更してほしいとデザイナーから依頼が来ましたが、
static
ディレクトリにある画像を差し替え社内開発環境にリリースしまいしたが、ブラウザキャッシュが効いてしまい、昔のロゴが表示されてしまいました。- 何かクエリバラメーターをつけるなどして、画像のパスを変更すれば解決するのですが、何か別の解決方法がないかを探したところ、assetsに静的ファイルをおけば、webpackが静的ファイルをbase64エンコードし、htmlやcssに埋め込みをしてくれるみたいでした。が、試したところ、どうやらbase64エンコードしてくれる画像のファイルサイズがデフォルトで1KBと決まっているみたいなので、変更したいと思ったのですが、やり方を探しても見つからなかったので、せっかくならと思い、記事にまとめました。
実際にどうやるか
結論から話すと、下のように書くと、base64エンコードしてくれる静的ファイルのサイズ上限を変更できました。
nuxt.config.jsmodule.exports = { extend(config, { loaders }) { // これで1MB以下のassetsディレクトリにある静的ファイルがbase64エンコードされます。 loaders.imgUrl.limit = 100000 }, }この
loaders
とimgUrl
がどこから来たからというと、下のドキュメントに記載されていました。
https://ja.nuxtjs.org/api/configuration-build/#loaders// loadersの中身 { file: {}, fontUrl: { limit: 1000 }, imgUrl: { limit: 1000 }, // デフォルトは1KB pugPlain: {}, vue: { transformAssetUrls: { video: 'src', source: 'src', object: 'src', embed: 'src' } }, css: {}, cssModules: { localIdentName: '[local]_[hash:base64:5]' }, less: {}, sass: { indentedSyntax: true }, scss: {}, stylus: {}, vueStyle: {} }全部のコードを追えていないため、
imgUrl
のプロパティがどうurl-loader
の設定と紐づいているかまではわかっていないのですが、プロパティの名前から予測することができたため、試したところうまくbase64エンコードされました。ちなみに、base64エンコードされるとhtmlではこのようになります。
まとめ
- これで今後画像のパスを無理やり変更しなくても、画像によってエンコードした結果が変わるので、ブラウザキャッシュでファイルの中身が変更されないという問題は回避されるのかなと思いました。内容が少しニッチな気持ちもしますが、同じ問題で困った人の役に立てればと思い、qiitaにまとめました。最後まで読んでいただき、ありがとうございました。
- 投稿日:2020-01-12T17:33:35+09:00
JavaScript勉強の記録その8: スプレッド構文&レスト構文を利用した配列オブジェクトの操作
スプレッド構文を利用して2つの配列を結合
const/let 定数名/変数名 = [...配列名]とすることで、2つの配列を結合することができます。
例としては、以下のような2つの配列があった場合は、const b = [55, 80, ...a]と記述することで、2つの配列を結合することができます。
この方法をスプレッド構文と呼びます。index.jsconst a = [10, 40, 12, 50]; const b = [55, 80, ...a]; console.log(b); //[55, 80, 10, 40, 12, 50]レスト構文を利用して、配列の要素を別の定数へ代入
配列の要素を別の定数に代入したいという場合はレスト構文が役に立ちます。
下の例ではconst [a, b, ...other] = scores2とすることで、scores2の1番前の要素から順にaとbに要素を代入しています。
scores2の残りの要素は、otherという定数に配列として格納されます。これをレスト構文と呼びます。ちなみに配列の要素を別々の変数や定数に格納することを分割代入と呼びます。
index.jsconst scores1 = [10, 40, 12, 50]; const scores2 = [55, 80, ...scores1]; const [a, b, ...other] = scores2; console.log(a); //=>55 console.log(b); //=>80 console.log(other); //=>[10, 40, 12, 50]
- 投稿日:2020-01-12T17:33:35+09:00
JavaScript勉強の記録その8: スプレッド構文&レフト構文を利用した配列オブジェクトの操作
スプレッド構文を利用して2つの配列を結合
const/let 定数名/変数名 = [...配列名]とすることで、2つの配列を結合することができます。
例としては、以下のような2つの配列があった場合は、const b = [55, 80, ...a]と記述することで、2つの配列を結合することができます。
この方法をスプレッド構文と呼びます。index.jsconst a = [10, 40, 12, 50]; const b = [55, 80, ...a]; console.log(b); //[55, 80, 10, 40, 12, 50]レフト構文を利用して、配列の要素を別の定数へ代入
配列の要素を別の定数に代入したいという場合はレフト構文が役に立ちます。
下の例ではconst [a, b, ...other] = scores2とすることで、scores2の1番前の要素から順にaとbに要素を代入しています。
scores2の残りの要素は、otherという定数に配列として格納されます。これをレフト構文と呼びます。ちなみに配列の要素を別々の変数や定数に格納することを分割代入と呼びます。
index.jsconst scores1 = [10, 40, 12, 50]; const scores2 = [55, 80, ...scores1]; const [a, b, ...other] = scores2; console.log(a); //=>55 console.log(b); //=>80 console.log(other); //=>[10, 40, 12, 50]
- 投稿日:2020-01-12T17:28:54+09:00
JavaScriptのUIライブラリ ReactのHook使用してToDoアプリを作成してみました
はじめに
この記事ではJavaScriptのライブラリであるReactとReactの機能であるhookを使用して簡単なToDoアプリの実装を行います。
以前に書いた記事をベースに書き換えるので、そちらも参考にしてみてください。
JavaScriptのUIライブラリ ReactでToDoアプリを作成してみました
Reactのドキュメントやチュートリアル(三目並べ)を一通り行った後の練習になるように書きたいと思います。
環境構築に関しては、create-react-app
を使用して作成しています。
環境構築に関しては以前書いた記事があります。
ソースコード目次
- コンポーネントの確認
- 各コンポーネントの解説
- まとめ
1. コンポーネントの確認
すぐに動かせる環境を置いておきます。
See the Pen
ReactToDo with Hook by oq-Yuki-po (@oq-yuki-po)
on CodePen.
ReactはUIのパーツをコンポーネントという独立した一つの部品とみなして構成していきます。
今回の例では下記の様に分割しました。
以前は、これらのコンポーネントを全てクラスで定義して行きましたが今回は関数コンポーネントで定義して行きます。ToDoアプリケーションを構成するコンポーネントは全部で4つあります。
ToDo
ToDoアプリケーションの全体を表しますTaskAdd
新しいタスクの追加を行いますTaskList
追加されたタスクをリストにして表示しますTaskItem
一つのタスクを表します2. 各コンポーネントの解説
2-1. ToDo.js
2-1-1. ソース全体
ToDo.jsimport React, { useState, useReducer } from "react"; import TaskAdd from './TaskAdd'; import TaskList from './TaskList'; function reducer(state, action) { switch (action.type) { case 'add': return [ ...state, action.NewTask ]; case 'delete': let TaskIndex = 0; for (var i = 0; i < state.length; i++) { if (state[i].key.toString() === action.TaskId.toString()) { TaskIndex = i; } } return state.filter((_, index) => index !== TaskIndex); default: return state; } } function ToDo() { const [TaskId] = useState(0) const [ToDoList, dispatch] = useReducer(reducer, []) return ( <main className='todo-component'> <TaskAdd id={TaskId} dispatch={dispatch} TaskList={ToDoList}/> <TaskList TaskList={ToDoList} /> </main> ); } export default ToDo;2-1-2. モジュールのインポート
importimport React, { useState, useReducer } from "react"; import TaskAdd from './TaskAdd'; import TaskList from './TaskList';
useState
とuseReducer
がhookと呼ばれるものです。
これを使用することで、クラスコンポーネントで行なっていた状態管理が関数コンポーネントでも行えます。2-1-3. ToDoコンポーネント
ToDoコンポーネントfunction ToDo() { const [TaskId] = useState(0) const [ToDoList, dispatch] = useReducer(reducer, []) return ( <main className='todo-component'> <TaskAdd id={TaskId} dispatch={dispatch} TaskList={ToDoList}/> <TaskList TaskList={ToDoList} /> </main> ); }クラスで定義していた際は、constructorを定義していたと思います。
以前の記事では、下記の様に定義していました。ToDoコンポーネントのconstructorconstructor(props) { super(props); this.state = { TaskList: [], TaskId: 0 }; this.deleteTask = this.deleteTask.bind(this); this.addTask = this.addTask.bind(this); }関数でコンポーネントを定義する際は以下の様になります。
ToDoコンポーネント(クラス版)const [TaskId] = useState(0) const [ToDoList, dispatch] = useReducer(reducer, [])stateを定義する際に
useState
を使用します。
const [状態管理したい変数, 状態を変更する関数] = useState(状態管理したい変数の初期値)
TaskIdは子コンポーネントに渡して、そちらで管理するので変更する関数を定義していません。
(定義してる場所が、そもそもどうなの?みたいなツッコミはご勘弁を。後々にAPIとの連携を見越した実装だと解釈お願いします。。。)
const [状態管理したい変数, 状態を変更する関数] = useState(初期値)
配列や複雑なロジックをstateに持たせる時は、TaskListの様にuseReducer
を使用します。
const [状態管理したい変数, reducerで定義した関数(dispatch)] = useReducer(reducer, 状態管理したい変数の初期値);
公式の引用通常、useReducer が useState より好ましいのは、複数の値にまたがる複雑な state ロジックがある場合や、
前の state に基づいて次の state を決める必要がある場合です。また、useReducer を使えば
コールバックの代わりに dispatch を下位コンポーネントに渡せるようになるため、
複数階層にまたがって更新を発生させるようなコンポーネントではパフォーマンスの最適化にもなります。引用にも書いてある通り、
<TaskAdd id={TaskId} dispatch={dispatch} TaskList={ToDoList}/>
dispatchをTaskAdd
に渡しています。2-1-4. reducerの定義
reducerの定義function reducer(state, action) { switch (action.type) { case 'add': return [ ...state, action.NewTask ]; case 'delete': let TaskIndex = 0; for (var i = 0; i < state.length; i++) { if (state[i].key.toString() === action.TaskId.toString()) { TaskIndex = i; } } return state.filter((_, index) => index !== TaskIndex); default: return state; } }reducerの定義を行います。
state
は状態管理したい変数、今回はTaskListが入っています。
action
はdispatch
で、この関数を実行するときに指定したパラメータが格納されています。
action.type
で、どの操作なのかを判定するのに使用しています。
add
やdelete
はそれぞれ、TaskListに新規にタスクを追加、指定したタスクを削除の処理を行なっています。2-2. TaskAdd.js
2-2-1. ソース全体
TaskAdd.jsimport React, { useState } from "react"; import Task from './Task' function TaskAdd(props) { const [NewTask, setTask] = useState('') const [TaskId, setTaskId] = useState(props.id) const [ErrorMessage, setErrorMessage] = useState('') function handleClick() { if (NewTask === '') { setErrorMessage('入力が空です。') return 0 } for (var i = 0; i < props.TaskList.length; i++) { if (props.TaskList[i].props.name === NewTask) { setErrorMessage('タスク名が重複しています。') return 0 } } props.dispatch({ type: 'add', NewTask: <Task key={TaskId} id={TaskId} name={NewTask} dispatch={props.dispatch} /> }) setTaskId(TaskId + 1) setTask('') setErrorMessage('') } return ( <section className='task-creator'> <h2>Task Add</h2> <input className='task-item-text' type="text" placeholder="Task" value={NewTask} onChange={(e) => setTask(e.target.value)} /> <button className='task-add-btn' type="button" onClick={handleClick}> Add </button> <p className='error-msg'>{ErrorMessage}</p> </section> ) } export default TaskAdd;2-2-2. stateの定義
stateの定義const [NewTask, setTask] = useState('') const [TaskId, setTaskId] = useState(props.id) const [ErrorMessage, setErrorMessage] = useState('')Task.jsと同じ様に定義しています。
const [状態管理したい変数, 状態を変更する関数] = useState(状態管理したい変数の初期値)
2-2-3. handleClick
TaskAdd.jsのhandleClickfunction handleClick() { if (NewTask === '') { setErrorMessage('入力が空です。') return 0 } for (var i = 0; i < props.TaskList.length; i++) { if (props.TaskList[i].props.name === NewTask) { setErrorMessage('タスク名が重複しています。') return 0 } } props.dispatch({ type: 'add', NewTask: <Task key={TaskId} id={TaskId} name={NewTask} dispatch={props.dispatch} /> }) setTaskId(TaskId + 1) setTask('') setErrorMessage('') }定義したstateにアクセスする際は
this.state.NewTask
の様に行なっていましたが
hookでは単純にNewTask
でアクセスできます。(this.stateの呪縛から開放される!!)stateの更新の際は
this.setState({ ErrorMessage: '入力が空です。' })
ではなく
state定義時の関数をそのまま使用できます。
つまりsetErrorMessage('入力が空です。')
と書けます。タスクの追加時にはTask.jsからpropsとして受け取ってあるdispatchを使用しています。
props.dispatch({ type: 'add', NewTask: <Task key={TaskId} id={TaskId} name={NewTask} dispatch={props.dispatch} /> })
type
とNewTask
はactionで取れる様に追加しています。2-2-4. render
TaskAdd.jsのrenderreturn ( <section className='task-creator'> <h2>Task Add</h2> <input className='task-item-text' type="text" placeholder="Task" value={NewTask} onChange={(e) => setTask(e.target.value)} /> <button className='task-add-btn' type="button" onClick={handleClick}> Add </button> <p className='error-msg'>{ErrorMessage}</p> </section> )
this.state
で取得することが無くなったので、少しスッキリしたと思います。2-3. Task.js
2-3-1. ソース全体
Task.jsimport React, { useState } from "react"; function Task(props) { const [isDone, setStatus] = useState(props.isDone) const task_id = 'task-id-' + props.id.toString() const css_label = 'task-item-label' const css_isDone = `${css_label} isDone` const css_Wip = `${css_label} WorkInProgress` return ( <li className='task-item-row'> <input id={task_id} className='task-item-checkbox' type='checkbox' onChange={() => (isDone === true) ? setStatus(false) : setStatus(true)}> </input> <label htmlFor={task_id} className={(isDone === true) ? css_isDone : css_Wip}> {props.name} </label> <i className="material-icons icon" onClick={() => props.dispatch({type:'delete', TaskId:props.id})}> delete </i> </li> ); } export default Task;2-3-2. stateの定義
stateと定数の定義const [isDone, setStatus] = useState(props.isDone) const task_id = 'task-id-' + props.id.toString() const css_label = 'task-item-label' const css_isDone = `${css_label} isDone` const css_Wip = `${css_label} WorkInProgress`stateとclassNameの定数を定義しているのみです。
2-3-3. render
Task.jsreturn ( <li className='task-item-row'> <input id={task_id} className='task-item-checkbox' type='checkbox' onChange={() => (isDone === true) ? setStatus(false) : setStatus(true)}> </input> <label htmlFor={task_id} className={(isDone === true) ? css_isDone : css_Wip}> {props.name} </label> <i className="material-icons icon" onClick={() => props.dispatch({type:'delete', TaskId:props.id})}> delete </i> </li> );
onChange={() => (isDone === true) ? setStatus(false) : setStatus(true)}
で単純なif文を省略しています。
onClick={() => props.dispatch({type:'delete', TaskId:props.id})}
でpropsで受け取ったdispatchを使用してタスクの削除を行なっています。2-4. TaskList.js
TaskList.jsimport React from "react"; function TaskList(props) { return ( <section className='task-list'> <h2>Task List</h2> <ul> {props.TaskList} </ul> </section> ); } export default TaskList;特にクラスと関数で違いは無いでしょうか、強いて言えばクラスで書くより短いくらいでしょうか??
3. まとめ
Reactのhookを使用して、クラスコンポーネントを関数コンポーネントのみで書き換えてみました。
スッキリ書き換えられるのはメリットに感じました。
ドキュメントを読む限りでは、完全に互換性がある訳では無いそうなので更に勉強が必要そうです。。。
最後まで見てくださり、ありがとうございます。
質問や、指摘は大歓迎ですので、よろしくお願いします。
- 投稿日:2020-01-12T17:10:50+09:00
JavaScriptでテーブルを自動で生成
以下のサンプルソースは、forループでテーブルを自動生成し、生成されたテーブルを指定した行数ずつ表示するページング機能を実装しています。
<!DOCTYPE html> <html lang="ja"> <head> <title>サンプル</title> <meta charset="utf-8"> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js"></script> <style> div#paging { text-align: center; /* スクロールに対してページングボックスの位置を固定 */ position: fixed; /* 位置を指定 */ bottom: 0; right:45%; } div#pagingbox{ background: #FFF; } th{ /* ヘッダ背景塗りつぶし */ background: #eee; } th,td { /* 枠線を1本線指定 */ border: solid 1px; width:auto; } table{ /* 枠線を1本線指定 */ border: solid 1px; border-collapse: collapse; white-space: nowrap; } footer{ position: fixed; width: 100%; background-color: #C0C0C0; padding: 15px 0; bottom: 0; /*下に固定*/ } </style> </head> <body> <!--テーブル生成位置--> <div id ='maintable'></div> <!--ページングボタン配置--> <footer> <div id="paging"> <table> <tr> <tb><button id="prevbtn" type="button"><</button></tb> <tb> <span id="currentpage">currentpage</span> / <span id="lastpage">lastpage</span> </tb> <tb><button id="nextbtn" type="button">></button></tb> </tr> </table> </div> </footer> <script> // table要素を生成 var table = document.createElement('table'); // tr部分のループ for (var i = 0; i < 700; i++) { // tr要素を生成 var tr = document.createElement('tr'); // th・td部分のループ for (var j = 0; j < 50; j++) { // 1行目のtr要素の時 if(i === 0) { // th要素を生成 var th = document.createElement('th'); // th要素内にテキストを追加 th.textContent = i + '-' + j; // th要素をtr要素の子要素に追加 tr.appendChild(th); } else { // td要素を生成 var td = document.createElement('td'); // td要素内にテキストを追加 td.textContent = i + '-' + j; // td要素をtr要素の子要素に追加 tr.appendChild(td); } } // tr要素をtable要素の子要素に追加 table.appendChild(tr); } // 生成したtable要素を追加する document.getElementById('maintable').appendChild(table); </script> <script>// ページング機能 jQuery(function($) { var page = 0; var displayrows = 30;// 1ページ当たり表示する行の数 function draw() {// ページの表示 $('#lastpage').html(Math.ceil(($('tr').size()-1)/displayrows)); $('#currentpage').html(page + 1); $('tr').hide(); $('tr:first,tr:gt(' + page * displayrows + '):lt(' + displayrows + ')').show();// 変数を使用する場合は' + + 'を忘れずに } $('#prevbtn').click(function() {// 1ページ後進 if (page > 0) { page--; draw(); } }); $('#nextbtn').click(function() {// 1ページ前進 if (page < ($('tr').size() - 1) /displayrows - 1) { page++; draw(); } }); draw();//初回表示 }); </script> </body> </html>
- 投稿日:2020-01-12T17:10:50+09:00
JavaScriptでテーブルを動的に生成
以下のサンプルソースは、forループでテーブルを自動生成し、生成されたテーブルを指定した行数ずつ表示するページング機能を実装しています。
<!DOCTYPE html> <html lang="ja"> <head> <title>サンプル</title> <meta charset="utf-8"> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js"></script> <style> div#paging { text-align: center; /* スクロールに対してページングボックスの位置を固定 */ position: fixed; /* 位置を指定 */ bottom: 0; right:45%; } div#pagingbox{ background: #FFF; } th{ /* ヘッダ背景塗りつぶし */ background: #eee; } th,td { /* 枠線を1本線指定 */ border: solid 1px; width:auto; } table{ /* 枠線を1本線指定 */ border: solid 1px; border-collapse: collapse; white-space: nowrap; } footer{ position: fixed; width: 100%; background-color: #C0C0C0; padding: 15px 0; bottom: 0; /*下に固定*/ } </style> </head> <body> <!--テーブル生成位置--> <div id ='maintable'></div> <!--ページングボタン配置--> <footer> <div id="paging"> <table> <tr> <tb><button id="prevbtn" type="button"><</button></tb> <tb> <span id="currentpage">currentpage</span> / <span id="lastpage">lastpage</span> </tb> <tb><button id="nextbtn" type="button">></button></tb> </tr> </table> </div> </footer> <script> // table要素を生成 var table = document.createElement('table'); // tr部分のループ for (var i = 0; i < 700; i++) { // tr要素を生成 var tr = document.createElement('tr'); // th・td部分のループ for (var j = 0; j < 50; j++) { // 1行目のtr要素の時 if(i === 0) { // th要素を生成 var th = document.createElement('th'); // th要素内にテキストを追加 th.textContent = i + '-' + j; // th要素をtr要素の子要素に追加 tr.appendChild(th); } else { // td要素を生成 var td = document.createElement('td'); // td要素内にテキストを追加 td.textContent = i + '-' + j; // td要素をtr要素の子要素に追加 tr.appendChild(td); } } // tr要素をtable要素の子要素に追加 table.appendChild(tr); } // 生成したtable要素を追加する document.getElementById('maintable').appendChild(table); </script> <script>// ページング機能 jQuery(function($) { var page = 0; var displayrows = 30;// 1ページ当たり表示する行の数 function draw() {// ページの表示 $('#lastpage').html(Math.ceil(($('tr').size()-1)/displayrows)); $('#currentpage').html(page + 1); $('tr').hide(); $('tr:first,tr:gt(' + page * displayrows + '):lt(' + displayrows + ')').show();// 変数を使用する場合は' + + 'を忘れずに } $('#prevbtn').click(function() {// 1ページ後進 if (page > 0) { page--; draw(); } }); $('#nextbtn').click(function() {// 1ページ前進 if (page < ($('tr').size() - 1) /displayrows - 1) { page++; draw(); } }); draw();//初回表示 }); </script> </body> </html>
- 投稿日:2020-01-12T16:45:42+09:00
JavaScript勉強の記録その7: 配列オブジェクトの作成・追加・削除
配列の作成方法
[]内にカンマ区切りでデータを入れることで配列を作ることができます。
コンソールに出力してみると、配列の情報が見れます。index.jsconst scores = [80, 90, 40]; console.log(scores); //(3) [80, 90, 40] //0: 80 //1: 90 //2: 40 //length: 3 // .....配列の要素へのアクセス
配列の各要素へはインデックス番号を指定してあげることによってアクセスすることができます。
index.jsconst scores = [80, 90, 40]; console.log(scores[0]); //80 console.log(scores[1]); //90 console.log(scores[2]); //40ループ処理を使い各要素へアクセス
ループ処理を使って、各要素を取り出すこともできます。
配列オブジェクトにはlengthというプロパティというのがありますので、配列名.lengthとすることで配列の長さを取得することができます。
以下の例では、lengthを利用してループ処理をし、各要素を取り出しています。index.jsconst scores = [80, 90, 40]; console.log(scores.length); //3 for (let i = 0; i < scores.length; i++) { console.log(`it is ${scores[i]}`); } //it is 80 //it is 90 //it is 40データの追加および削除
配列オブジェクトに用意されてあるpushメソッドとspliceメソッドを利用することによって要素の追加および削除ができます。
index.jsconst scores = [80, 90, 40]; scores.push(15,20); //配列の後ろから15と20を加える console.log(scores); //[80, 90, 40, 15, 20] scores.splice(1, 2); //配列の1番目から2個の要素を削除する console.log(scores); //[80, 15, 20] scores.splice(1, 1, 30, 70); //配列の1番目の要素から1つの要素を削除して、30と70を入れる console.log(scores); //[80, 30, 70, 20]
- 投稿日:2020-01-12T16:19:29+09:00
JSファイル net::ERR_ABORTED 404 (Not Found)と言われた時の対処方法
JSファイルが net::ERR_ABORTED 404 (Not Found)と言われた時の対処方法
Laravelでの開発でのスクリプトを、bladeへの直書きではなくて、外部JSファイルとして外出しした書き方をしている場合、コンソール画面にて、net::ERR_ABORTED 404 (Not Found)となりました。
原因:JSファイルの置き場所が間違っていた!!
JSファイルのbladeへの呼び出し方によって、ファイルの置き場所にお作法があるそうなんです。
JSファイルの置き場所の「お作法」
ケース1: JSファイルを作成して読み込む場合は、public/jsの下に配置
ケース2: JSファイルを作成し、Laravel Mixでコンパイルして読み込む場合は、resources/js/assets/の下に配置
ということでした。
反省
基礎からコツコツ学習している人にとっては、当然のことなんだと思うんですが、手当たり次第にLaravelを触っている身としては、こんな初歩的な事も知らなかったのです。お恥ずかしい・・・。
- 投稿日:2020-01-12T15:48:28+09:00
Indexed Database APIを使ってスタンドアロンな付箋アプリを作ってみる
記事の趣旨
せっかくIndexed Database APIについて勉強したので,それを使ってブラウザアプリを作ってみた.
1. 作ったアプリと技術要素
ブラウザにスティッキー(付箋紙)っぽいメモを残していけるツール.本当はカンバン方式の管理ツールを作りたかったんだけれど,力尽きたのでいったん保留.画面キャプチャは以下のとおり.
使用した技術要素は以下のとおり.Vue.jsは初めて触るので,書籍と睨めっこしながらの対応.
- HTML
- CSS
- Vue.js
- Indexed Database API(敢えてライブラリ未使用)
ソースコードはGithubのリポジトリに置いているのでご自由にどうぞ.動作は「Chrome 79.0.3945.117」で確認.Edgeでも動作するがレイアウトが若干崩れる.
2. 操作方法
直感で扱えるレベルだと思うけれど,とりあえず以下のとおり.
- cloneなりzip downloadなりでローカルに落としてきたら
kanban.html
を開く.- 付箋紙を新たに作りたいときは左下のプラスマークをクリック.
- 付箋紙はドラッグ&ドロップで自由に移動可能.
- 付箋紙をダブルクリックすると編集ウィンドウ(後述)が開く.
- 編集ウィンドウでURLを登録しておけば,付箋紙左下の「URL」ボタンクリックでその画面に新タブで遷移可能.
3. 苦戦したポイント
スキル低いので,初歩的だったり,間抜けなものばかりだが,恥を忍んで書いていく.
3.1 Promise
以下の処理は,「DBへのコネクションを確立する」->「チケットを全件取得する」->「グローバル変数にチケット情報を詰め込む」という意図のコードになる.このうち,
createConnection()
およびgetAllTickets()
は,Promise
オブジェクトを返却する関数である.先日の記事でも述べたとおり,Indexed Database APIは非同期処理であるため,Promise
を活用することで同期を図っている.created: function() { createConnection().then( getAllTickets() ).then( (allTickets) => { allTickets.forEach( ticket => { tickets.push({ id: ticket.id, text: ticket.text, url: ticket.url, categoryId: ticket.categoryId, left: ticket.left, top: ticket.top, zIndex: ticket.zIndex, deleted: false }) } ) } ).catch( (reason) => alert(reason) ) }さて,上記コードは一部が不正で,これだと同期処理にならない.どこかわかるだろうか.
答えは3行目.
getAllTicket()
と書いているが,ここはgetAllTicket
と,関数オブジェクトを指定しないといけない.
promise.prototype.then()
は,あくまでメソッドであり,その引数指定するのはコールバック関数.ものすごく間抜けなミスだが,うっかりif/elseの構文と同じような感覚で記述してしまい,上記のミスに至ってしまった.3.2 Bubbling
今回の付箋紙オブジェクトは,複数のレイヤで構成されているので,Bubblingを意識してイベントをハンドリングしないといけない.が,そもそもその辺りを全然理解しておらず,序盤はかなり意図しない挙動に苛まれた.たぶん今も多少バグは残っていると思われる.
Bubblingについては,こちらのページの説明がとてもわかりやすいと思う.定義を引用させていただくと以下のとおり.
要素上でイベントが起きると、最初にその上のハンドラが実行され、次にその親のハンドラが実行され、他の祖先に到達するまでそれらが行われます。
今回でいうと,例えば「URL」ボタンをクリックすると,続けて付箋紙自体にセットされたマウスダウンイベントが処理されることになる.後者は付箋紙の移動に関わる処理なので,付箋紙がワープするなどの謎挙動が頻発していた.
Bubblingを防ぎたい場合は,
event.stopPropagation()
を呼び出すことで,それ以降の親オブジェクトへの伝播を抑制することができる.4. ペンディングしている事項
- 付箋紙の色の変更機能
- categoryIdという死にパラメータがその名残
- ファイルサーバやローカルへのショートカットボタン
- エクスプローラの直接操作は無理そうなので,パスをクリップボードにコピーする処理を想定
- ゴミ箱アイコンへのドロップによる付箋紙削除処理
- 面倒臭くなって今は削除ボタンで代替
- カンバンボード化と,付箋紙へのステータス(ToDo,WIP,Done)導入
- そもそもそれを目指したアプリだったので
- 複数タブ対応
- 現在は複数タブでこのアプリを開いたら何が起きるか不明
- 投稿日:2020-01-12T15:08:58+09:00
何でもランキングにしてツイートできる「Cappps(キャップス)」を公開した【個人開発】
ランキングを作ってTwitterでツイートできるサービス「Cappps」を作ったので、公開してみました。
どうにか飽きずにリリースまで持っていけました。
今まで作っては飽き作っては飽きで、完成できなかったので、リリースできてよかったです。リリースしたもの
#Cappps
— Cappps -公式- (@Cappps_Official) January 11, 2020
#ランキングメーカー
ランキングメーカー「Cappps」でランキングを作ってつぶやいてみよう!
登録不要で、1分でランキングを作れます。
構想から完成まで7時間で作りました。https://t.co/R4tQ1eEWof
#プログラミング#webサービス#個人開発Cappps 何でもランキングにしてみよう!
ランキングを作ってツイートできるサービスです。
作ったランキングをシェアすることもできます。
#Cappps#ランキングメーカー
— いちいち (@ichiichi_1115) January 12, 2020
「好きな寿司ランキング」に高評価をしました。https://t.co/CO4oECH2me見た目
ランキング作成画面
作った理由
食べ物のおすすめランキングサイトを作っていました。
でも、いちいちHTML書くの嫌だからシステムにしちゃおう。
↓
いっそのことwebサービスにしちゃおう。っていう流れでできました。
どうでも良い機能
- 視聴回数
投稿者のモチベアップにつながるかな と思ってつけました。
- ジャンル分け
これは完全に暇だったからです。
セレクトボックスから選んでジャンルをつけられます。作りたいもの
お問い合わせ
流石に作ったほうが良いですね。
匿名のほうが問い合わせやすいですし。アクセスランキング
これも作りたいけど、インデックスしてないのでつらそうです。
使った言語
フロントエンド
- JavaScript
ライブラリ使ってない素のJSです。
あと対してJSは使ってないです。
ランキング表示部だけ、慣れてるJavaScriptで書きました。
- CSS
エラーメッセージがないときの消去を疑似要素:emptyでやりました。
サーバーサイド
- PHP
こちらも素のPHPです。
データベースがよくわからないので、テキストファイルに書き出して保存していました。一応ジャンル分けもできるようになってます。
感想
最低限の機能だけでも案外ものになるなと思いました。
なんならまだTwitterカードの動作が不安ですけれど...
投稿する というところが動いた時点でリリースしました。こんくらい雑でも動けばいいです。なんなら動かなくてもいいです。
今、アプリでもサービスでも完成して作り込んでいる方は、とりあえず最初のリリースをしてみてください。
あなたも、ユーザーも、WinWinになるでしょう。宣伝
好きなゲームでも、嫌いな授業でも、何でも良いです。
ぜひ、ランキングを作ってみてください。
Cappps
- 投稿日:2020-01-12T15:08:58+09:00
【個人開発Webサービス】何でもランキングにしてツイートできる「Cappps(キャップス)」をリリースしました
ランキングを作ってTwitterでツイートできるサービス「Cappps」を作ったので、公開してみました。
どうにか飽きずにリリースまで持っていけました。
今まで作っては飽き作っては飽きで、完成できなかったので、リリースできてよかったです。リリースしたもの
#Cappps
— Cappps -公式- (@Cappps_Official) January 11, 2020
#ランキングメーカー
ランキングメーカー「Cappps」でランキングを作ってつぶやいてみよう!
登録不要で、1分でランキングを作れます。
構想から完成まで7時間で作りました。https://t.co/R4tQ1eEWof
#プログラミング#webサービス#個人開発Cappps 何でもランキングにしてみよう!
ランキングを作ってツイートできるサービスです。
作ったランキングをシェアすることもできます。
#Cappps#ランキングメーカー
— いちいち (@ichiichi_1115) January 12, 2020
「好きな寿司ランキング」に高評価をしました。https://t.co/CO4oECH2me見た目
ランキング作成画面
作った理由
食べ物のおすすめランキングサイトを作っていました。
でも、いちいちHTML書くの嫌だからシステムにしちゃおう。
↓
いっそのことwebサービスにしちゃおう。っていう流れでできました。
どうでも良い機能
- 視聴回数
投稿者のモチベアップにつながるかな と思ってつけました。
- ジャンル分け
これは完全に暇だったからです。
セレクトボックスから選んでジャンルをつけられます。作りたいもの
お問い合わせ
流石に作ったほうが良いですね。
匿名のほうが問い合わせやすいですし。アクセスランキング
これも作りたいけど、インデックスしてないのでつらそうです。
使った言語
フロントエンド
- JavaScript
ライブラリ使ってない素のJSです。
あと対してJSは使ってないです。
ランキング表示部だけ、慣れてるJavaScriptで書きました。
- CSS
エラーメッセージがないときの消去を疑似要素:emptyでやりました。
サーバーサイド
- PHP
こちらも素のPHPです。
データベースがよくわからないので、テキストファイルに書き出して保存していました。一応ジャンル分けもできるようになってます。
感想
最低限の機能だけでも案外ものになるなと思いました。
なんならまだTwitterカードの動作が不安ですけれど...
投稿する というところが動いた時点でリリースしました。こんくらい雑でも動けばいいです。なんなら動かなくてもいいです。
今、アプリでもサービスでも完成して作り込んでいる方は、とりあえず最初のリリースをしてみてください。
あなたも、ユーザーも、WinWinになるでしょう。宣伝
好きなゲームでも、嫌いな授業でも、何でも良いです。
ぜひ、ランキングを作ってみてください。
Cappps下のようなランキングを作れます。
#Cappps#ランキングメーカー
— いちいち (@ichiichi_1115) January 12, 2020
ランキングメーカーCapppsを使って「嫌いな行事(学校)」っていうランキングを作ったよ。
見てみてね!!https://t.co/yzzaOwpDO9
- 投稿日:2020-01-12T15:08:58+09:00
【個人開発Webサービス】何でもランキングにしてツイートできる「Cappps(キャップス)」をリリース
ランキングを作ってTwitterでツイートできるサービス「Cappps」を作ったので、公開してみました。
どうにか飽きずにリリースまで持っていけました。
今まで作っては飽き作っては飽きで、完成できなかったので、リリースできてよかったです。リリースしたもの
#Cappps
— Cappps -公式- (@Cappps_Official) January 11, 2020
#ランキングメーカー
ランキングメーカー「Cappps」でランキングを作ってつぶやいてみよう!
登録不要で、1分でランキングを作れます。
構想から完成まで7時間で作りました。https://t.co/R4tQ1eEWof
#プログラミング#webサービス#個人開発Cappps 何でもランキングにしてみよう!
ランキングを作ってツイートできるサービスです。
作ったランキングをシェアすることもできます。
#Cappps#ランキングメーカー
— いちいち (@ichiichi_1115) January 12, 2020
「好きな寿司ランキング」に高評価をしました。https://t.co/CO4oECH2me見た目
ランキング作成画面
作った理由
食べ物のおすすめランキングサイトを作っていました。
でも、いちいちHTML書くの嫌だからシステムにしちゃおう。
↓
いっそのことwebサービスにしちゃおう。っていう流れでできました。
どうでも良い機能
- 視聴回数
投稿者のモチベアップにつながるかな と思ってつけました。
- ジャンル分け
これは完全に暇だったからです。
セレクトボックスから選んでジャンルをつけられます。作りたいもの
お問い合わせ
流石に作ったほうが良いですね。
匿名のほうが問い合わせやすいですし。アクセスランキング
これも作りたいけど、インデックスしてないのでつらそうです。
使った言語
フロントエンド
- JavaScript
ライブラリ使ってない素のJSです。
あと対してJSは使ってないです。
ランキング表示部だけ、慣れてるJavaScriptで書きました。
- CSS
エラーメッセージがないときの消去を疑似要素:emptyでやりました。
サーバーサイド
- PHP
こちらも素のPHPです。
データベースがよくわからないので、テキストファイルに書き出して保存していました。一応ジャンル分けもできるようになってます。
感想
最低限の機能だけでも案外ものになるなと思いました。
なんならまだTwitterカードの動作が不安ですけれど...
投稿する というところが動いた時点でリリースしました。こんくらい雑でも動けばいいです。なんなら動かなくてもいいです。
今、アプリでもサービスでも完成して作り込んでいる方は、とりあえず最初のリリースをしてみてください。
あなたも、ユーザーも、WinWinになるでしょう。宣伝
好きなゲームでも、嫌いな授業でも、何でも良いです。
ぜひ、ランキングを作ってみてください。
Cappps下のようなランキングを作れます。
#Cappps#ランキングメーカー
— いちいち (@ichiichi_1115) January 12, 2020
ランキングメーカーCapppsを使って「嫌いな行事(学校)」っていうランキングを作ったよ。
見てみてね!!https://t.co/yzzaOwpDO9
- 投稿日:2020-01-12T15:08:58+09:00
【個人開発サービス】たった6時間で作った、なんでもランキングにしたがるお前ら向けのサービス「Cappps(キャップス)」をリリースした
ランキングを作ってTwitterでツイートできるサービス「Cappps」を作ったので、公開してみました。
どうにか飽きずにリリースまで持っていけました。
今まで作っては飽き作っては飽きで、完成できなかったので、リリースできてよかったです。リリースしたもの
#Cappps
— Cappps -公式- (@Cappps_Official) January 11, 2020
#ランキングメーカー
ランキングメーカー「Cappps」でランキングを作ってつぶやいてみよう!
登録不要で、1分でランキングを作れます。
構想から完成まで7時間で作りました。https://t.co/R4tQ1eEWof
#プログラミング#webサービス#個人開発Cappps 何でもランキングにしてみよう!
ランキングを作ってツイートできるサービスです。
作ったランキングをシェアすることもできます。
#Cappps#ランキングメーカー
— いちいち (@ichiichi_1115) January 12, 2020
「好きな寿司ランキング」に高評価をしました。https://t.co/CO4oECH2me見た目
ランキング作成画面
作った理由
食べ物のおすすめランキングサイトを作っていました。
でも、いちいちHTML書くの嫌だからシステムにしちゃおう。
↓
いっそのことwebサービスにしちゃおう。っていう流れでできました。
どうでも良い機能
- 視聴回数
投稿者のモチベアップにつながるかな と思ってつけました。
- ジャンル分け
これは完全に暇だったからです。
セレクトボックスから選んでジャンルをつけられます。作りたいもの
お問い合わせ
流石に作ったほうが良いですね。
匿名のほうが問い合わせやすいですし。アクセスランキング
これも作りたいけど、インデックスしてないのでつらそうです。
使った言語
フロントエンド
- JavaScript
ライブラリ使ってない素のJSです。
あと対してJSは使ってないです。
ランキング表示部だけ、慣れてるJavaScriptで書きました。
- CSS
エラーメッセージがないときの消去を疑似要素:emptyでやりました。
サーバーサイド
- PHP
こちらも素のPHPです。
データベースがよくわからないので、テキストファイルに書き出して保存していました。一応ジャンル分けもできるようになってます。
感想
最低限の機能だけでも案外ものになるなと思いました。
なんならまだTwitterカードの動作が不安ですけれど...
投稿する というところが動いた時点でリリースしました。こんくらい雑でも動けばいいです。なんなら動かなくてもいいです。
今、アプリでもサービスでも完成して作り込んでいる方は、とりあえず最初のリリースをしてみてください。
あなたも、ユーザーも、WinWinになるでしょう。宣伝
好きなゲームでも、嫌いな授業でも、何でも良いです。
ぜひ、ランキングを作ってみてください。
Cappps下のようなランキングを作れます。
#Cappps#ランキングメーカー
— いちいち (@ichiichi_1115) January 12, 2020
ランキングメーカーCapppsを使って「嫌いな行事(学校)」っていうランキングを作ったよ。
見てみてね!!https://t.co/yzzaOwpDO9
- 投稿日:2020-01-12T15:03:18+09:00
【JavaScript】getElementByIdでnullが返ってきたときに確認すること
はじめに
凡ミスで長い時間つまずいたのでメモ。
あと役に立ちそうなサイトもメモ。jsのコードの場所を確認
外部ファイルを読み込むにしろ, htmlの中に書くにしろ, どこに書くかは割と重要ですね。
外部ファイルでつまずいたのでそちらの事例を書きます。<script src="menu.js"></script> <div id="menu"></div>var menu = document.getElementById('menu');この例だと
変数menu
はnull
になってしまいます。
理由は簡単ですが,<div id="menu"></div>
の前にjsファイルを読み込んでいるので,menu
というid属性なんて知らないわよ! どこにあるの!ってなってしまいます。
ってわけなので, 順番を入れ替えてあげればなおります。<div id="menu"></div> <script src="menu.js"></script>その他
他の事例はこちらのサイトが参考になるかも
- 投稿日:2020-01-12T14:48:41+09:00
jQuery の基礎
つい先日下記の2つの記事を書きました。
JavaScriptをHTMLファイルにどのように適用するかというものですね。『JavaScriptをWEBサイト(HTMLファイル)に適用する方法』
https://qiita.com/kibinag0/items/7843727e4328881ae964『JavaScript と DOM 応用編』
https://qiita.com/kibinag0/items/6e9b9169dcd261bd9ae1今回その続編のjQuery編です。
DOMで document.querySelector("h1") とかって記載していたんですけど、何とjQueryを使用すると、、、そのコードが $("h1") で済むみたいです。時短かよ。
ちなみに "$ = jQuery" という意味で jQuery("h1")としても大丈夫見たい。jQuery.js// 今までのDOM Selector document.querySelector("h1") // jQuery での Selector $("h1")しかもh1タグが複数ある場合は、document.querySelecterAll("h1")とかやらなきゃいけなかったのも、全て$("h1")で引っ張ってきてくれるようになっています。
なんと便利な。そんな便利なjQueryの基礎をメモっていきます。
今回も、AngelaさんのUdemyのコースです。→UdemyリンクまずはjQueryを使用できる環境を作る
jQuery公式サイト の中からGoogle Hosted Libraries を探し、最新のjQueryのscriptタグを探しに行きます。google developer
qiita.html// ①Google Developersから取得し、jQueryを読み込む <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> // ②jQuery 読み込んだ後に、javascriptシートを読み込む <script src="index.js" charset="utf-8"></script> // ちなみに場所は、bodyタグ内の一番最後 だから、こうなるはず </body>jQuery で CSS を編集・追加・消去
css("プロパティ","値") で編集できます。
直接編集・追加しちゃうやり方はこちら。
qiita.js// h1タグのcss編集・追加 $("h1").css("color","brown"); // 複数のcssを変更・追加 $("h1").css({"color":"brown","text-align":"left"}); // ちなみにプロパティだけを選択すると値を返してくれる console.log($("h1").css("color"));新たにクラスを追加・消去する場合
もしクラスを作って、そのクラスを適用させたい、もしくは、すでに適用されているクラスを外したいといった場合は、こんな感じ。
今回は h1タグに"oshan"というクラスを適用・外す場合。
qiita.js// oshan クラスを適用する $("h1").addClass("oshan"); // すでに適用されているoshanクラスを外す $("h1").removeClass("oshan"); // oshan と stylish 2つのクラスを適用したい場合 $("h1").addClass("oshan stylish");jQuery でテキストを変える
次にjQueryを使用してテキストを変更する2つの方法。
text()でテキストのみを変更する。
こちらはhelloだとしたら、その"hello"部分のみを変更しています。
qiita.js// button内のテキストを"OMG!!"変更する $("button").text("OMG!!");.htmlでそのタグ自体を変更する。
こちらはhelloのタグも含めた全ての部分を編集しています。だから、タグなんかも入れちゃえるのですね。
qiita.js// buttonのタグ自体を編集する $("button").html("<em>OMG!!</em>")jQueryで属性(attributes)を編集する
次に、jQueryで属性を編集する方法です。属性(attributes)ってなに?って方のために説明しておくと、属性とはaタグのhrefとか、imgタグのsrc, altのようなものです。
それを attr()で変更できます。
qiita.js// 属性とはタグの中のhrefやsrc, altなどのこと。 <a href=""> <img src="" alt=""> // aタグのsrc属性をgoogle.comに変更するよ $("a").attr("src","https://www.google.com/");jQuery で add Event Listener を実装する
以前のDOM, JavaScript応用編でクリックされた時やキーボードが押された時に何かしらの処理を実行させるaddEventListenerのjQuery版を見て行きます。
クリックされたときは click()で
qiita.js// h1がclickされた時に関数で色をpurpleに変更させる $("h1").click(function(){ $("h1").css("color","purple") });キーボード入力されたときは keypress()で
qiita.js// keyboard入力があった時に、h1の色をpurpleに変更する $("body").keypress(function(){ $("h1").css("color","purple") });まぁでも、on() で全部対応できるみたい
上記のようにclick()とか、keypress()とかでもevent別に対応できるけど、on()を使用すると、その全てに対応できるみたい。
on(イベント、適応する関数)
qiita.js// h1がclick された時にcssの関数を適用 $("h1").on("click", function(){ $("h1").css("color","red"); }); // mouseover された時にcssの関数を適用 $("h1").on("mouseover", function(){ $("h1").css("color","red"); });ただイベントを書き換えるだけ。。便利かよ。
ちなみに、clickとかkeypressとかmouseoverとかのイベント一覧↓
https://developer.mozilla.org/ja/docs/Web/EventsjQueryで直接要素をHTMLに追加する
今まではすでにあるHTML要素にCSSの変更を加えたり、HTML内のテキストを編集したりという処理でしたが、jQueryではHTMLファイルに要素自体を加えることも可能になっています。
qiita.js// beforeは、こうなる // <button>NEW</button> <h1>Hello World</h1> $("h1").before("<button>NEW</button>"); // afterは、こうなる // <h1>Hello World</h1> <button>NEW</button> $("h1").after("<button>NEW</button>"); // prependは、こうなる // <h1> <button>NEW</button> Hello World</h1> $("h1").prepend("<button>NEW</button>"); // appendは、こうなる // <h1>Hello World <button>NEW</button> </h1> $("h1").append("<button>NEW</button>");逆にHTMLファイルから要素を除去する場合はこんな感じ。
qiita.js// 逆に何か要素をHTMLファイルから消去するときは $("button").remove(); // ちなみに、これ button 要素が全部消え去りますのでご注意を 笑jQueryで簡単なアニメーションを作成する
指定した要素をhide(隠す)したり、show(表示)させたり、toggle(表示・隠す)したり、fadeOut(フェードアウト)したり、fadeIn(フェードイン)したり、fadeToggle(フェードイン・アウト)したり、slideUp(スライドイン)したり、slideDown(スライドアウト)したり、いろんなアニメーションを使用することができます。
qiita.js// button をclick したときに h1 を hide するとき $("button").on("click", function(){ $("h1").hide(); });ちなみに、上記の"hide"部分をshow, fadeIn, fadeOut, slideUp, slideDownなどに変えればそれぞれの処理を適用できます。
また、animate()を使用すると、cssなどを適用して、アニメーションを作ることができます。
下記は、アニメーションを使用して 透明性(opacity)を0.5にしています。animateの場合は、中に入れる値が数字でなければならないことに注意です。qiita.js$("button").on("click", function(){ $("h1").animate({opacity: 0.5}); });複数のアニメーションを順番に適用する
複数のアニメーションを適用することもできます。やってみると意外とかっこいい。
qiita.js// slideUpしてslideDownして、最後に透明性を0.5にしてます。 $("h1").slideUp().slideDown().animate({opacity: 0.5});以上でjQueryの基礎終わりです。
1つ1つを暗記するのではなく、「こんなのがあるのか」とざっくり覚えておくとググりながら使えるよ!ってAngelaさんが言ってました笑
確かに〜。
- 投稿日:2020-01-12T14:34:02+09:00
JavaScript勉強の記録その6: 色々な関数の定義方法
関数の定義
JavaScriptにはいくつか関数定義の方法があります。
関数宣言
一番ポピュラーなやり方として、関数宣言があります。
以下の例ではsumという関数を定義した後、sumの呼び出しを行なっております。
index.jsfunction sum(a, b, c) { return a + b + c; } console.log(sum(1,1,1)); //3関数式
変数や定数に代入するような形で関数を定義することもできます。
以下の例では定数であるsumに関数を代入し、定数sumに()を付けて実引数を渡しています。index.jsconst sum = function(a, b, c) { return a + b + c; } console.log(sum(1,1,1)); //3アロー関数
関数式をより短く書く方法も用意されてあり、それをアロー関数と呼びます。
書き方としては、functionと書く代わりに=>を{}と繋げてあげればOKです。index.jsconst sum = (a, b, c) => { return a + b + c; } console.log(sum(1,1,1)); //3もっと短くアロー関数
さらに短く書く方法も用意されてあり、処理結果を返すだけであれば{}とreturnを削ることもできます。
index.jsconst sum = (a, b, c) => a + b + c; console.log(sum(1,1,1)); //3もっともっと短くアロー関数
引数が1つだけの場合は()すらも省略できる書き方も用意されてあるようです。
以下の例ではコメントアウトされてある関数式と、コメントアウトされていない部分は同じ意味になります。index.js//const doble = function(a) { //return a * 2; //} const doble = a => a * 2; console.log(doble(2)); //4
- 投稿日:2020-01-12T13:20:09+09:00
JavaScript勉強の記録その5: breakとcontinueを使ったループ処理
breakとcontinueを使ったループ処理
breakやcontinueを利用して、ある条件の時にはループ処理を抜けたり、次のループ処理に入ったり等、制御をすることができます。
まず、以下の例では変数が3の倍数の時にコンソールに「こんにちは」と表示します。index.jsfor (let i = 1; i <= 10 ; i++) { if (i % 3 === 0) { console.log('こんにちは'); } console.log(i); } // 1 // 2 // こんにちは // 3 // 4 // 5 // こんにちは // 6 // 7 // 8 // こんにちは // 9 // 10上記の例では3の倍数の時には、「こんにちは」と「3の倍数」が出力されてありますが、「3の倍数」の時は、数字は表示せず、「こんにちは」だけ表示したいときもあるかと思います。
そのような時は、continueという命令を加えることで、制御を加えることができます。以下の例ではcontinueが呼ばれた時点で次のループ処理に入ることができますので一番下のconsole.log(i)は呼ばれません。
index.jsfor (let i = 1; i <= 10 ; i++) { if (i % 3 === 0) { console.log('こんにちは'); continue; } console.log(i); } // 1 // 2 // こんにちは // 4 // 5 // こんにちは // 7 // 8 // こんにちは // 10最後に、ある条件に当てはまった時点でループ処理を抜けたい、という時もあるかと思います。
そう言う時はbreakという命令を使えば、その時点でループ処理を抜けられます。index.jsfor (let i = 1; i <= 10 ; i++) { if (i % 3 === 0) { console.log('こんにちは'); break; } console.log(i); } // 1 // 2 // こんにちは