20201223のHTMLに関する記事は11件です。

数値入力で input[type="number"]を使ったら、ユーザから問い合わせが増えてしまった話

この記事は SmartHR Advent Calendar 2020 23 日目の記事です。

こんばんは! @diescake です。

今年は、React で「そこそこの規模で、そこそこ機能のフォームアプリケーション」を設計・実装する機会がありました。
技術選定で formik + yup を選んだので、その選定理由と結果を共有しよう!

……そんなふうに考えていた時期が私にもありました。

驚くほど筆が進まず、いつの間にやら当日どころか既に夜になってしまったので tips 的な話に逃げる運びとなりました。ご了承ください。

というわけで、数値入力フォームに何気なく input[type="number"] を利用したら、ユーザから問い合わせが増えてしまった話です!

input[type="number"] とは?

今更説明するまでもないくらい基本的な要素ですが HTML において input はユーザの入力を受け付ける要素で、type 属性に "number" を指定すると、数値入力用のフォームを実現できます。

<input type="number" />

PC Chrome 87 の場合だと、こんな感じの UI でレンダリングされます。

スクリーンショット 2020-12-23 21.17.32.png

大体のブラウザでは、フォーカスするとクリッカブルな アイコンが表示されて、クリックすることでインクリメント・デクリメントできます。

加えて、通常のテキスト入力フォームである input[type="text"] とは異なり input[type="number"] の場合は、数値として valid な値しか入力を受け付けません。

そのため、数値の入力を期待しているフォームでは input[type="number"] を使うと安心です。
なんといっても、入力された value をそのまま parseInt() なり Number() に食わせれば数値として取り出せるのですから…。

そう考えて input[type="number"] を利用しました。

どんな問い合わせが発生したのか?

さて、ウェブアプリケーションをリリースしてユーザが触れるようになると、数値の入力ができない という問い合わせが発生しました。
キーボードを叩いても、数値入力フォームは無反応。テキスト入力フォームは入力できるのに、と……。

問い合わせを受け、プログラムを見直してもフォームに disabled な状態は定義していない……。
何より、テキスト入力は可能なのにどうして数値入力だけできないんや…… ?

うーん、何かしらのユーザ環境依存だったり、ブラウザ依存? そうか! IE ! やはり IE お前なのか!(ぐるぐる目)
などなど、色々と調査をしたものの……。

原因はなんだったのか?

わかってしまえばとても単純で、ユーザの IME で 全角入力 のまま数字キーを叩いてたことが原因でした。
全角数字は valid な数値として扱われないため、キーを叩いてもフォームは反応しません。

さらに悪いことに、この数値入力フォームでは 空入力を許容していませんでした。
つまり初期値の 0 を削除できないということですね。

なんでこんな仕様にしていたか言い訳をすると、フォームに対応する数値の型を number に絞っていたことが原因です。

type = {
  なんらかの数値A: number
  なんらかの数値B: number
  ...
}

こうすると、なんらかの数値 は non nullable として扱えるので、何かと楽なのはお分かりいただけます……よね?

……というわけで、開発都合で、BackSpace キー押しても無反応だし、(全角入力の状態で)数字キーを押しても無反応という、ユーザにとってとても分かりづらい状況が生まれていました。

どう対処したのか?

結局の所、数値入力フォームは input[type="text"] として自由に入力して貰って、onBlur のタイミングでサニタイズ、フォーマットすることにしました。
全角数値や といった入力も、半角文字列に置き換え valid な数値に変換しています。

合わせて、フォームに対応する型定義も、number から number | null や、素直に string に変更する必要がありますが、
ここは、入力中の文字列は、ローカルスコープの useState で保持するようにして、onBlur のタイミングでフォームに対応する値を更新することで number のまま凌ぎました。

いっそここまでくると、非制御コンポーネントにするという手段もありかもしれませんねー。

また、方針は変えずに半角数値のみを許容するにしても、
全角入力のままキーを叩いたら、tooltip で警告を出すような仕組みでも良いかもしれません。

例えば、OS のログイン画面だと、CapsLock のまま password を入力しようとすると tooltip で警告がでますよね。アレみたいなイメージです。

おわりに

話をスムーズにするため、そこはかとなく事実を湾曲しております!?

実際には input[type="number"] ではなくて、同等の動作をする自作コンポーネントに起因した話だったり、私は直接ユーザからの問い合わせを受ける立場になく、サポートチーム経由の話だったりします。

私は常々 SPA を書くエンジニアとして、使いやすさを大事にしていると思い込んでいたんですが、
いざ蓋をあけてユーザに触ってもらうと、多々反省もありました。

子供の頃読んだ 地獄先生ぬ〜べ〜不幸の手紙 の話をよく覚えているのですが、1 人 1 人のヘイトが小さくとも数十万人のヘイトが集まると巨大な怨念になりますよね。
いつしかこの怨念が生霊となって復習を果たしに来ることがないよう、フィードバックを受け止めてカイゼンを重ねていこうと思います!

では、明日は SmartHR の誇る催眠術師 @ouji-miyahara です!

参考文献

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

【初心者でもわかる】CSSで立体文字を再現する方法

どうも7noteです。CSSで3Dの文字を再現するには。

基本的にwebサイトは平面ですが、立体感のある表現をしたい時にちょっと無理やりですが文字を立体にさせることができます。

見本
sample.png

立体文字の書き方

