- 投稿日:2019-11-24T23:22:37+09:00
Ignite UI for Angular のテーマについて
おはこんばんちは
私が「Ignite UI for Angular のテーマ」について、ある程度理解するまでに1週間くらいかかってしまったので、これから学ばれる方が効率的に学習できるようにまとめたいと思います。
項目
- テーマとは
- グローバルテーマとコンポーネントテーマ
- グローバルテーマの設定方法
- コンポーネントテーマの設定方法
- 両方を使用した例
- 参照
テーマとは
- 最小限のコードで、コンポーネントのテーマを変更できるAPIです。(主に色)
※コンポーネントとは、ページを構成するUI部品のことです。グラフ、グリッド、ボタンなどのことで、入れ子に配置することができる。
グローバルテーマとコンポーネントテーマ
グローバルテーマ
カラーパレットを使用して、全てのコンポーネントにスタイルを設定できる。
設定方法
hogehoge.scss//↓テーマを使うときに、必要↓ @import '~igniteui-angular/lib/core/styles/themes/index'; //グローバルテーマの引数を作成 //主にパレットやグローバルテーマに含めないテーマのリストを作成 //↓パレットの一部を設定した例 $app-color-palette: igx-palette( $primary: red, $secondary: yellow ); //↓テーマを使う前に、必ず宣言するもの(お約束)引数なし↓ @include igx-core(); //↓グローバルテーマを設定↓ @include igx-theme($app-color-palette);
- グローバルテーマの引数
名前 型 デフォルト 説明 $palette map undefined メインの色やサブの色など一定パターンに沿ったカラーセット。詳しくはこちら $schema map $light-schema テーマで使用する全てのプロパティのリスト $exclude list ( ) グローバルテーマに含めない、コンポーネントテーマのリスト $legacy-support boolean true テーマの設定方法を決定 falseに設定した場合はCSS変数での設定 コンポーネントテーマ
- 個別にテーマのスタイル設定ができる。
グローバルに定義する。
設定方法(ボタンテーマの場合)
hogehoge.scss//↓テーマを使うときに、必要↓ @import '~igniteui-angular/lib/core/styles/themes/index'; //ボタンテーマの引数の設定 //ボタンの色を設定 //↓ボタンの一部を設定した例 $app-button-theme: igx-button-theme ( $raised-focus-text-color: green, $raised-border-radius: 2px ); //↓テーマを使う前に、必ず宣言するもの(お約束)引数なし↓ @include igx-core(); //↓グローバルテーマを設定↓ @include igx-button($app-button-theme);
- コンポーネントテーマの全量とパラメータ(今後追加)
両方を使用した例
hogehoge.scss//↓テーマを使うときに、必要↓ @import '~igniteui-angular/lib/core/styles/themes/index'; $app-color-palette: igx-palette( $primary: red, $secondary: yellow ); $my-card-theme: igx-card-theme( $background: red, $content-text-color: blue ); $app-button-theme: igx-button-theme( $raised-focus-text-color: green, $raised-border-radius: 2px ); $exclude-list:(igx-button,igx-card); //↓テーマを使う前に、必ず宣言するもの(お約束)引数なし↓ @include igx-core(); //↓グローバルテーマを設定↓ @include igx-button($app-button-theme); @include igx-card($app-card-theme); @include igx-theme($app-color-palette, $exclude: $exclude-list);TODO
- 今後追加の追加
- 実はコンポーネントテーマはCSSのようにクラス単位でスコープを設定できるので、その詳細
参照
テーマ
https://jp.infragistics.com/products/ignite-ui-angular/angular/components/themes/index.htmlグローバル テーマ
https://jp.infragistics.com/products/ignite-ui-angular/angular/components/themes/global-theme.htmlコンポーネント テーマ
https://jp.infragistics.com/products/ignite-ui-angular/angular/components/themes/component-themes.html
- 投稿日:2019-11-24T22:53:28+09:00
初心者です。
書籍にあるcss,javascriptのサンプルコードを書いてみたのですが反映されません・・・
どうすればよいのでしょうか??
- 投稿日:2019-11-24T22:44:44+09:00
年末まで毎日webサイトを作り続ける大学生 〜37日目 cssでパララックス風サイトを作る〜
はじめに
こんにちは!@70days_jsです。
cssで簡単にパララックス風サイトを再現してみました。
今日は37日目。(2019/11/24)
よろしくお願いします。サイトURL
https://sin2cos21.github.io/day37.htmlやったこと
cssで簡単にパララックス風サイトを作れるという記事をみたので早速やってみました。
とても手軽に実装できます。htmlもcssもシンプルです。
htmlは以下のように、ページ単位でdivに分けます。(one-pageクラスがついてるdiv)<body> <div class="one-page page-zero"> <p>桃太郎</p> </div> <div class="one-page page-one"> <p>むかしむかし、あるところに</p> </div> <div class="one-page page-two"> <p>お爺さんとお婆さんがいました。</p> </div> <div class="one-page page-three"> <p>お爺さんは山へ芝刈りへ。</p> <p>お婆さんは川へ洗濯へ。</p> </div> <div class="one-page page-four"> <p>すると、お婆さん。</p> <p>洗濯をしていると川から桃が流れてきました。</p> </div> <div class="one-page page-five"> <p>私、桃はあんまり好きじゃないなぁ。</p> <p>お婆さんは桃を無視しました。</p> </div> <div class="one-page page-six"> <p>二人はその後も幸せに暮らしましたとさ。</p> <p>おしまい。</p> </div> </body>cssの重要な部分 ↓
.one-page { width: 100vw; height: 100vh; position: sticky; top: 0; }これで.one-pageクラスのついた部分は縦横いっぱいなので1ページみたいに見えます。
そしてpositionがstickyなので、スクロールしてtopの位置が0になった時にfixedとなり、パララックスのように見えるというわけです。
そのあとは各ページごと(divごと)にz-indexをつけて重なる順序を指定して、他のスタイルを調整して終わりです。↓
.page-zero { z-index: 0; background-color: rgba(150, 100, 50, 1); } .page-one { z-index: 1; background-color: rgba(50, 0, 0, 1); } ...感想
以前javascriptでパララックスサイトを作ろうとして失敗した経験があったので、こんなに簡単に実装できて驚いています。
パララックスにも色々タイプがあるみたいなので、他のタイプのも年末までにやってみたいですね。最後までお読みいただきありがとうございます。明日も投稿しますのでよろしくお願い致します。
参考
- たった数行のCSSでパララックス風表現ができる「position: sticky」の使い方 - WPJ (https://www.webprofessional.jp/js-library-scrollreveal-2/)
とても分かりやすかったです。ありがとうございます!
- 投稿日:2019-11-24T22:26:12+09:00
ハマった!!!FontAwesomeがバージョン5からCDN配布を止めていた!
公式サイトでCDNを探しても見当たらない!
いつの間にか、バージョン5からCDN配布を止めており、メアドを入力してファイルを取得する必要があった!
Javascriptファイルを読み込んで動的にスタイルシートを展開する方針に変わっていた!
<!doctype html> <html> <head> <!-- Place your kit's code here --> <script src="https://kit.fontawesome.com/XXXXXXXXXX.js" crossorigin="anonymous"></script> </head> <body> <!-- Ready to use Font Awesome. Activate interlock. Dynotherms - connected. Infracells - up. Icons are go! --> </body> </html>ちなみに・・・公式サイトでダウンロードできるファイルはバージョン5に対応していない!
ダウンロードしたファイルからscssだけを取り出してコンパイルして使用しましたが、
□
が表示されて使用できなかった!
バージョン5からは@font-face
が使用されているが、ダウンロードファイルは使用されていなかった!登録は無料!
恐る恐るメアドを入力して登録してみましたが、登録自体は無料でした!
ログイン後は管理画面に表示され、有料版にアップグレードすることができる形式になっていた!
- 投稿日:2019-11-24T22:06:35+09:00
フォームのCSS
フォームは特にブラウザごとの表示の差異が大きいので、普段あまりCSSを使わない人向けにベース用のCSSを書いてみました。
CSS適用前後の比較
before
after
CSS適用後画像(HTMLもデザイン変更に必要な部分は一部変更しています)
共通リセット
cssinput, button, textarea, select { /* デフォルトスタイルをリセット */ -webkit-appearance: none; -moz-appearance: none; appearance: none; /* font-familyを継承しないので、継承させる */ font-family: inherit; /* iOSの角丸をリセット */ border-radius: 0; /* box-size */ box-sizing: border-box; /* 文字の大きさ iOSでズームさせないために16px以上を指定 */ font-size: 16px; /* 文字色を親から継承 */ color: inherit; } label { /* iOSでのlabelとinput,select,textareaの関連付け */ cursor: pointer; } /* スピンボタン非表示 chrome,safari */ input[type="number"]::-webkit-outer-spin-button, input[type="number"]::-webkit-inner-spin-button, input[type="month"]::-webkit-outer-spin-button, input[type="month"]::-webkit-inner-spin-button, input[type="datetime-local"]::-webkit-outer-spin-button, input[type="datetime-local"]::-webkit-inner-spin-button, input[type="week"]::-webkit-outer-spin-button, input[type="week"]::-webkit-inner-spin-button, input[type="time"]::-webkit-outer-spin-button, input[type="time"]::-webkit-inner-spin-button, input[type="date"]::-webkit-outer-spin-button, input[type="date"]::-webkit-inner-spin-button { /*-webkit-appearance: none; margin: 0;*/ display: none; } /* スピンボタン非表示(firefox) */ input[type="number"], input[type="month"], input[type="datetime-local"], input[type="week"], input[type="time"], input[type="date"] { -moz-appearance: textfield; } /* クリアボタン非表示 */ input[type="date"]::-webkit-clear-button, input[type="month"]::-webkit-clear-button, input[type="datetime-local"]::-webkit-clear-button, input[type="time"]::-webkit-clear-button, input[type="week"]::-webkit-clear-button { -webkit-appearance: none; }input
input共通CSS
css/* input */ input { /* 背景色(任意の色を指定) */ background-color: #eee; /* inputの枠線を消す */ border: 1px solid transparent; transition: border 0.2s ease-out; /* 文字色を親から継承 */ color: inherit; /* 任意の高さ */ height: 46px; /*inputのフォーカス時の枠線を消す*/ outline: 0; } /* inputにフォーカスが当たっている時 */ input:focus { border-bottom: 1px solid#e74c3c; }input(日時系以外)
html<div> <label for="form__input--text">テキスト(type="text")</label> <input type="text" name="text" id="form__input--text"> </div> <div> <label for="form__input--email">メールアドレス(type="email")</label> <input type="email" name="email" id="form__input--email"> </div> <div> <label for="form__input--password">パスワード(type="password")</label> <input type="password" name="password" id="form__input--password"> </div> <div> <label for="form__input--url">URL(type="url")</label> <input type="url" name="url" id="form__input--url"> </div> <div> <label for="form__input--tel">電話番号(type="telephone")</label> <input type="tel" name="tel" id="form__input--tel"> </div> <div> <label for="form__input--search">検索ワード(type="search")</label> <input type="search" name="search" id="form__input--search"> </div> <div> <label for="form__input--number">数(type="number")</label> <input type="number" name="number" id="form__input--number"> </div>css/* サーチキャンセルボタンのデザイン変更 */ input[type="search"]::-webkit-search-cancel-button { -webkit-appearance: none; height: 16px; width: 16px; background-image: url(cancel.svg); background-repeat: no-repeat; background-size: cover; /* サーチキャンセルボタンを非表示にする場合 */ /*-webkit-appearance: none;*/ } input[type="search"]::-webkit-search-decoration { display: none; } input[type="search"]:focus { outline-offset: -2px; }input(日時系)
html<div> <label for="form__input--date">日付(type="date")</label> <input type="date" name="date" id="form__input--date"> </div> <div> <label for="form__input--datetime-local">日時(type="datetime-local")</label> <input type="datetime-local" name="datetime-local" id="form__input--datetime-local"> </div> <div> <label for="form__input--month">月(type="month")</label> <input type="month" name="month" id="form__input--month"> </div> <div> <label for="form__input--week">週(type="week")</label> <input type="week" name="week" id="form__input--week"> </div> <div> <label for="form__input--time">時(type="time")</label> <input type="time" name="time" id="form__input--time"> </div>css/* カレンダー表示ボタン アイコン変更 */ input[type="date"]::-webkit-calendar-picker-indicator, input[type="datetime-local"]::-webkit-calendar-picker-indicator, input[type="month"]::-webkit-calendar-picker-indicator, input[type="week"]::-webkit-calendar-picker-indicator { color: transparent; cursor: pointer; background: #eee url(calendar.svg) no-repeat center right 0px/16px 16px; background-size: 16px; }range
html<div> <label for="form__input--range">範囲(type="range")</label> <input type="range" name="range" id="form__input--range"> </div>cssinput[type=range] { -webkit-appearance: none; /* height: 46px; */ } /* ツールチップ 非表示(ms) */ input[type=range]::-ms-tooltip { display: none; } /* ツマミ(webkit) */ input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; background: #fff; border-radius: 50%; box-shadow: 0 0 4px 0 #333; cursor: pointer; height: 20px; width: 20px; margin-top: -10px; /* iOS safari */ border: none; } /* ツマミ(mozilla) */ input[type=range]::-moz-range-thumb { background-color: #fff; border: none; border-radius: 50%; box-shadow: 0 0 4px 0 #333; cursor: pointer; height: 20px; width: 20px; } /* ツマミ(ms) */ input[type=range]::-ms-thumb { background-color: #fff; border: none; border-radius: 50%; box-shadow: 0 0 4px 0 #333; cursor: pointer; height: 20px; width: 20px; margin: 0 1px; } /* ツマミ(ms) chomeツマミ調整の影響を消す */ @supports (-ms-ime-align: auto) { input[type=range]::-webkit-slider-thumb { margin-top: 0px !important; } } /* 溝(mozilla) */ input[type=range]::-moz-range-track { height: 0; border: 1px solid #333; } /* 溝(WebKit) */ input[type=range]::-webkit-slider-runnable-track { width: 100%; height: 0; cursor: pointer; background: #cccccc; border: 1px solid #333; margin-top: 0px; } /* 溝(ms) */ input[type=range]::-ms-track { width: 100%; height: 12px; cursor: pointer; background: transparent; border-color: transparent; border-width: 16px 0; color: transparent; } /* 溝の位置調整(ie) */ @media all and (-ms-high-contrast: none) { input[type=range]::-ms-track { margin-top: -5px !important; } } /* 溝の色(ms)(つまみより左側) */ input[type=range]::-ms-fill-lower { background: #cccccc; border: 0.2px solid #010101; border-radius: 2.6px; box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; } /* 溝の色(ms)(つまみより右側) */ input[type=range]::-ms-fill-upper { background: #cccccc; border: 0.2px solid #010101; border-radius: 2.6px; box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; } /* 溝の色(ms)(つまみより左側)フォーカス時 */ input[type=range]:focus::-ms-fill-lower { background: #999999; } /* 溝の色(ms)(つまみより右側)フォーカス時 */ input[type=range]:focus::-ms-fill-upper { background: #999999; }color
html<div> <label for="form__input--color">色(type="color")</label> <input type="color" name="color" id="form__input--color"> </div>css/* なし(共通リセットのみ) */file
html<div> <p>ファイル(type="file")</p> <input type="file" name="file" id="form__input--file"> <label for="form__input--file">ファイルを選択</label> </div>cssinput[type="file"] { display: none; } input[type="file"]+label { background: #2980b9; border-bottom: 4px solid #434343; border-radius: 4px; color: #FFF; display: table; font-weight: bold; line-height: 1; margin: 10px auto 0; padding: 20px 40px; transition: background-color 0.2s ease-out, transform 0.2s ease-out, border-bottom 0.2s ease-out; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } input[type="file"]+label:hover { background: #3498db; } input[type="file"]+label:active { background: #34495e; border-bottom: 4px solid transparent; transform: translateY(4px); }radio
html<p>性別(type="radio")</p> <div class="radiobuttonMember"> <input type="radio" name="sex" value="male" id="form__input--radio1"> <label for="form__input--radio1"> <svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 64 64"> <path d="M21.184 54.944c-9.963-.554-17.871-8.793-17.871-18.88 0-10.442 8.479-18.909 18.938-18.909s18.938 8.466 18.938 18.909-8.479 18.91-18.938 18.91" /> </svg>男 </label> </div> <div class="radiobuttonMember"> <input type="radio" name="sex" value="female" id="form__input--radio2"> <label for="form__input--radio2"> <svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 64 64"> <path d="M21.184 54.944c-9.963-.554-17.871-8.793-17.871-18.88 0-10.442 8.479-18.909 18.938-18.909s18.938 8.466 18.938 18.909-8.479 18.91-18.938 18.91" /> </svg>女</label> </div>css.radiobuttonMember { margin-top: 10px; } /* デフォルトのinputを隠す */ input[type="radio"] { display: none; } /* ラベルの前の四角形を作る */ input[type="radio"]+label::before { content: ""; position: absolute; border: 2px solid #343434; width: 16px; height: 16px; margin-top: 4px; } /* SVGの位置と大きさの指定 */ input[type="radio"]+label>svg { width: 20px; height: 20px; position: relative; margin-right: 5px; left: 3px; top: 3px; } /* チェックマークの設定(未チェック時) */ input[type="radio"]+label>svg>path { fill: none; stroke: #27ae60; stroke-linejoin: round; stroke-linecap: round; stroke-dasharray: 130; stroke-dashoffset: 100; stroke-opacity: 0; stroke-width: 8px; transition: stroke-dashoffset 0.2s ease-out; } /* チェックマークの設定(チェック時) */ input[type="radio"]:checked+label>svg>path { stroke-dashoffset: 0; stroke-opacity: 1; }checkbox
html<div> <p>趣味</p> <div class="checkboxMember"> <input type="checkbox" name="hobby" value="listenmusic" id="form__input--checkbox1"> <label for="form__input--checkbox1"> <svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 64 64"> <path d="M18 4l-6 42s66-68 74-40" /> </svg>音楽鑑賞 </label> </div> <div class="checkboxMember"> <input type="checkbox" name="hobby" value="readbook" id="form__input--checkbox2"> <label for="form__input--checkbox2"> <svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 64 64"> <path d="M18 4l-6 42s66-68 74-40" /> </svg>読書 </label> </div> <div class="checkboxMember"> <input type="checkbox" name="hobby" value="watchmovie" id="form__input--checkbox3"> <label for="form__input--checkbox3"> <svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 64 64"> <path d="M18 4l-6 42s66-68 74-40" /> </svg>映画鑑賞 </label> </div> <div class="checkboxMember"> <input type="checkbox" name="hobby" value="playgame" id="form__input--checkbox4"> <label for="form__input--checkbox4"> <svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 64 64"> <path d="M18 4l-6 42s66-68 74-40" /> </svg>ゲーム </label> </div>css.checkboxMember { margin-top: 10px; } input[type="checkbox"] { display: none; } input[type="checkbox"]+label::before { content: ""; position: absolute; border: 2px solid #343434; width: 16px; height: 16px; margin-top: 4px; } input[type="checkbox"]+label>svg { width: 20px; height: 20px; position: relative; margin-right: 5px; left: 3px; top: 2px; } input[type="checkbox"]+label>svg>path { fill: none; stroke: #e74c3c; stroke-linejoin: round; stroke-linecap: round; stroke-dasharray: 100; stroke-dashoffset: 100; stroke-opacity: 0; stroke-width: 8px; transition: stroke-dashoffset 0.1s ease-out; } input[type="checkbox"]:checked+label>svg>path { stroke-dashoffset: 0; stroke-opacity: 1; }selectbox
html<div> <label for="form__select--blood">血液型(select)</label> <select name="blood" id="form__select--blood"> <option value="A">A型</option> <option value="B">B型</option> <option value="O">O型</option> <option value="AB">AB型</option> </select> </div>cssselect { /* 右端の▼を消す */ -webkit-appearance: none; -moz-appearance: none; appearance: none; border: none; /* 代わりに任意の画像を指定 */ background: #eee url(triangle_down.svg) no-repeat center right 10px/16px 16px; background-size: 10px; } /* 右端の▼を消す(IE) */ select::-ms-expand { display: none; } select:focus { background: #eee url(triangle_up.svg) no-repeat center right 10px/16px 16px; background-size: 10px; border-bottom: 1px solid#e74c3c; outline: none; }textarea
html<div> <label for="form__textarea">コメント(textarea)</label> <textarea name="comments" rows="4" cols="40" id="form__textarea"></textarea> </div>csstextarea { padding: 10px; border-radius: 0; resize: none; border: 1px solid transparent; transition: border 0.2s ease-out; outline: none; background-color: #eee; } textarea::-webkit-scrollbar { width: 10px; } textarea::-webkit-scrollbar-track { background-color: #eee; } textarea::-webkit-scrollbar-thumb { background-color: #ccc; } textarea:focus { border-bottom: 1px solid #e74c3c; box-shadow: none; outline: none; }ボタン
フォームでは
input[type="submit"]
やinput[type="reset"]
が用意されていますが、特に理由がなければbutton[type="submit"]
を使用したほうが良いです。
- 送信ボタン
input[type="submit"]
、リセットボタンinput[type="reset"]
はCSSが効きにくい- リセットボタンはユーザビリティ的に配置自体しない方が良いので
button[type="reset"]
も使わないhtml<div> <p>送信ボタン(button)</p> <button type="submit" value="send">送信</button> </div>cssbutton { background: #16a085; border: none; border-bottom: 4px solid #434343; border-radius: 4px; color: #FFF; display: table; font-weight: bold; line-height: 1; margin: 10px auto 0; outline: none; padding: 20px 40px; transition: background-color 0.2s ease-out, transform 0.2s ease-out, border-bottom 0.2s ease-out; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } button:hover { background: #1abc9c; } button:active { background: #006266; border-bottom: 4px solid transparent; transform: translateY(4px); }状態別
プレースホルダの文字色
html<div> <label for="form__input--text">テキスト(type="text")</label> <input type="text" name="text" id="form__input--text" placeholder="プレースホルダ"> </div> <div> <label>コメント</label> <textarea name="comments" rows="2" cols="40" placeholder="プレースホルダ"></textarea> </div>css/* placeholderの文字色指定 */ input::placeholder, textarea::placeholder { color: #7f8c8d; } /* IE (疑似クラスで指定) */ input:-ms-input-placeholder, textarea:-ms-input-placeholder { color: #7f8c8d; } /* Edge (疑似要素で指定)*/ input::-ms-input-placeholder, textarea::-ms-input-placeholder { color: #7f8c8d; }オートコンプリート有効時
browser-syncを使用しているときは無効なので気づきにくいですが、chromeはオートコンプリート使用時に独自のスタイルが効いてしまうので、リセットします。
css/* auto complete(chrome) */ input:-webkit-autofill { /* 背景色(background-colorではなくbox-shadow) */ -webkit-box-shadow: 0 0 0 1000px #eee inset; /* 文字色(colorではなく-webkit-text-fill-color) */ -webkit-text-fill-color: #333 !important; }入力値の検証
inputタグのtype属性で指定されている形式に合致しているか、していないかを判定できます。
cssinput:invalid{ /* 入力値がOKのときのスタイルを記述 */ } input:valid { /* 入力値がNGのときのスタイルを記述 */ }値の範囲の検証
疑似クラス 状態 in-range inputタグのminやmax属性で指定されている数値の範囲に合致している out-of-range inputタグのminやmax属性で指定されている数値の範囲に合致していない html<input type="number" min="1" max="20">cssinput:in-range { /* 範囲内のときのスタイルを記述 */ } input:out-of-range { /* 範囲外のときのスタイルを記述 */ }その他の擬似クラス
疑似クラス 状態 required requiredが指定されている(入力必須) disabled disabledが指定されている(変更できない) read-only readonlyが指定されている(変更できない) empty 値が空 placeholder-shown スレースホルダが表示されている table:disabledとreadonlyの違い
disabled readonly 値の編集 できない できない form送信時 値を送信しない 値を送信する 値の選択やコピー 選択不可 選択可 フローティングラベル
最近流行りのフローティングラベルもCSSで対応できます。
html<div> <label class="floatinglabel"> <input type="text" placeholder=" "> <span class="labeltext">名前</span> </label> </div>css.floatinglabel { margin-top: 40px; position: relative; } .labeltext { position: absolute; top: 0px; left: 16px; font-size: 16px; color: #7f8c8d; transform-origin: 0 0; transition: all 0.2s ease-out; } .floatinglabel input:focus+span { color: #333; transform: translateY(-38px) translateX(-16px) }リポジトリ
- 投稿日:2019-11-24T21:45:11+09:00
ブラウザレンダリングの仕組み
概要
webサービスを公開するにあったて必ず使われることになるのがブラウザです。ブラウザがユーザーにwebページを表示する仕組みを理解することで、フロントエンド開発に役立てたり、ページ表示までのレスポンスの改善などに役立てていきたいと思い、今回ブラウザのレンダリングの仕組みの基本事項についてまとめました。
レンダリングの流れ
ブラウザがWebページをレンダリングする仕組みは上のような一連の流れになっています。以下でその一つ一つの工程の内容をみていきます。
レンダリングエンジンとJavaScriptエンジン
各工程をみる前に、ブラウザの構成要素をみていきます。
ブラウザにはレンダリングエンジンとJavaScriptエンジンという2つのエンジンが動作しています。レンダリングエンジン
HTMLやCSSなどを解析し、実際の画面に描画するためのもの。
レンダリングエンジンによってHTMLやCSSの解釈に差があるためデザインがブラウザによって崩れるという問題があります。JavaScriptエンジン
JavaScriptを実行するためのエンジン。【主なブラウザと各種エンジン】
ブラウザ レンダリングエンジン JavaScriptエンジン Google Chrome Blink V8 Safari Webkit Nitro IE Trident Chakra Microsoft Edge Blink V8 Mozilla Firefox Gecko SpiderMonkey Opera Blink V8 Microsoft Edgeの新しいversion(Chromiumベース)が来年の1月15日にリリース予定です。
https://forest.watch.impress.co.jp/docs/news/1216492.htmlリソースのダウンロード
ブラウザにURLが与えられると、ブラウザは画面のレンダリングに必要なリソース(HTMLやCSSファイル、JavaScriptファイルや画像ファイルなど)を読み込み始めます。
リソースは通常サーバ上に保存されていて、TCP/IPプロトコルを用いてブラウザはサーバーにリクエストしてレスポンスとしてリソースを受け取ります。
オブジェクトモデルの構築
ブラウザではレンダリングを行うために取得したリソースから「オブジェクトモデル」と呼ばれるものを構築します。この時、HTMLはDOMに、CSSはCSSOMに変換されます。
以下の図のように、バイト→文字列→トークン→ノード→オブジェクトモデル(DOM/CSSOM)
という流れでオブジェクトモデルは構築されます。
参考:https://developers.google.com/web/fundamentals/performance/critical-rendering-path/constructing-the-object-modelDOMツリーの構築
変換
ブラウザはディスクやネットワークからHTMLのバイトを読み取り、utf8などの文字コードに応じてや
のような文字列に変換しますトークン化
文字列を
StartTag:html
やEndTag:head
といったトークンに変換します(W3C HTML5 standardによって決められています)。字句解析
トークンはプロパティとルールを定義するオブジェクトに変換されます。
DOMの構築
オブジェクトはツリー構造にリンクされます。このツリー構造はオリジナルのマークアップの親子関係をそのまま保持します。つまり、HTMLオブジェクトはbodyオブジェクトの親であり、bodyは
オブジェクトの親であるという関係になります。ブラウザでは以降このページを処理する際に必ずこのDOMを使用します。
ブラウザはHTMLを処理する度に上のDOM構築の一連の流れを全て行うため処理するHTMLが多い場合は一連のプロセスに時間がかかりボトルネックになってしまいます。ページのライフサイクルイベントのDOMContentLoadedはDOMツリーの構築が完了した時点でレンダリングエンジンにより発火されます。そのためDOMContentLoadedイベントの時点では画像やCSSは読み込まれていない可能性があります。同じライサイクルであるloadイベントは画像やCSSなどを含む全てのリソースを読み込んだ時点で発火します。
CSSOMツリーの構築
ブラウザでDOMを構築している際にドキュメントのheadタグで外部のcssスタイルシートを参照しているlinkタグに遭遇すると、ブラウザはページのレンダリングにこのリソースが必要であると想定してこのリソースに対するリクエストを即座にディスパッチし、CSSのパースを行います。HTMLと同様にバイトが文字列、トークン、ノードに変換され最終的にCSSOMツリーを構築します。
DOMツリーとCSSOMツリーはそれぞれ独立しており後の工程で出てくるレンダリングツリーによってリンクされます。
JavaScriptの実行
各種リソースを読み込んだ後は、JavaScriptエンジンによってJavaScriptのコードが解析、実行されます。
まずJavaScriptのコードが字句解析されトークンとなり、次に構文解析され抽象構文木となり、最後にコンパイルされて実行可能なファイルとなり実行されます。レンダリングツリーの構築
コンテンツを記述したDOMツリーととドキュメントに適用するスタイルルールを記述したCSSOMツリーを結合することでレンダリングツリーを構築する必要があります。レンダリングツリーは各表示要素のレイアウトを計算するために使用され、画面にピクセルをレンダリングするペイント処理の入力となります。
レンダリングツリー構築の手順として、DOMツリーの各要素に対してどのCSSプロパティがマッチするかを計算します。CSSのルールセットにはh1やpのようなCSSセレクタと、widthやcolorのようなCSSプロパティがあり、最初にCSSセレクタによってDOMツリーの要素とCSSルールのマッチングを行います。その後、各DOM要素にどのCSSプロパティがマッチングするかを計算します。
CSSセレクタのマッチンング
レンダリングエンジンはCSSセレクタを右側から順に評価していきます。
ex)
.name > ul > li > p { color: red; }上のような場合、レンダリングエンジンはページ内の全ての要素に対して次のように判定します
- DOMがpである
- pの親要素がliである
- liの親要素がulである
- ulの親要素のclass名にnameが含まれている
CSSプロパティのマッチング
どのCSSプロパティがDOM要素に適用されるかをレンダリングエンジンが決定するための詳細度という仕組みがあります。詳細度には3つのレベルがあり、各CSSセレクタとの関連は次のようになっています
A. IDセレクタ
B. クラスセレクタ、擬似クラスセレクタ(:first-child)、属性セレクタ([type=input])
C. 要素セレクタ(div)、擬似要素セレクタ(::before)各レベルの優先度は A > B > Cとなっていて1つでも上位レベルが含まれる場合はそれが優先され、同じレベルでは値が大きい方が優先されます。
ex)
// A=0, B=2,C=0 .wrapper > .container { color: red; } // A=0, B=1, C=1 div > .container { color: white; } // A=1, B=0,C=0 #id { font-size: 20px; } // A=1, B=0,C=0 #id { font-size: 30px; } // A=1, B=0, C=1 #id::before { content: 'user'; }上の例で2つの
container
クラスが同じ要素に対して修飾されている場合、詳細度からcolor:red
の方が優先されます。また詳細度が同じ場合は後に定義されたCSSルールセットが適用されますそのため上の例の#id
の要素にはfont-size:30px
が適用されます。レンダリングツリーのレイアウト
レイアウトの工程では端末ビューボード内での各ノードのレイアウト情報を算出しますレイアウト情報には要素の大きさやmargin/padding、x/y/z軸の位置などが含まれます。
レンダリングエンジンはレンダリングツリーのルート要素から順番に各要素が持つCSSプロパティを元にレイアウト情報を決めていきます。ex)
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width,initial-scale=1"> <title>Critial Path: Hello world!</title> </head> <body> <div style="width: 50%"> <div style="width: 50%">Hello world!</div> </div> </body> </html>上のようなHTMLの場合、
viewport
がデバイスのサイズで倍率が1倍ということに決まります。
ネストされた2つのdivがあり、1つ目のdivがviewportの幅の50%になり、2つ目のネストしたdivの幅は親のdivの50%(全体の25%)になります。レンダリングツリーの描画
webページの描画にはペイントとコンポジットの2つの工程があります。
ペイント
各レイヤごとにテキストや色、画像などをピクセルに書き込みます。
1. 命令リストの作成
2. ピクセルへの書き込みの2つの工程からなります。
まず前の手順で構築したレンダリングツリーを元にグラフィックエンジンのための命令リストを作成します。命令リストでは「どのピクセルに何色を入れるのか」という命令が入っているため、その命令を元にグラフィックエンジンがピクセルの描画を行います。
次にピクセルの書き込みを行います(ラスタライゼーション)。ピクセルの書き込みはレイヤ単位で行われ、position:abosolute;やopacityといったCSSプロパティなどz軸を考慮する必要のある要素が存在する場合は新しいレイヤが作成されます。コンポジット
ピクセルを書き出したレイヤを合成してレンダリング結果を出力します。
まとめ
ブラウザレンダリングの仕組みを各工程ごとにみることで、ページライフサイクルのイベントがどの工程で発生するのかをきちんと理解できるようになりました。レンダリングの仕組みの知識を元に、ブラウザの開発ツールを駆使して開発時にデバッグやパフォーマンスのボトルネックの分析、改善に役立てていきたいと思います。
参考資料
- 投稿日:2019-11-24T15:06:20+09:00
HTML <br>タグは乱用するな!
コードレビュー会で
<必要なところに<br>タグを使うのは良いけど、レイアウトを整えるために<br>タグを使うのは良くないよ
とご指摘を受けたので<br>タグを使う時の注意点をまとめてみた。参考サイト→https://tech.pepabo.com/2017/06/22/html-markup-tips-01/
br_tag.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>brタグの乱用はダメ!</title> <link rel="stylesheet" href="css/br_tag.css"> </head> <body> <!-- brタグで行間を空ける --> <div class="container01"> 長い文章で作成したコンテンツの改行。 <br> 長い文章で作成したコンテンツの改行。 <br> 長い文章で作成したコンテンツの改行。 <br> 長い文章で作成したコンテンツの改行。 <br> <br> 長い文章で作成したコンテンツの改行。 <br> </div> <!-- pタグにclass名をつけて行間はCSSで調整 --> <div class="container02"> <p class="br_tag"> 長い文章で作成したコンテンツの改行。 </p> <p class="br_tag"> 長い文章で作成したコンテンツの改行。 </p> <p class="br_tag"> 長い文章で作成したコンテンツの改行。 </p> <p class="br_tag"> 長い文章で作成したコンテンツの改行。 </p> <p class="br_tag"> 長い文章で作成したコンテンツの改行。長い文章で作成したコンテンツの改行。長い文章で作成したコンテンツの改行。長い文章で作成したコンテンツの改行。 <br> 長い文章で作成したコンテンツの改行。長い文章で作成したコンテンツの改行。長い文章で作成したコンテンツの改行。長い文章で作成したコンテンツの改行。 </p> </div> </body> </html>br_tag.cssdiv { width: 50%; margin: 20px auto; text-align: center; } .container01 { background: rgb(231, 231, 231); color: darkblue; } .container02 { background: rgb(231, 231, 231); color: rgb(204, 7, 99); } .br_tag { margin: 5px 0; }表示させるとこんな感じ。ちょっと変だけど
影響は?
「行間空けるしbrタグ使おう」と安易な考えでマークアップしたのが青い字。
「きっと行間の調整もあるだろうしメンテナンスしやすい構造にしよう」と考えてマークアップしたのが赤い字。
brタグだけでは後々のメンテナンスが行いにくくなる。行間の自由が利かない。
brタグを2つ続けるなんてもっての外!
pタグ(改行が必要ない文ならspanタグでも良いかも)は段落を作るタグであり、文章が長く改行が必要なところにだけbrタグを使うべき。
行間の調整はclass名を付けてCSSで行う。こっちのほうが使い勝手が良く、メンテナンスもしやすい。
- 投稿日:2019-11-24T08:25:26+09:00
初心者によるプログラミング学習ログ 167日目
100日チャレンジの167日目
twitterの100日チャレンジ#タグ、#100DaysOfCode実施中です。
すでに100日超えましたが、継続。100日チャレンジは、ぱぺまぺの中ではプログラミングに限らず継続学習のために使っています。
167日目は
おはようございます
— ぱぺまぺ@webエンジニアを目指したい社畜 (@yudapinokio) November 23, 2019
167日目
webサイトコーディング課題
休みだったけど、ほかにいろいろやることあったので、コーディング自体は1時間ぐらいです。
さぼりぎみなので、今日は集中してやります#100DaysOfCode #駆け出しエンジニアと繋がりたい #早起きチャレンジ
- 投稿日:2019-11-24T01:43:12+09:00
【css】複数画像を背景に指定して固定する
仕上がりはこんな感じ
どのような指定をしたか
複数指定することがあまりないのでピンときませんが、
ちょい足しでできるようです。
「,」カンマで区切って複数指定する方法で、下記のようになります。style.cssbody{ background-image: url(img/hidoi_reply_yokeru_woman.png),url(img/hidoi_reply.png); background-repeat: no-repeat, no-repeat; background-position: left,right; background-position-y: top,top; background-attachment: fixed,fixed; background-size: 40%,40%; }第一引数、第二引数みたいな感じで、1個目に指定した画像には、
background-repeatなどのプロパティの1個目の指定が適用されるようです。デモページ
http://okachanblog.com/static/20191124background/
ひとこと
一枚画像作ろうと思ってたけどこの方法を知って、
複数配置をちょい足しで実装できたので画像作る手間省けました!
ラッキー!参考記事
CSS での複数の背景の利用方法
https://developer.mozilla.org/ja/docs/Web/CSS/CSS_Background_and_Borders/Using_CSS_multiple_backgrounds
- 投稿日:2019-11-24T00:18:09+09:00
transform: scale()で縮めた要素をposition: absoluteで絶対位置表示した時に、想定位置に表示されない問題を調べた
経緯
8pxの文字を表示したいという意志
chrome君の絶対10px以下の文字は許さないという仕様うーん、そんな時には10pxの文字を0.8倍表示!
デザインの指定では、ブロックの右下の端に表示ってあるから絶対位置指定で・・・
html<body> <div class="container"> <div class="badge"> 8px </div> </div> </body>css.container { background: skyblue; height: 100px; width: 100px; position:relative; } .badge { background: green; color: white; height: 15px; width: 15px; font-size: 10px; transform: scale(0.8); position: absolute; bottom: 0; right: 0; }え、なん?隙間なんなん?
原因究明
原因を特定するため、
- 絶対位置指定をいったん止める
- コンテナサイズを縮小前バッヂのサイズと同じにする
でどうなるか見てみます。
css(変更後).container { background: skyblue; height: 15px; width: 15px; } .badge { background: green; color: white; height: 15px; width: 15px; font-size: 10px; transform: scale(0.8); }すると・・・
こうなりました。
ここから、
transform: scale()
で縮小・拡大する際は、中心を基準にして指定倍のサイズになるということがわかりました。バッヂサイズを0.8倍して、
(元サイズ - 縮小後サイズ) / 2
の1.5px分のマージン(に似て非なるもの)が、上下左右についた状態で表示される感じ解法1 transformでの変化の基準点を指定する
transform-origin: right bottom;
と指定することで、右下基準で変化させることができる。css.container { height: 100px; width: 100px; background: skyblue; position: relative; } .badge { padding: 0; background: green; color: white; height: 15px; width: 15px; font-size: 10px; transform: scale(0.8); transform-origin: right bottom; position: absolute; bottom: 0; right: 0; }解法2 絶対位置指定をマイナスで指定する
transform-origin
がどーしても使えない状況があった場合(なさそう)、(元サイズ - 縮小後サイズ) / 2
分だけマイナス絶対位置を指定することで無理やり右端に合わせることができる
が、
-1.5pxなど、少数が絡むとうまくいかないので注意!うまくいかないパターン
css.container { height: 100px; width: 100px; background: skyblue; position: relative; } .badge { padding: 0; background: green; color: white; height: 15px; width: 15px; font-size: 10px; transform: scale(0.8); position: absolute; bottom: -1.5px; right: -1.5px; }うまくいくパターン
css.container { height: 100px; width: 100px; background: skyblue; position: relative; } .badge { padding: 0; background: green; color: white; height: 15px; width: 15px; font-size: 10px; transform: scale(0.6); position: absolute; bottom: -3px; right: -3px; }なお、絶対位置は0指定でマイナスマージンをとってもよい(こちらも小数点以下指定でずれる)
css.badge { padding: 0; background: green; color: white; height: 15px; width: 15px; font-size: 10px; transform: scale(0.6); position: absolute; bottom: 0; right: 0; margin-bottom: -3px; margin-right: -3px; }結の論
transformを使うときは、transform-originの存在を頭に置いておこう!