- 投稿日:2020-05-17T19:14:01+09:00
html要素のidにコロンを指定するのは注意!
そのidを検索する際の処理で躓く可能性があります、っていう話です。
はじめに
実績がない初心者なりに、アプリコンテストを目指したアプリの試作をしている今日此頃。
ところが、想定外していなかったミスで、数時間も食う事件が発生しました。
今回はその事件の原因について、自分への戒めも兼ねて記します。
事件背景
先述したアプリについて、ざっくりいうと、下のような構成です。
- 外部API
- フレームワーク
- vuejs
- レスポンシブデザイン向けライブラリ
- bootstrap
- 地図ライブラリ
- leafletjs
要は外部APIからデータを取得し、そのデータと地図を連携させ、更にそれをbootstrapで良い感じに見せたい!というもの。
事件は、bootstrapのcollapse(折りたたみ)を使っている時に起こりました。
事件詳細
詳細なデータを受け取り、それをアコーディオン表示をさせようと、サンプルをほぼそのまま参考に、カード部分をコンポーネントにしました。vuejsが分かる人向けのイメージは以下。
Detail.vue<template> <div id="detail"> <div class="accordion" :id="cardParent" > <Card v-for="data in jsonData" :key="data['@id']" :cardId="data['@id']" :title="data['title']" :parentId="cardParent" /> </div> </div> </template>Card.vue<template> <div class="card" > <div class="card-header" :id="this.headerId" > <button class="btn btn-link btn-block text-left collapsed" type="button" data-toggle="collapse" :data-target="'#'+this.bodyId" aria-expanded="false" :aria-controls="this.bodyId" > {{this.title}} </button> </div> <div :id="this.bodyId" class="collapse card-body" :data-parent="'#'+this.parentId" > <p>テスト</p> <!-- 外部APIから取得したデータの 詳細をここに記す --> </div> </div> </template> <script>カードをどれだけ作るかは、外部APIのデータやユーザーの操作次第。なので、カードやその内部要素のidは、コンポーネント内では決められません。
そこで、外部APIのデータのidを元に、カードや内部要素のidを決めることに。
idが
id00001
なら、カードはid00001
、内部要素はid00001_header
とするように、コードを記述しました。(vuejsが分かる人向けに言うと、Card.vueではcardIdをプロパティに受け取り、それに基づき、算出プロパティで内部要素用idをセットしています)
サンプルほぼそのままなので、これで問題はないはず!と動かした所、最初は問題なく表示されているように見えました。
でも、何かがおかしい。よく見ると、アコーディオン表示が開けない。
しかし、サンプルコードを見比べても、動作しているhtmlを見ても、何も問題がない。
加えて、
display : none
できちんとDOMは描画されている。一体、何だ!?何が原因なんだ!?!?!?
犯人
結局、上記のミスに気づくのに数時間を費やしてしまいました。
まだ扱いに慣れていないbootstrapばかりに気を取られて、原因は全く関係のない所にあったことに気づくのが遅かった為です。
実は、外部APIのデータのidをもとに、カードのidとしたのが、犯人でした。
外部APIからのjsonデータ(のid)に、記号、コロンなどが含まれていました。
このidを、直接カードのidに指定していたのが、全ての問題の始まり。
別に、htmlの要素のidに、コロンを含む記号は、問題なく指定できます。
シンプルなgetElementById("変なid名")も、特に問題なく動きます。
しかし。ライブラリによっては、id名を元にする検索などで、思うように動いてくれない場合があります。
今回は恐らく、bootstrapが依存するjQueryの問題ではないかと個人的に考えています。
(参考 : https://www.buildinsider.net/web/jqueryref/036 )
終わりに
教訓はこの2つ。以後気をつけたい。
- idに記号を使う場合は要注意
- ハイフンなど以外、入れないのが非常に好ましい
- 外部データの値をidに使う場合は要注意
- 意図せず記号が入ってしまう場合がある
余談
該当のコードは、v-forの要素数オプションも使って、下のようにid名を工夫した。
Detail.vue<template> <div id="detail"> <div class="accordion" :id="cardParent" > <Card v-for="(data,i) in jsonData" :key="data['@id']" :cardId="'card'+i" :title="data['title']" :parentId="cardParent" /> </div> </div> </template>
- 投稿日:2020-05-17T18:37:08+09:00
HTMLエスケープ文字列のデコード用関数にしっくりくるものが無かったので、自分で作ってみた
HTMLデコード
function decodeHTML(str) { return str.replace(/&(?:([a-z]+?)|#(\d+?));/g, function(m, c, d) { return c ? ({ "amp": "&", "lt": "<", "gt": ">", "quot": '"', "nbsp": " " }[c] || m) : d ? String.fromCharCode(d) : m; }); } decodeHTML("<a onclick="hoge('fuga')">") // <a onclick="hoge('fuga')">
- 投稿日:2020-05-17T18:09:57+09:00
Day15 #HTML/CSS汎用的小技
headerの固定・横一杯・常に上側に表示
.header { position: fixed; width: 100%; z-index: 1; }-positionプロパティ
|static |初期値。特に配置方法を指定しない。 |
|relative |相対位置への配置となる。|
|absolute |絶対位置への配置になる。absoluteを指定した親ボックスにstatic以外の値が指定されている場合に、その親ボックスの左上が基準位置になる。親ボックスに指定がない場合は、body要素が起点となる。|
|fixed |absoluteと同じく絶対位置への配置になるが、画面をスクロールした際も位置が固定されたままになる。|
|sticky position |relativeのように位置が指定され、fixedのように固定される。|-z-index
z軸方向(画面の手前・奥)の数値を指定するイメージ親要素内で子要素をど真ん中に置きたい時
htmlの親子関係<div class="side-header"> # ① <div class="side-box"></div> # ② </div>css子要素をど真ん中に.side-header{ display: flex; #中に入ってる要素を横並びに align-items: center; #中に入ってる要素を中央配置 } .side-box{ height: 20px; width: 260px; background-color: #ffffff; margin: 0 20px; #左右のmarginで中央配置 }
margin: 0 auto;
borderの設定
border色・太さ・種類→順不同.Edit-btn{ border: #eeeeee 1px solid; #この色で、color(font)も自動適用 font-size: 16px; padding: 15px 20px; }
- 投稿フォームの設定
formタグとinput<div class="form"> <form> <input class="input-text" type="text" placeholder="type a text"> <input class="send-btn" type="submit" name="commit" value="send"> </form> </div>
- 投稿日:2020-05-17T16:19:36+09:00
最低限知っておきたいBootstrapの使い方
はじめに
ここでは、Bootstrapの基本的な利用方法について解説します。
スターターテンプレート
Bootstrapには、色々なデザインが用意されていますが、その大元となる「スターターテンプレート」があります。
よく使うデザイン
Jumbotron
ウェブページのヘッダーのテンプレートとして使いやすいデザインです。
https://getbootstrap.jp/docs/4.4/components/jumbotron/Alerts
処理完了やエラーメッセージの表示に使えます。
alert-
のclass属性は、色の指定にも使えるので便利です。https://getbootstrap.jp/docs/4.4/components/alerts/
Buttons
他のページへのリンクやフォームの送信ボタンなどに使えます。
こちらもbtn-success
などのクラス属性は、色を変える時に使えます。https://getbootstrap.jp/docs/4.4/components/buttons/
Cards
ブログの記事一覧で個々の記事を表示したりするのに使えます。
https://getbootstrap.jp/docs/4.4/components/card/
Forms
新規登録や記事投稿などのフォームに使えます。
https://getbootstrap.jp/docs/4.4/components/forms/
Navbar
ヘッダー部分のメニューバーなどとして使えます。
https://getbootstrap.jp/docs/4.4/components/navbar/
Pagination
記事一覧ページなどのページネーションに使えます。
https://getbootstrap.jp/docs/4.4/components/pagination/
チートシート
Bootstrapのチートシートのページには、様々なデザインと、対応するコードがまとめられています。
- 投稿日:2020-05-17T15:10:29+09:00
vueで改行に合わせて伸縮するテキストフォーム
実装
textareaを利用することを想定。rowsの値を変更してformを伸び縮みさせている。
function fitToForm(target: HTMLFormElement, text: string, defaultRows = 1): void { const lines = text.match(/\n/g)?.length || 1; if (defaultRows <= lines) { target.rows = lines + 1; } if (defaultRows > lines) { target.rows = defaultRows; } }利用イメージ
v-model渡しているstateをfitToFormの第二引数に渡しています。
第三引数にtextareaのデフォルトのrowsを設定すると、それ以上小さくなりません。<textarea v-model="comment" placeholder="コメントを入力" rows="4" type="text" @input="fitToForm($event.target, state.comment, 4)" />完成gif
- 投稿日:2020-05-17T13:26:41+09:00
pugで構造とデータを分離して管理した
はじめに
データとHTMLの元になるpugファイルを分割して、いくつかの書き方を試してみました。環境設定さえ出来てしまえば、HTMLとjavasriptの知識があれば、pugのドキュメントにあるサンプルを眺めながら書けるので非常に楽です。
pugそのものの説明はしません。(公式ドキュメントはコードの例示のみで理解可能)テンプレートエンジンとしてのpugの話
pugは、HTMLをcoffeeやsassのようにインデントを使う簡易記法です。また、PHPのようにテンプレートエンジンとして利用することができます。さらに、データ部分はjavascriptで書きます。私にとっては、PHPよりjavascripのほうが馴染みがあるので、ありがたいです。
一点、テンプレートエンジンとしては、PHPと大きく違う点があります。それは、PHPはリクエストがあったタイミングで動的にHTMLを作成することを前提としている反面、pugは静的なHTMLを用意するためのツールだと言う点です。(原理的には動的なHTMLを作成することは可能ではないかと思います。)
しかし、ホームページやブログなど静的なwebページは多いので、pugをテンプレートエンジンとして利用するのはありかなと考えます。
何をしたかという話
今回、請求書を書くのに通常のアプリを使うのがなんとなくプログラマっぽくない。"怠惰"さに欠ける行為のような気がして、ブラウザからPDF出力する仕組みを作りました。請求書とセットで領収書も出力できるように、データを外だしして重複がない状態を作りました。
これを行うにあたり、gulpでpugがオートコンパイルされる様に環境設定しました。内容はこちらにまとめてあります。(バックエンドエンジニアがタグ書くのが面倒で組んだgulpfile晒します)
ソースコード
https://github.com/iwamoto-takaaki/invoice_sheet
請求書のイメージ
構成
pugファイルの構成
データ
json形式で表示用のデータを書きます。以下の2つのファイルがあります。
- 振り込み先や連絡先など固定的なもの
- 請求の中身に関する請求先・請求明細など個別のもの
テンプレート
通常のpugの記述の方法ですが、バインディング用の記法を使っています。
この2つは同じデータから作成されます。(それぞれでしか使わないデータもある)
- 請求書
- 領収書
構成
. ├── dist ここにHTMLが出力される │ └── css └── src ├── data データ(通常はここにデータを書く前提) ├── includes テンプレート ├── sample データのサンプル └── sass sassフォルダ(今回は解説しない)コード解説
データ部分
固定的な情報を
profile.pug
に記述しました。src/sample/profile.pug- var profile = { name: 'takaaki takaaki', adress: 'kumamoto city', email: 'iwamoto.takaaki@gmail.com', phone: '090-9999-9999', account: 'higo bank<br>9999999<br>takaaki iwamoto' }pugは
-
のあとインデントをつけると中をjavasriptとして記述します。コンパイル時に解釈され、コンパイル後には出力されません。account: 'higo bank<br>9999999<br>takaaki iwamoto'中にタグを書いてます。そのまま、だとエスケープ文字に変換されてしまいます。(よく出来ている)適用時にひと工夫をすると適用出来ます。
とはいえ、データ部分に表示に関わる部分を書くのは、あまり良くないと思います。そして支払先の情報は毎回変わるので、別ファイルとして保管します。
src/sample/sample.pug- var invoice = { issue: '2020/05/07', name: 'john smith', term: '2020/04', due: '2020/06/30', paid: '2020/06/18', details: [ {date: '2020/04/22', title: 'something (1)', price: 10000}, {date: '2020/04/23', title: 'something (2)', price: 500}, ] } invoice.total = invoice.details.map(x => x.price).reduce((acc, cur) => acc + cur)
term
は月次で請求する場合に、オプションで使います。
details
は請求明細で、配列が保持されています。
total
はdetails
の金額を合計しています。javascriptが問題なく動作します。というわけでもっと複雑なシステムも可能です。テンプレート部分
請求書
src/include/invoice.pugdoctype html html(lang="jp") head title="請求書" link(rel="stylesheet" type="text/css" href="./css/style.css") body header h2 請求書 .issue #{ invoice.issue } 作成 .container .my-profile .my-name= profile.name .my-address 住所: #{ profile.adress } .my-email e-mail: #{ profile.email } .my-phone TEL: #{ profile.phone } section.bill-to h2 お請求先 .container .your-name #{ invoice.name } 様 if invoice.term .billing-term #{ invoice.term } 分 section.details h2 請求明細 .container .details-row.details-header .details-date 作業日 .details-title 項目 .details-price 金額 for detail in invoice.details .details-row .details-date #{ detail.date } .details-title #{ detail.title } .details-price= '¥' + detail.price.toLocaleString() + '-' section.total h2 請求合計 .container .total-price= '¥' + invoice.total.toLocaleString() + '-' footer .container .billing-due 支払い期限: #{ invoice.due } .account-number !{ profile.account }バインド(ヒゲこと中括弧で書く)
javasriptの値を表示する方法は2つあります。
1つは、#{ value }
という形式で書く方法です。.my-phone TEL: #{ profile.phone }テンプレートエンジンっぽい書き方で、脳内での変換がしやすいです。
電話番号はこの様に変換されます。<div class="my-phone">TEL: 090-9999-9999</div>バインド(
=
でjavascriptで記述する)もう一つは、
= value
という書き方です。.my-name= profile.name括弧がいらないあたり、pugっぽい見た目です。多少のロジックが必要だったりした場合はこの書き方が良いかもしれません。
この場合はこの様に変換されます。<div class="my-name">takaaki takaaki</div>タグを含むバインド
振り込み先の部分はbrタグを書き込みました。
account: 'higo bank<br>9999999<br>takaaki iwamoto'タグなどは、通常のバインドではエスケープされて文字として表示しようとするので、この様に指定します。
.account-number !{ profile.account }するとエスケープされずに出力されます。
<div class="account-number">higo bank<br>9999999<br>takaaki iwamoto</div>
=
を使う書き方だと、こうなります。.account-number!= profile.account条件式(
if
)if invoice.term .billing-term #{ invoice.term } 分if文の条件がtrue(この場合は値があること)場合にインデント以下が表示されます。
インデントが一つ下がって、読みづらいので多用は避けたいです。
繰り返し(
for
)明細は配列の形で保持しているので繰り返しで出力する必要があります。
for detail in invoice.details .details-row .details-date #{ detail.date } .details-title #{ detail.title } .details-price= '¥' + detail.price.toLocaleString() + '-'いわゆる
each
で繰り返す方法です。これもインデントがややこしくなるのであまり複雑なのは書きたくないですね。
表示の変換はこのファイルで行うのが正解ですが、記述がややこしくなったら別ファイルにヘルパー関数を用意しても良いかもです。
あと全然関係無いのですが、明細行のcssは初めてグリッドを使いました。cssっぽさが無いけど便利です。もう、tableタグは使いたくないし、bootstrapのグリッドもいらないです。flexboxとは使い分けが必要かなと思います。
領収書
請求書のほとんど同じなので割愛します。
結合ファイル
上記のファイルを結合して出力します。
/src/sample_invoice.pug//- 請求・領収データ(json) include sample/sample.pug //- 私の情報(json) include sample/profile.pug //- 請求書の書式 include includes/invoice.pug単純に
include
で展開するだけです。
請求データをこのファイルに含めてしまえば、良いのですが、領収書でも使っていますので、別ファイルになります。引数でテンプレートにわたす形にできれば関係が見て取れるので可読性が
ちなみに、領収書を作成する場合はテンプレートを変更するだけです。
/src/sample_receipt.pug//- 請求・領収データ(json) include sample/sample.pug //- 私の情報 include sample/profile.pug //- 領収書の書式 include includes/receipt.pugコンパイル
私の場合はgulpで自動コンパイルしています。
詳細は以前の投稿を見て下さい。
(バックエンドエンジニアがタグ書くのが面倒で組んだgulpfile晒します)終わりに
感想
ものすごく長い文章になったけど、pugそのものが非常にわかりやすくどんどん改善できていったので書いていて何しろ気持ちが良いです。
クラスはセマンティックに出来るだけ1つだけにしたら、htmlぽさが無いほどスッキリ書けました。sass/scssを使うとクラスをいくつも足す必要がなくなるのでありがたいです。
書けるヒトだったらwordpressでブログやホームページを作るより、pugで組んだほうが幸せかもしれないとまで思えます。改善案メモ
- pugファイルの自動生成
- データを追加したら自動で請求書/領収書が作成される。
- データのフォルダから、生成されたHTMLのリンクを作成する。
brock
を使ってみたい。サブのメニューがあったりとかに便利そう。mixin
を使ってみたい。for
の中にif
があってとインデントが深くなったときに使いたい。- markdownを拡張で使えるみたい。
- 投稿日:2020-05-17T07:03:19+09:00
初心者によるプログラミング学習ログ 318日目
100日チャレンジの318日目
twitterの100日チャレンジ#タグ、#100DaysOfCode実施中です。
すでに100日超えましたが、継続。
100日チャレンジは、ぱぺまぺの中ではプログラミングに限らず継続学習のために使っています。
318日目は、おはようございます
— ぱぺまぺ@webエンジニアを目指したい社畜 (@yudapinokio) May 16, 2020
318日目 2h
・架空LP模写9日目2h(@ririru_123)
・架空LP模写:スキル部分を作成#早起きチャレンジ#駆け出しエンジニアと繋がりたい#100DaysOfCode
- 投稿日:2020-05-17T06:00:14+09:00
CSSの子孫セレクタを使うなら子セレクタの方が良いかもしれない理由
はじめに
設計なので、これが正解!というものはないです。
時と場合によります。
今回のケースは、パーツを作っていて、スタイルを当てたい時だと考えています。
子孫セレクタとは ・・・
.class .class
のことで、子や孫以降全て
子セレクタ ・・・.class > .class
のことで、直接子要素のみ
結論
- 影響範囲を小さく出来るから
- 影響しているスタイルを打ち消さなくても良くなるから
設計で重要なことは予測しやすいこと
CSSの設計で重要なことに予測しやすいというものがあります。
それは他のスタイルが影響せずに、自分が書いたとおりに動くとされていることです。解説
HTML用意します
<div class="sidebar"> <h1 class="text">サイドバー</h1> <ul class="lists"> <li class="list"><a class="text">リスト1</a></li> <li class="list"><a class="text">リスト2</a></li> <li class="list"><a class="text">リスト3</a></li> </ul> </div>サイドバーという文字を大きくしたいな〜って思った時に、
子孫セレクタで書くと、
.sidebar .text { font-size: 40px; }予期していないところまで文字が大きくなってしまいます。
子セレクタで書くと
.sidebar > .text { font-size: 40px; }良い感じ?
個人的には、全てclass命名して書いていきたい
理由は
-.class > .class
は.class
より詳細度が高くなり、スタイルが効かないことがある
- HTML側がdivなどでwrapされるという変更が起きた時に、スタイルも編集しなくてはならない(HTMLに依存し、変更に弱い)
- 子孫セレクタ使うんだったら、影響範囲が小さい子セレクタを使いたい
- 子セレクタ使うと、上記で述べた理由がある
ので、class付与してやりたいですね...
じゃあ、CSSの名前被るんだけど!?って話になるんですが、BEMなどの名前空間っぽいやつがあります。そもそもBEMだったら、名前が被る事なんて少ないですね。もっと言うなら、Shadow DOMやCSS in JS、CSSmodulesとかあるんですが、その話になるとずれていきます。もし、コンポーネント単位で作っていても、その中でスタイルの競合等があれば、困りますよね。
- 投稿日:2020-05-17T04:38:49+09:00
phpについて(初心者)その1
はじめに
こちらではphpについて問合せがきた内容について書いていきたいと思います。
(適切な題名が思い浮かばなかったためその1といたします)
はじめはどう書いたらいいのかわからなく、初歩的な内容だとそもそもググっても出てこず、挫折することもあると思います。
こちらではその内容を解決する手助けができればと思います。コードは問合せ者からいただいたものを部分的に抽出し、書き換えたものです。
自分のコードと置き換えつつ解決できればとおもいます。
またこれは1つの案にすぎません。
こんなやり方もあるんだなと参考程度になればと思います。
また、もっとスマートな方法があるとは思いますが、ご容赦いただければと思います。環境
CentOS
Apache
Vagrant
※こちらは筆者の環境であり問合せ者の環境ではありません内容
問合せ:htmlから取得した内容をフィールドに反映したいが一つの
フィールドに複数の要素を反映したい。いただいたコード(実際はもっとながいですがこん今回は一部抜粋)
index.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>index</title> <link rel="stylesheet" href="css/style.css"> </head> <body> <header> <h1>インプット</h1> <header> <form action="test.php" method="post"> <table border="3"> <tbody> <tr> <td align="left" valign="top" class="title">name</td> <td><input type="text" name="comment"></td> </tr> <tr> <td align="left" valign="top" class="title">checkbox</td> <td> <input name="checkbox" type="hidden" value=""> <input type="checkbox" name="checkbox[]" value="checkbox1">checkbox1</input> <br> <input type="checkbox" name="checkbox[]" value="checkbox2">checkbox2</input> </td> </tr> </tbody> </table> <input type="submit" value="送信" class="button"> </form> </body> </html>test.php<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="css/style.css"> </head> <body> <header> <h1>確認画面</h1> </header> <table border="2"> <?php $Array = $_POST; foreach ($Array as $key => $value) { if ($key == 'comment') { echo "<tr>"; echo "<td class='title'>" . $key . "</td>"; echo "<td>" . $value . "</td>"; echo "</tr>"; } if ($key == 'checkbox') { echo "<tr>"; echo "<td class='title'>".$key."</td>"; for ($i = 0; $i < count($value);++$i) { echo "<td>".$value[$i]."</td>"; } } } ?> </table> </body> </html>画面1
遷移後画面
フィールドが新しく作られるようになっています。
なのでコードの内容を噛み砕いていきたいお思います。test.php<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="css/style.css"> </head> <body> <header> <h1>確認画面</h1> </header> <table border="2"> <?php #入力内容の取得 $Array = $_POST; foreach ($Array as $key => $value) { #コメントレコードの作成 if ($key == 'comment') { echo "<tr>"; echo "<td class='title'>" . $key . "</td>"; echo "<td>" . $value . "</td>"; echo "</tr>"; } #チェックボックスレコードの作成 if ($key == 'checkbox') { echo "<tr>"; #kayフィールドの作成 echo "<td class='title'>".$key."</td>"; #valueフィールドの作成 echo "<td>"/*ここに書く*/"</td>"; } } } ?> </table> </body> </html>test.php<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="css/style.css"> </head> <body> <header> <h1>確認画面</h1> </header> <table border="2"> <?php $Array = $_POST; foreach ($Array as $key => $value) { if ($key == 'comment') { echo "<tr>"; echo "<td class='title'>" . $key . "</td>"; echo "<td>" . $value . "</td>"; echo "</tr>"; } if ($key == 'checkbox') { echo "<tr>"; echo "<td class='title'>".$key."</td>"; echo "<td>"; #for文の配置変更 for ($i = 0; $i < count($value);++$i) { #ここから加えたコード if($i==0){ echo $value[$i]; }else{ echo ",".$value[$i]; } #ここまで } echo "</td>"; } } ?> </table> </body> </html>まとめ
いかがだったでしょうか、なるべく変更を加えることなく期待結果になるようにしてみました。
解説が下手でわかりにくかもしれませんが少しでも助けになればと思います。