index.html
<div class="text">777</div>
style.css
.text {
  font-size: 30px;    /* 文字サイズを30pxにする */
  font-weight: bold;  /* 太文字にする */
  text-shadow: 1px 1px 0 #666,2px 2px 0 #666,3px 3px 0 #666;  /* 影を3重に指定 */
}

正確には立体ではなく、あくまでも立体に見せかけているだけですが、パッと見はそれほど気にならないと思います。
影を3重にして、左上に伸びているような立体感を再現しています。

背景色を使うとこんなかんじの立体感も再現することができます。

sample2.png

style.css
.text {
  color: #99f;
  font-size: 30px;
  font-weight: bold;
  text-shadow: 1px 1px 0 #fff,2px 2px 0 #fff,-1px -1px 0 #fff;
  background: #99f;
  padding: 5px;
}

まとめ

グラデーションをつけたり、さらに立体化を求めるなら画像にするしか今は方法がないかもしれませんね。

おそまつ!

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

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

VScodeでPHPにコードユニペットを登録・追加する方法

開発に必須で何回も打つこいつ。

qiita.php
var_dump($qiita);

簡単に予測変換してくれないから登録した!

コードユニペットに登録する

PHP.jsonファイルを見つけ出す

Code > Preferences > User Snippets
検索窓に「PHP」と入力して

PHP.json(PHP)

を開く。

そしたらこんな内容のファイルが開かれる。

php.json
{
    // Place your snippets for php here. Each snippet is defined under a snippet name and has a prefix, body and
    // description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
    // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the
    // same ids are connected.
    // Example:
    // "Print to console": {
    //  "prefix": "log",
    //  "body": [
    //      "console.log('$1');",
    //      "$2"
    //  ],
    //  "description": "Log output to console"
    // }
}

記述開始

php.json
{
    // Place your snippets for php here. Each snippet is defined under a snippet name and has a prefix, body and
    // description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
    // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the
    // same ids are connected.
    // Example:
    // "Print to console": {
    //  "prefix": "log",
    //  "body": [
    //      "console.log('$1');",
    //      "$2"
    //  ],
    //  "description": "Log output to console"
    // }
    "var_dump": {
    "prefix": "vd",
    "body": [
      "var_dump($1);"
    ],
    "description": "var_dump"
    }
}

これで、「vd」→「Enter」キーで、「var_dump( );」を入力できるようになった!

参照記事

https://jobtech.jp/php/3747/

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

【Rails】フォームのオートフォーカスによる画面表示時のエラーについて

Railsで個人アプリを開発しているときに発生したエラーです。
備忘録としてエラーの発見から解消までを記します。

事象

スマートフォン(OS:iOS、ブラウザ:Google Chrome,Safari)でサインアップページにアクセスすると、ページのレイアウトが一瞬だけ表示された後、「このページは開けません。」というエラーが発生する。

image.png

ただし、該当URLを直打ちでアクセスするとページが正常に表示される。(これについては最終的に謎のまま終わりました・・・)

調査

他に同様のエラーが出るページは無いか探したところ、プロフィール編集ページでも発生しました。

サインアップページとプロフィール編集ページの共通点は、ユーザー情報を入力するフォームがあることでした。

サインアップページ

image.png

プロフィール編集ページ

image.png

原因

1画面内にautofocus: trueのフォームが複数存在すると、スマートフォンで閲覧した際にエラーが発生するようです。
そのため、autofocus: trueを一番最初の入力フォームにのみ残し、残りのフォームから消したところエラーは解消されました。
PCで閲覧する際にはエラーが出ないので見落としていました・・・
フォームをコピペで増やしていくのもいいですが、この辺りをしっかりメンテナンスしないといけないですね・・・。

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

【Rails】フォームのオートフォーカスによる画面エラー

Railsで個人アプリを開発しているときに発生したエラーです。
備忘録としてエラーの発見から解消までを記します。

事象

スマートフォン(OS:iOS、ブラウザ:Google Chrome,Safari)でサインアップページにアクセスすると、ページのレイアウトが一瞬だけ表示された後、「このページは開けません。」というエラーが発生する。

image.png

ただし、該当URLを直打ちでアクセスするとページが正常に表示される。(これについては最終的に謎のまま終わりました・・・)

調査

他に同様のエラーが出るページは無いか探したところ、プロフィール編集ページでも発生しました。

サインアップページとプロフィール編集ページの共通点は、ユーザー情報を入力するフォームがあることでした。

サインアップページ

image.png

プロフィール編集ページ

image.png

原因

1画面内にautofocus: trueのフォームが複数存在すると、スマートフォンで閲覧した際にエラーが発生するようです。
そのため、autofocus: trueを一番最初の入力フォームにのみ残し、残りのフォームから消したところエラーは解消されました。
PCで閲覧する際にはエラーが出ないので見落としていました・・・
フォームをコピペで増やしていくのもいいですが、この辺りをしっかりメンテナンスしないといけないですね・・・。

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

S3 で ホスティングしたウェブサイトをSSL化し、かつ特定の IP のみに開示する方法

はじめに

この記事は2020年の RevComm アドベントカレンダー25日目の記事です。クリスマス当日ですね!

前日は @qii-purine さんの「pythonでのアーキテクチャを考える」でした。

今回は最後となりますが、S3 で ホスティングしたウェブサイトをSSL化し、かつ特定の IP のみに開示する方法について紹介します。

やりたいこと

aws 使っている会社で、自分の作ったサイト(例えばデモサイト)を社内で共有するとき、みなさまどのように共有しますか。

典型的なやり方だと EC2 や Fargate でフロントエンドのサーバを立てるのではないかと思います。また、複数の静的ウェブページであれば、サーバーレスも検討するのではないかと思います。

そんな中、A) 頻繁にアクセスしない、B) 静的ファイルでも良い といった場合、 S3でのホスティング はコスト的に有効です。

しかし A) 社内のVPN のみで共有したい、B) サイトを転送時に暗号化したい と言う条件が加わると、SSL化特定のIPのみに開示 する必要があります。そのためには SSL 証明書発行 と ファイヤーウォール を用意する必要があります。

今回 Route 53, WAF, CloudFront, Certificate Manager, S3 を使って, どのようにSSL化して S3 でホスティングするかを紹介します。

また、今回 CloudFront を使いますが、CloudFront の場合はデータがキャッシュされるため、TTL (Time to Live) を意図的に設定しない限り変更が即時反映されません。TTL を短くすればいい話ですが、今回 S3 にあるサイトを変更したときにどのようにサイトを更新し、変更を即反映させるかについて紹介します。

( 内容はこちらのリンクとほぼ被りますので、もし、本記事でわからないことがあればそちらを読んでいただけると幸いです)

全体構成

全体構成はこんな感じです。

diagram.png

作成手順

取り組む前の準備

  • Route 53 でメインドメインをまだ作成していない場合は作成してください。
  • Route 53 に追加するレコード名を予め、検討してください。( revcomm-christmas-demo.example.com とします。)

素材の準備

まず、画像を用意します。(頑張ってコピー等でダウンロードしてください。)

christmas_tree.png

コードの準備

index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset='utf-8'>
    <title>Merry Christmas</title>
    <link rel='stylesheet' type='text/css' media='screen' href='main.css'>
</head>
<body>
    <div class="content">
        <img class="tree" src="./christmas_tree.png">
    </div>
</body>
</html>
main.css
.content{
    text-align:center;
    position: relative;
    width: 100%;
    height: 100%;
}

.content .text{
    position: absolute;
    text-align:center;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-family: 'Charm', cursive;
    font-size: 5em; 
}

html {
    width: 100%;
    height: 100%;
}

body{
    width: 100%;
    height: 100%;
    background: radial-gradient(#ffffff 50%, #91bee5 100%);
}


.ball{
    position: absolute;
    padding: 0px;
    margin: 0px;
    width: 20px;
    height: 20px;
    border-radius: 10px;
    background-color: rgb(218, 233, 247);
}

S3 の設定

S3 にアクセスし、バケットを新規作成してください。(revcomm-christmas-demo にします)

s00.png
s01.png

次にコンテンツをアップロードしてください。(ドラッグアンドドロップで可能)

s03.png

これで S3 にコンテンツが追加されました。

Certificate Manager の設定

SSL 証明書の作成です。
Certificate Manager にアクセスし、Region を N.Virginia にした状態で SSL 証明書の新規作成をクリックしてください。

image.png

予め検討した URL を入力してください。

image.png

DNS検証 にしてください。

image.png

レコード作成を選択してください。

image.png

レコードを追加すると、 Route 53 に SSL 証明書用のレコードが追加されます。

CloudFront の設定

CloudFront にアクセスし、CloudFront Distribution の新規作成をクリックしてください。

image.png

以下のようにパラメータを修正してください。(英語のままですが、ご容赦願います。)

  • Enable Origin Shield: No
  • Restrict Bucket Access: Yes
  • Origin Access Identity: Create New Identity
  • Generate Read Permissions on Bucket: Yes
  • Viewer Protocol Policy: Redirect
  • Price class: Use All Edge
  • Default Root Object: index.html
  • Alternate Domain Names: 予め検討した URL (revcomm-christmas-demo.example.com)
  • SSL Certificate: Custom SSL Certificate
  • Custom SSL: 予め検討した URL をタイプしてみてください。対応する SSL が出ます。(revcomm-christmas-demo と書けば出るはず)

cf01.png
cf02.png

しばらくすると、デプロイが完了します。(15分以上かかる可能性あり)
完了したら、 CloudFront のドメイン名をメモってください。

cf03.png

補足

  • S3 のポリシーも自動的に更新します。
  • OAI (Origin Access Identity) を使用することで、 S3 をpublic化をしない状態でホスティングすることができます。

Route 53 のレコード作成

Route 53 にアクセスし、ドメイン名 > レコードを追加 をクリックしてください。

image.png

レコードを作成してください。
Route ポリシーを Simple Route、 レコード名を予め検討した URL のサブドメイン名(revcomm-christmas-demo)、 レコードタイプをCNAME、 Value を先ほどメモった CloudFront を URL 入力してください。

image.png

WAF の設定

WAF にアクセスし、IP Sets で Region を Global (CloudFront) にした状態で新規 IP Set を作成してください。

image.png

IP を設定してください。

image.png

次にファイヤウィールの設定です。Web ACLs (Access Control List) で Region を Global (CloudFront) にした状態で 新規 ACL を作成してください。

image.png

任意の名前を設定して、関連リースの追加をクリックしてください。

image.png

WAF に関連するリソースを追加してください。(CloudFrontのID番号が表示する)

image.png

次へをクリックし、Add rules > Add my own rules ... をクリックしてください。

image.png

ルールタイプ IP Set を選択し、 ルール名(任意)を好きな名前にした状態で先ほど作成した IP set を入力し、作成してください。Default Action を Allow にしてください。

image.png

デフォルトを Block にしてください。

image.png

あとは ACL を作成して完成です。

結果

予め検討した URL (revcomm-christmas-demo.example.com) にアクセスすると、以下のようになります。

f00.png

ちなみに IP アドレスを変えると、以下のようになります。

f01.png

更新手順

次に更新方法です。

コードの修正

以下のようにコードを修正してください。

index.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset='utf-8'>
        <title>Merry Christmas</title>
        <link rel='stylesheet' type='text/css' media='screen' href='main.css'>
        <link rel="preconnect" href="https://fonts.gstatic.com">
        <link href="https://fonts.googleapis.com/css2?family=Charm:wght@700&family=Pacifico&display=swap" rel="stylesheet">
    </head>
    <body>
        <div class="content">
            <img class="tree" src="./christmas_tree.png">
            <div class="text">
                We wish you a Merry Christmas <br />
                and a Happy New Year
            </div>
        </div>
    </body>

    <script>
        function drop_snow(){

            var snow_ball = document.createElement("div")
            snow_ball.className = "ball"
            snow_ball.style.top = 0 + 'px'
            snow_ball.style.left = Math.random() * document.body.clientWidth  + 'px'

            var content = document.getElementsByClassName('content')[0]
            content.appendChild(snow_ball);

            var pos = 0
            var refreshIntervalId = setInterval(frame, 10);

            function frame() {
                if (pos > document.body.clientHeight) {
                    content.removeChild(snow_ball);
                    clearInterval(refreshIntervalId);
                } else {
                    pos+= 1;
                    snow_ball.style.top = pos + 'px';
                    snow_ball.style.opacity = (1 - pos/document.body.clientHeight)
                }
            }
        }

        for (let i=0; i < 10; i ++){
            drop_snow()
        }
        setInterval(drop_snow, 500);
    </script>

</html>
main.css
.content{
    text-align:center;
    position: relative;
    width: 100%;
    height: 100%;
}

.content .text{
    position: absolute;
    text-align:center;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-family: 'Charm', cursive;
    font-size: 5em; 
}

html {
    width: 100%;
    height: 100%;
}

body{
    width: 100%;
    height: 100%;
    background: radial-gradient(#ffffff 50%, #91bee5 100%);
}


.ball{
    position: absolute;
    padding: 0px;
    margin: 0px;
    width: 20px;
    height: 20px;
    border-radius: 10px;
    background-color: rgb(218, 233, 247);
}

S3に再度アップロード

再度作成した S3 バケットにアップロードしてください。

CloudFront のキャッシュ無効化

CloudFront を通して一度サイトにアクセスすると、作成したウェブページがキャッシュされます。
そこで、キャッシュを無効化する必要があります。キャッシュを無効化するためにはまず CloudFront にアクセスし、作成した CloudFront Distribution をクリックしてください。

cf10.png

Invalidations で 無効化の作成 をクリックしてください。

cf11.png

あとは index.html 入力し、無効化を実行してください。

結果、こんな感じになります。(codepen です。画像だとつまらないので。)

See the Pen aws_s3_ssl_example1 by zomaphone1 (@zomaphone) on CodePen.

まとめ

以上で、S3 で ホスティングしたウェブサイトをSSL化し、かつ特定の IP のみに開示する方法について一つ紹介しました。

通常のホスティングであれば S3 で設定が済みますが、SSL化 と 特定の IP に対して公開したい場合、上記の手段は有効です。

もちろん他の手段として、S3 だけで解決する手段もあります。

こちらの記事のようにアクセスポイントを変えるだけ https で公開することもできます。IP と https のみを許容する場合は bucket policy を変えるだけで済みます。

Before:
https://revcomm-christmas-demo.s3-website-ap-northeast-1.amazonaws.com/
After: 
https://s3-ap-northeast-1.amazonaws.com/revcomm-christmas-demo/index.html

ただし、このやり方で注意していただきたいのが、httpsindex.html を明示的に示さないといけないことです。そのため、共有するときに URL の扱いに注意しないといけなくなります。

もし、リダイレクト等も含めたい、index.html まで記述させたくない場合、ぜひこちらの記事を参考に実施していただけると幸いです。

最後に

いかがでしたでしょうか。

今回の Qiita advent calendar は RevComm として初の試みではありましたが、有益な情報は得られましたでしょうか。

RevComm では「コミュニケーションを再発明し、人が人を想う社会を創る」というミッションを基に、電話営業をディープラーニングの技術で支援するプロダクト( Miitel ) をはじめ、コミュニケーションに関わる様々なプロダクト開発を行っており、日頃からコミュニケーションの在り方を再定義するという難しい課題に取り組んでいます。

弊社ではテックに関わらず成長に貪欲な人がたくさんいます。会社としてまだまだ若いところもありますが、新しい技術等に積極的に取り入れる会社ではあるので、エンジニアとしてテックスタックを広げたいという人にとってはいい会社です。

もし、弊社で一緒に働きたいという想いがあれば、あるいは少しでも興味があればぜひぜひ弊社の採用ページに応募してみてください。

ちなみに働き方について興味があれば、ぜひ CTO が書いたこちらの記事を読んでください。

では良いクリスマスを!!

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

【CSS】いまさらだけどclearfixを知ったので図解します!

はじめに

今回はCSSでレイアウト崩れを防止できる、clearfixという手法をご紹介します!
この方法自体は2004年からあるらしく全然定番のお話らしいですが、自分は本を読んでて最近やっと仕組みがハラオチしたので、ぜひアウトプットさせていただきたく参上しました!

clearfixいつ使うの?:floatプロパティでレイアウト崩れちゃう!という場合

基本的にHTMLって縦方向に骨組みを作成するじゃないですか。

スクリーンショット 2020-12-05 21.23.48.png

これは夏目漱石のポートフォリオサイト「Kusa-Makura」(さっき作った)ですが、HTMLとしては以下のようになっています。(ボディー部のみ)

      <div class="main">
        <h1>About</h1>
        <section class="profile">
          <div class="profile-text">
            <h2 class="icon">Profile</h2>
            <p><span>夏目漱石</span><br>
              わし。なんかとてもすごい人。<br>
              すごすぎて顔が1000円札に使われていたこともある。<br>しかし今その辺でわしの1000円札を使おうとすると、店員にちょっと変な顔されると思うので注意されたい。<br>ここからWikipedia・・・本名は夏目 金之助(なつめ きんのすけ)。俳号は愚陀仏。明治末期から大正初期にかけて活躍した近代日本文学の頂点に立つ作家の一人である。代表作は『吾輩は猫である』『坊っちゃん』『三四郎』『それから』『こゝろ』『明暗』など。明治の文豪として日本の千円紙幣の肖像にもなり、講演録「私の個人主義」も知られている。漱石の私邸に門下生が集った会は木曜会と呼ばれた。・・・ここまでWikipedia</p>
          </div>
          <img src="images/natsume.jpg" alt="夏目漱石のプロフィール画像" class="profile-image" width="200">
        </section>
        <section class="career">
            <h2 class="icon">Works</h2>
              <p><span>草枕</span><br>
              山路を登りながら、こう考えた。<br>
              智に働けば角が立つ。情に棹させば流される。意地を通せば窮屈だ。とかくに人の世は住みにくい。住みにくさが高じると、安い所へ引き越したくなる。どこへ越しても住みにくいと悟った時、詩が生れて、画が出来る。</p>
        </section>
      </div>

で、HTMLだととにかく縦方向にしか進まないので、横方向にレイアウトしたいときに便利なのがCSSのfloatプロパティです。

スクリーンショット 2020-12-05 21.39.07.png

上の画像のように、プロフィール内のテキスト部分を左に、画像を右に寄せていい感じのレイアウトにしたかったのですが…
自分が最初に辿り着いたのは、以下のような状態でした。

スクリーンショット 2020-12-05 21.42.51.png

ヘッダー部分はうまいこと左右がいい感じになったのですが、ボディー部分のレイアウトが崩れてしまったのです……
こういう場合に、clearfixが役立ちます!

そもそもなぜfloatはレイアウト崩れるの?:親要素の高さがなくなるから

floatでレイアウトが崩れるのは、親要素の高さがなくなるからです。
(ヘッダーは親要素の高さをあらかじめ決めていますが、ボディー部は高さを決めておくことってなかなかないですよね)
もともと親要素は、内包する子要素に合わせて高さを変えられます。

advCal01.png

しかしfloatプロパティを適用すると、文字通り親から子が浮いてしまいます。

advCal02.png

今回のようなレイアウトだと、プロフィールセクション内は左のプロフィール文と右の写真しか子要素を持たないため、両方浮かせてしまうと、親要素は高さゼロになってしまいます。

advCal03.png

そして、floatは後続の要素を回り込ませる特性があるため、左の文と右の写真の段差に次のレイアウト要素が回り込んでしまっているというわけです。

clearfixどう使うの?:親要素にclearfixで杭を打ち込んで高さを維持する!

さて、ここでいよいよclearfixの登場です!やり方を順を追って説明します。

①HTMLで親要素にclearfixクラスを追加する

      <div class="main">
        <h1>About</h1>
        <section class="profile clearfix"> <!--  ここ!  -->
          <div class="profile-text">
            <h2 class="icon">Profile</h2>
            <p><span>夏目漱石</span><br>
              わし。なんかとてもすごい人。<br>
<!--  後略  -->

②CSSでclearfixに空のボックスを生成する

.clearfix:after {
  content: "";
}

これは何をしているかというと、擬似要素「:after」を利用して、親要素の末尾に空のボックスを置いています。
まだ回り込みは解消されていません。

advCal04.png

③空のボックスをブロックレベル要素にする

.clearfix:after {
  content: "";
  display: block;
}

display: block;を追加して空のボックスをブロックレベル要素にしています。
ブロックレベル要素にすると必ず前後に改行が入り、高さや幅の指定が可能になります。(↔️インライン要素:テキストの一部として扱われるため、改行は入りません)
つまり、前後の要素の回り込みが解消されます。

advCal05.png

④空のボックスのfloatを解除する

.clearfix:after {
  content: "";
  display: block;
  clear: both;
}

最後にclearプロパティを使って、空のボックスにも自動的に適用されていたfloatを解除します。
これにより、空のボックスが杭を打ち込むような形になり、親要素の高さを保てるようになります!

advCal06.png

出来上がりです!
回り込みが解決して、ちゃんとしたレイアウトになりましたね!
お疲れ様でした!

スクリーンショット 2020-12-05 23.24.43.png

おわりに

親要素の高さを保つだけなら、floatしているものと同じ高さの空ボックスを置くなど、いろんな対策があると思うのですが、
擬似要素で必ず親要素の末尾に空ボックスを置く方法であれば、どんな高さのレイアウト部分にも使用できて、再利用性が高いのがすごく便利だなと思いました!さすが老舗の方法…!

あと、図解すると、親要素から「浮く」って何???という疑問が解決して、スッキリしました!
(一応HTMLの本読みながら描きましたが、解釈が間違ってたら教えてくださいmm!)

ここまでお読みいただき、ありがとうございました!

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

html, javasript, jquery (OOP) 作成 P5(private, publicプロパティ)

初めに

 ジャバスクリプトのprivate, publicプロパティがあります。

private, publicプロパティに対して

①HTML分に以下のHTMLソースを追加する

<html>

  <head>
    <title>Hoge</title>
  </head>

  <body id="main-id">
  </body>

</html>

②JQUERYを選択してから、JAVASRIPT分に以下のソースを追加する

// マインオブジェクト
var $main  = $("#main-id");

// クラス定義
var Hoge = function() {

  // private プロパティ
  var privateString = "サンプルテキスト";

  // public プロパティの場合、 thisを使う
  this.privateString = privateString;
};

// ボタンオブジェクト
var $button = $("<button>").html("hoge");

// ボタンクリックイベント
$button.click(function() {

   // インスタンス作成
   var hoge = new Hoge();

   // public プロパティを呼ぶ
   alert(hoge.privateString);
});

//親オブジェクトに子オブジェクトを追加
$main.append($button);

実行
スクリーンショット 2020-12-23 13.31.10.png

private, public関数に対して

①HTML分に以下のHTMLソースを追加する

<html>

  <head>
    <title>Hoge</title>
  </head>

  <body id="main-id">
  </body>

</html>

②JQUERYを選択してから、JAVASRIPT分に以下のソースを追加する

// マインオブジェクト
var $main  = $("#main-id");

// クラス定義
var Hoge = function() {

  // private プロパティ
  var privateString = "サンプルテキスト";

  // public プロパティの場合、 thisを使う
  this.privateString = privateString;

  // private 関数
  var show = function() {
    alert(privateString);
  };

  // public 関数の場合、thisを使う
  this.show = function() {
      return show();
  }
};

// ボタンオブジェクト
var $button = $("<button>").html("hoge");

// ボタンクリックイベント
$button.click(function() {

   // インスタンス作成
   var hoge = new Hoge();

   // public 関数を呼ぶ
   hoge.show();
});

//親オブジェクトに子オブジェクトを追加
$main.append($button);

実行
スクリーンショット 2020-12-23 13.35.56.png

public関数に対してprototypeもできます。

例)

// クラス定義
var Hoge = function() {

  // private プロパティ
  var privateString = "サンプルテキスト";

  // publicの場合、 thisを使う
  this.privateString = privateString;


  // private 関数
  var show = function() {
    alert(privateString);
  };

  // public 関数の場合、thisを使う
  this.show = function() {
      return show();
  }
};

上の方法の代わりに、prototypeもできます

// クラス定義
var Hoge = function() {

  // private プロパティ
  var privateString = "サンプルテキスト";

  // public プロパティの場合、 thisを使う
  this.privateString = privateString;
};

// public 関数の場合
Hoge.prototype.show = function() {
    alert(this.privateString);
};

実行した後、結果と同じ
スクリーンショット 2020-12-24 16.22.53.png

以上

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

PHPによる繰り返し処理(FOREACH)の応用

はじめに

PHP学んで1ヶ月のクソ初心者。
日々の学びを投稿で残していきますわ。

そもそもFOREACHとは

for文のように繰り返しループ処理を行う構文。
foreach が使えるのは配列とオブジェクトだけ。
構造には二種類の構文がある。

①foreach (iterable_expression as $value)
②foreach (iterable_expression as $key => $value)

★オブジェクト・・・現実世界に存在する「もの」や「概念」のこと。

①の実例

$x = [
    "めろん" => "melon", "もも" => "peach", "いちご" => "strawberry"
];

foreach ($x as $fruit) {
    echo $fruit."<br>";
}

①の表示結果

melon
peach
strawberry

繰り返し処理を行わないで、単純に1つの値を連想配列の中から取り出すのであれば、
値を取り出すための「キー」を指定してあげる必要がある。
しかし!!!
FOREACHでは「キー」を指定することなく、値を順番に利用できる?

②の実例

$x = [
    "めろん" => "melon", "もも" => "peach", "いちご" => "strawberry"
];

foreach ($x as $kudamono => $fruit) {
    echo "キーは「".$kudamono."」<br>";
    echo "値は「".$fruit."」<br>";
}

②の表示結果

キーは「めろん」
値は「melon」
キーは「もも」
値は「peach」
キーは「いちご」
値は「strawberry」

連想配列の値を取り出すときに使う時にキー自体を使うことも可能?‍♂️

実例(応用)

htmlにPHPを組み込んで使用したい場合を紹介します?
★一部コーディングは省略している。

<?php
$fruit = [
    [
        'name' => 'めろん',
        'price' => 1000,
        'note' => '夕張メロンを推します'
    ],
    [
        'name' => 'もも',
//省略
        'note' => 'イチゴ狩り多し!'
    ],
]
?>
<!-- 省略 -->
    <?php foreach ($fruit as $fruit){ ?>
    <div class="box">
            <p class="name"><?php echo '果物名:'.'<br>'.$fruit['name'] ; ?></p>
            <p class="price"><?php echo'¥'.$fruit['price'] ; ?></p>
            <p class="note"><?php echo'メモ:'.'<br>'.$fruit['note'] ; ?></p>
    </div>
    <?php } ?>

「fruit as」の後に同じ変数である「fruit」を使っているが、
FOREACHでは、左側の変数を一度別の変数(右側の変数)に入れて
表示というルールがあるため、このようなコーディングとなる。
★同じだからといって「as fruit」を省略してはダメ?‍♀️

表示結果

スクリーンショット 2020-12-22 21.47.14.png

おわりに

FOREACHって面白いね!(小並感)
てか、フルーツ食べたくなりました???

参考サイト

公式はやっぱ神。はっきりわかんだね。
https://www.php.net/manual/ja/control-structures.foreach.php

スペシャルサンクス

会社のみなさん?‍♂️?‍♂️?‍♂️
ありがとうございまーす!

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

html, javasript, jquery (OOP) 作成 P4(リスト追加)

html、 javasriptのツールの開発

 →オンラインツールを使えるため、ツールのダウンロードが不要です。
   https://jsfiddle.net/

スクリーンショット 2020-12-21 10.23.23.png

テーブルに対して(例1)

①HTML分に以下のHTMLソースを追加する

<html>

  <head>
    <title>Hoge</title>
  </head>

  <body id="main-id">
  </body>

</html>

②JQUERYを選択してから、JAVASRIPT分に以下のソースを追加する

// マインオブジェクト
var $main  = $("#main-id");

// テーブルオブジェクト
var $table = $("<table>"); 

// クラス
var Row = function(table) { // table:変数

   // プロパティ
   var $tr    = $("<tr>");                        // 行オブジェクト
   var $td    = $("<td>").html("サンプルテキスト");  // カラムオブジェクト

   // メソッド
  this.addRow = function() {
    table.append($tr.append($td));
  };
};

// ボタンオブジェクト
var $button = $("<button>").html("追加");

// ボタンクリックイベント
$button.click(function() {

    // インスタンス作成
    var row = new Row($table);

    // クラスのメソッドを呼ぶ
    row.addRow();
});

//親オブジェクトに子オブジェクトを追加
$main.append($button).append($table);

実行
スクリーンショット 2020-12-23 10.56.53.png

テーブルに対して(例2)

①HTML分に以下のHTMLソースを追加する

<html>

  <head>
    <title>Hoge</title>
  </head>

  <body id="main-id">
  </body>

</html>

②JQUERYを選択してから、JAVASRIPT分に以下のソースを追加する

// マインオブジェクト
var $main  = $("#main-id");

// テーブルオブジェクト
var $table = $("<table>"); 

// クラス定義
var Row = function(table) { // table:変数

   // プロパティ
   var $tr     = $("<tr>");                    // 行オブジェクト
   var $td    = $("<td>").html("インプット:");  // カラムオブジェクト
   var $input = $("<input>");                  // インプットオブジェクト

  // 追加メソッド
  this.addRow = function() {
    // 行を追加
    table.append($tr.append($td.append($input)))
  };

  // 削除メソッド
  this.removeRow = function() {
    // 行を削除
    table.find("tr:last").remove()
  };
};

// ボタンオブジェクト
var $buttonAdd = $("<button>").html("追加");

// ボタンクリックイベント
// ボタンをクリックすると、行が追加される
$buttonAdd.click(function() {

    // インスタンス作成
    var row = new Row($table);

    // クラスのメソッドを呼ぶ
    row.addRow();
});

// ボタンオブジェクト
var $buttonRemove = $("<button>").html("削除");

// ボタンクリックイベント
// ボタンをクリックすると、行が削除される
$buttonRemove.click(function() {

    // インスタンス作成
    var row = new Row($table);

    // クラスのメソッドを呼ぶ
    row.removeRow();
});

//親オブジェクトに子オブジェクトを追加
$main.append($buttonAdd).append($buttonRemove).append($table);

実行
スクリーンショット 2020-12-23 11.52.16.png

ul,liに対して

①HTML分に以下のHTMLソースを追加する

<html>

  <head>
    <title>Hoge</title>
  </head>

  <body id="main-id">
  </body>

</html>

②JQUERYを選択してから、JAVASRIPT分に以下のソースを追加する

// マインオブジェクト
var $main  = $("#main-id");

// ulオブジェクト
var $ul = $("<ul>"); 

// クラス
var Row = function(ul) { // ul:変数

   // プロパティ
   var $li    = $("<li>").html("サンプルテキスト");  // liブジェクト

   // メソッド
  this.addRow = function() {
    ul.append($li);
  };
};

// ボタンオブジェクト
var $button = $("<button>").html("追加");

// ボタンクリックイベント
$button.click(function() {

    // インスタンス作成
    var row = new Row($ul);

    // クラスのメソッドを呼ぶ
    row.addRow();
});

//親オブジェクトに子オブジェクトを追加
$main.append($button).append($ul);

実行
スクリーンショット 2020-12-23 10.54.32.png

以上

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

html, javasript, jquery (OOP) 作成 P4(クラス定義)

初めに

ジャバスクリプトにはクラス定義を行う(プロパティ、メソッド)

html、 javasriptのツールの開発

 →オンラインツールを使えるため、ツールのダウンロードが不要です。
   https://jsfiddle.net/

スクリーンショット 2020-12-21 10.23.23.png

テーブルに対して(例1)

①HTML分に以下のHTMLソースを追加する

<html>

  <head>
    <title>Hoge</title>
  </head>

  <body id="main-id">
  </body>

</html>

②JQUERYを選択してから、JAVASRIPT分に以下のソースを追加する

// マインオブジェクト
var $main  = $("#main-id");

// テーブルオブジェクト
var $table = $("<table>"); 

// クラス定義
var Row = function(table) { // table:変数

   // プロパティ
   var $tr    = $("<tr>");                        // 行オブジェクト
   var $td    = $("<td>").html("サンプルテキスト");  // カラムオブジェクト

   // メソッド
  this.addRow = function() {
    table.append($tr.append($td));
  };
};

// ボタンオブジェクト
var $button = $("<button>").html("追加");

// ボタンクリックイベント
$button.click(function() {

    // インスタンス作成
    var row = new Row($table);

    // クラスのメソッドを呼ぶ
    row.addRow();
});

//親オブジェクトに子オブジェクトを追加
$main.append($button).append($table);

実行
スクリーンショット 2020-12-23 10.56.53.png

テーブルに対して(例2)

①HTML分に以下のHTMLソースを追加する

<html>

  <head>
    <title>Hoge</title>
  </head>

  <body id="main-id">
  </body>

</html>

②JQUERYを選択してから、JAVASRIPT分に以下のソースを追加する

// マインオブジェクト
var $main  = $("#main-id");

// テーブルオブジェクト
var $table = $("<table>"); 

// クラス定義
var Row = function(table) { // table:変数

   // プロパティ
   var $tr     = $("<tr>");                    // 行オブジェクト
   var $td    = $("<td>").html("インプット:");  // カラムオブジェクト
   var $input = $("<input>");                  // インプットオブジェクト

  // 追加メソッド
  this.addRow = function() {
    // 行を追加
    table.append($tr.append($td.append($input)))
  };

  // 削除メソッド
  this.removeRow = function() {
    // 行を削除
    table.find("tr:last").remove()
  };
};

// ボタンオブジェクト
var $buttonAdd = $("<button>").html("追加");

// ボタンクリックイベント
// ボタンをクリックすると、行が追加される
$buttonAdd.click(function() {

    // インスタンス作成
    var row = new Row($table);

    // クラスのメソッドを呼ぶ
    row.addRow();
});

// ボタンオブジェクト
var $buttonRemove = $("<button>").html("削除");

// ボタンクリックイベント
// ボタンをクリックすると、行が削除される
$buttonRemove.click(function() {

    // インスタンス作成
    var row = new Row($table);

    // クラスのメソッドを呼ぶ
    row.removeRow();
});

//親オブジェクトに子オブジェクトを追加
$main.append($buttonAdd).append($buttonRemove).append($table);

実行
スクリーンショット 2020-12-23 11.52.16.png

ディブに対して

①HTML分に以下のHTMLソースを追加する

<html>

  <head>
    <title>Hoge</title>
  </head>

  <body id="main-id">
  </body>

</html>

②JQUERYを選択してから、JAVASRIPT分に以下のソースを追加する

// マインオブジェクト
var $main  = $("#main-id");

// divオブジェクト
var $div = $("<div>"); 

// クラス定義
var Row = function(div) { // div:変数

   // divプロパティ
   var $span   = $("<span>").html("タイトル");  // liブジェクト

   // divプロパティ
   var $div    = $("<div>").html("サンプルテキスト");  // divブジェクト

   // メソッド
  this.addRow = function() {
    div.append($span).append($div);
  };
};

// ボタンオブジェクト
var $button = $("<button>").html("追加");

// ボタンクリックイベント
$button.click(function() {

    // インスタンス作成
    var row = new Row($div);

    // クラスのメソッドを呼ぶ
    row.addRow();
});

//親オブジェクトに子オブジェクトを追加
$main.append($button).append($div);

実行
スクリーンショット 2020-12-24 9.32.54.png

ul,liに対して

①HTML分に以下のHTMLソースを追加する

<html>

  <head>
    <title>Hoge</title>
  </head>

  <body id="main-id">
  </body>

</html>

②JQUERYを選択してから、JAVASRIPT分に以下のソースを追加する

// マインオブジェクト
var $main  = $("#main-id");

// ulオブジェクト
var $ul = $("<ul>"); 

// クラス定義
var Row = function(ul) { // ul:変数

   // プロパティ
   var $li    = $("<li>").html("サンプルテキスト");  // liブジェクト

   // メソッド
  this.addRow = function() {
    ul.append($li);
  };
};

// ボタンオブジェクト
var $button = $("<button>").html("追加");

// ボタンクリックイベント
$button.click(function() {

    // インスタンス作成
    var row = new Row($ul);

    // クラスのメソッドを呼ぶ
    row.addRow();
});

//親オブジェクトに子オブジェクトを追加
$main.append($button).append($ul);

実行
スクリーンショット 2020-12-23 10.54.32.png

以上

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