20191127のHTMLに関する記事は6件です。

HTML フォームの部品をまとめてみる(JavaScriptでの関連処理も)

概要

Jsの学習を進める中で、フォームの部品に関しても学習する機会があったので、この際軽くまとめてみようと思う。本当に軽くね。

テキスト

text.html
<input type="text" name="text_name" value="text1" id="text" />
text.js
//テキストの値を取得
$("#text").val();
//テキストのvalueの設定
$("#text").val("設定値");

チェックボックス

check.html
<input type="checkbox" name="check" value="check1" id="check1" checked />
<input type="checkbox" name="check" value="check2" id="check2" />
  • 複数の部品のname属性に同じ値を設定すると、1つのグループを作れる。
  • 「checked」を設定することで最初からチェックされている状態になる。
check.js
//チェックされているかを取得
var check1 = $("#check1").prop("checked");
//チェックを設定
var check2 = $("#check2").prop("checked", check1);
//値を取得するだけ
$("#check1").val();
//同じグループのチェックボックスをまとめて扱いたい場合のセレクタの書き方
//チェックされている値を取得
//チェックされている値がなければ、「undefined」を取得する
$("input[name=check]:checked").val();

ラジオボタン

radio.html
<input type="radio" name="radio_name" value="radioBtn1" id="radio1" />
<input type="radio" name="radio_name" value="radioBtn2" id="radio2" />
  • 複数の部品のname属性に同じ値を設定すると、1つのグループを作れる。
  • Jsについてはチェックボックスに書いてあるもので十分なので割愛。

セレクトボックス

select.html
<select name="select_name" id="list" multiple size=3>
  <option value="リスト1" selected>リスト1</option>
  <option value="リスト2">リスト2</option>
  <option value="リスト3">リスト3</option>
  <option value="リスト4">リスト4</option>
  <option value="リスト5">リスト5</option>
</select>

-「selected」を設定することで最初から選択されている状態になる。
- multiple size属性では同時選択できる数の最大値を設定できる

select.js
//選択されている値の取得
//選択されている値がなければ「null」が取得される
$("#list").val();

テキストエリア

textarea.html
<textarea name="textarea_name" cols="20" rows="3" id="textarea"></textarea>
  • rows属性では入力欄の高さを行数で指定できる
  • cols属性では入力欄の幅を文字数で指定できる

SUBMITボタン

submit.html
<button type="submit" id="submit">提出</button>
submit.js
//submitボタンにイベントを追加
$("#submit").submit(function() {});

おまけ

alert.js
//アラートを出力
alert("アラート");
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ChromeならiPadでコンソール出力を確認できる

Chrome for iOSがv.74からjavascriptのコンソールのログ出力に対応していました。

使い方

通常のChromeのように開発者ツールを開くのではなく、アドレスバーに chrome://inspect と入力してアクセスすると以下のページが表示されます。
37CD8AB7-1A3B-4188-BBCB-CE4B376BC921.jpeg
"ログ記録を開始"ボタンを押して、他のタブでページを開くとこのページにログが記載されていきます。
06D60A03-632A-4D6B-B1EA-50561814002B.jpeg
便利!

開発者ツールが使えるようになったわけではありませんが、ログ出力が見れるようになっただけでも大助かりです。

参考
- https://blog.chromium.org/2019/03/debugging-websites-in-chrome-for-ios.html
- https://www.softantenna.com/wp/software/chrome-for-ios-inspect/

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

初心者によるプログラミング学習ログ 170日目

100日チャレンジの170日目

twitterの100日チャレンジ#タグ、#100DaysOfCode実施中です。
すでに100日超えましたが、継続。

100日チャレンジは、ぱぺまぺの中ではプログラミングに限らず継続学習のために使っています。

170日目は

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

仮想DOMの時代にDOMを返すAPIを扱う(CSR編)

DMMグループ Advent Calendar 2019 12日目の記事です。

アドカレ映えしない、地味~な内容ですが、よろしくお願いします。

TL;DR

Angular / React / Vue製サイトにWebAPI経由で返されたDOMを挿入する

Angular React Vue
image.png image.png image.png

前書き

大規模Webサイトの各ページへ横断的に要素を挿入したい要件があるとします。

  • ブランドロゴ、ナビゲーション、トラッキングタグなど
  • 大規模なので、ページ間で管轄が分かれていたり、別々のアーキテクチャで構築されていたりする
  • 要素の管轄は一部署に集約したい

これらの要件を満たす手段として、 DOM Stringを返すようなAPIが提供される ことがあります。

他の手段としては「DOMを構築・挿入するスクリプトを配布する」という方法もありますが、それと比べて以下のようなメリットが期待できます。

  • 呼び出しタイミングや組み込み位置をある程度制御できる
  • サーバサイド・クライアントサイドのどちらからでも呼び出せる

「クライアントサイドからでも呼び出せる」とはいえ、必ずしもすんなりできるとは限らず、特に昨今のJSフレームワークで開発されたWebサービスに組み込むにはハマりどころが色々あります。

今回はAngular, React, Vue製でクライアントサイドレンダリングするようなWebサイトにそのようなAPIを組み込む際の方法や注意を纏めています。

[全般]DomStringをDOMとしてrenderされるようにinnerHtmlラッパを使う

いずれのフレームワークもXSS対策のため、テンプレートやJSX中で使用される変数はHTMLエスケープされるようになっています。

そのため、APIレスポンスのDOM stringをそのままテンプレートやJSXへ書き出そうとしても、HTMLがプレーンテキストとして表示されるだけでHTML要素としては読み込まれません。

そこで、変数中のHTMLをHTML要素として表示するための機能が提供されておりますので、そちらを利用します。

大前提としてAPI側のXSS対策はなされているものとします。

Angular React Vue
innerHTML dangerouslySetInnerHTML v-html

[linkタグ]Angularの場合はlinkタグの扱いに注意

APIレスポンスの中に、スタイルシートを読み込むためのlinkタグが含まれる場合の話です。

React, Vueは上記の方法でlinkタグを出力することが出来るのですが、AngularではinnerHTMLでHTML要素を出力する際に、やはりXSS対策のためlinkタグやscriptタグが削除されるようになっています。

これを回避する方法も提供されており、DomSanitizerのbypassSecurityTrustHtmlメソッドを利用することでlinkタグを出力することができます。

[scriptタグ]innerHTMLではJSを実行出来ない

APIレスポンスにscriptタグが含まれる場合は厄介です。

innerHTML(やこれまで挙げたFWのinnerHTMLラッパ)を利用することで、DOMやスタイルを反映させることができますが、scriptタグ経由のJSを呼び出すことは出来ません。

これはW3CのHTML5 scriptタグの仕様として記述されています。

When inserted using the document.write() method, script elements execute (typically synchronously), but when inserted using innerHTML and outerHTML attributes, they do not execute at all.

これについては、フレームワーク側のサポートはなく、動的にJSを実行する方法などに記載されているように、

  1. document.createElement('script'); でscript要素を作成、
  2. domStringをパースしてscript要素を組み立てていき、
  3. 最終的に document.body.appendChild などで完成したscript要素を挿入する

という、骨の折れる作業をする必要がありそうです。

[実践]ヘッダー・フッターでコンテンツを挟み込む

ここで少し具体的な話になります。

APIのレスポンスとして、複数のUI、例えばヘッダーとフッターが一遍に返されるような場合はどのように実装すればよいでしょうか。

image.png

この場合、ヘッダー・フッターを1つのコンポーネントとして扱うのが良さそうです。

各フレームワークとも、似たようなかたちで任意のコンテンツを挟み込むようなコンポーネントを実装出来ます。

Angular React Vue
ng-content props.children slot

実装例 / Angular

app.component.html
<navigation>
  <!-- サイトコンテンツ -->
</navigation>
navigation.component.html
<div [innerHTML]='header'></div>
<ng-content></ng-content>
<div [innerHTML]='footer'></div>

実装例 / React

App.js(内のJSX)
<Navigation>
  <!-- サイトコンテンツ -->
</Navigation>
Navigation.js(内のJSX)
<div dangerouslySetInnerHTML={{ __html: header }} />
  {this.props.children}
<div dangerouslySetInnerHTML={{ __html: footer }} />

実装例 / Vue

index.html
<navigation>
  <!-- サイトコンテンツ -->
</navigation>
template
<div v-html="header"></div>
  <slot></slot>
<div v-html="footer"></div>

おわり

  • 本記事はDOMを返すようなAPIをmanageする、というのがテーマであり、共通UIを提供する手段としてDOMを返すAPIの設計を推奨するものではありません。
  • アプリケーションをSSRする場合は更なる課題がありそうですので、それはまた次の機会に。
  • 明日は @uruha さんより、Virtual DOMの時代の楽しい話が聞けるんじゃないかと思うので、乞うご期待!

サンプルアプリの元ネタ

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

CSSでスライドショーをつくる(SCSS)

CSSスライドショー

JSを一切使わない、フェードアニメーションによるスライドショーを作成しました。

使用言語

  • HTML
  • CSS(時短のためSCSSを使用、CSSでもできます。)

完成系です。いろいろと見た目もつけてます。

See the Pen xxxomJO by k1-web (@k1-web) on CodePen.

内容の解説

まずはHTMLでスライドショーのDOMから。

slideshow.html
<div class="slideshow">
    <input type="radio" name="slideshow" id="s-1" checked>
    <input type="radio" name="slideshow" id="s-2">
    <input type="radio" name="slideshow" id="s-3">
    <input type="radio" name="slideshow" id="s-4">
    <input type="radio" name="slideshow" id="s-5">
    <ul class="slideshow_list">
        <li class="item-1" style="background:#ff7f7f;"></li>
        <li class="item-2" style="background:#bf7fff;"></li>
        <li class="item-3" style="background:#7fffff;"></li>
        <li class="item-4" style="background:#7fff7f;"></li>
        <li class="item-5" style="background:#ffbf7f;"></li>
    </ul>
    <div class="slideshow_label">
        <label for="s-1"></label>
        <label for="s-2"></label>
        <label for="s-3"></label>
        <label for="s-4"></label>
        <label for="s-5"></label>
    </div>
    <div class="slideshow_page">
        <label for="s-1"></label>
        <label for="s-2"></label>
        <label for="s-3"></label>
        <label for="s-4"></label>
        <label for="s-5"></label>
    </div>
</div>

HTMLですが、PHPとかPugとかで個数分forなどで回すと簡単に制御できます。
例えば、PHPでやるとすると下記のように枚数を簡単に制御できます。

slideshow.php
<?php $slideNum = 5 ; ?>
<div class="slideshow">
    <?php for($i=1;$i<=$slideNum;$i++): ?>
    <input type="radio" name="slideshow" id="s-<?php echo $i; ?>"<?php echo $i == '1' ? ' checked': ''; ?>>
    <?php endfor; ?>
    <ul class="slideshow_list">
        <?php for($i=1;$i<=$slideNum;$i++): ?>
        <li class="item-<?php echo $i; ?>"></li>
        <?php endfor; ?>
    </ul>
    <div class="slideshow_label">
        <?php for($i=1;$i<=$slideNum;$i++): ?>
        <label for="s-<?php echo $i; ?>"></label>
        <?php endfor; ?>
    </div>
    <div class="slideshow_page">
        <?php for($i=1;$i<=$slideNum;$i++): ?>
        <label for="s-<?php echo $i; ?>"></label>
        <?php endfor; ?>
    </div>
</div>

解説-スライド画像の部分

slideshow.html
    <ul class="slideshow_list">
        <li class="item-1" style="background:#ff7f7f;"></li>
        <li class="item-2" style="background:#bf7fff;"></li>
        <li class="item-3" style="background:#7fffff;"></li>
        <li class="item-4" style="background:#7fff7f;"></li>
        <li class="item-5" style="background:#ffbf7f;"></li>
    </ul>

スライドショーの画像とかを入れる部分、スライドの枚数分、

タグを作成。
本来はタグの中にタグなどをいれますが、今回は画像添付が面倒なので、背景色を指定しているだけです。

解説-タグの部分

slideshow.html
    <input type="radio" name="slideshow" id="s-1" checked>
    <input type="radio" name="slideshow" id="s-2">
    <input type="radio" name="slideshow" id="s-3">
    <input type="radio" name="slideshow" id="s-4">
    <input type="radio" name="slideshow" id="s-5">

スライドショーのうち、どこが今見えている所かを判定するためのラジオボタンになります。
スライドの枚数分を作成し、一番上位の親要素の直下にを指定してください。
CSSセレクタの「~」という、隣接する要素を指定できるセレクタを使用するので、
スライドの

タグや、タグの親要素がタグに隣接していないとスタイルをあてられません。

また、見た目上には非表示にしていますが、要素で選択し、2番目の画像なら2番目にチェックみたいな感じで動作しています。

解説-左右の矢印ボタンとぺジテーション

slideshow.html
    <div class="slideshow_label">
        <label for="s-1"></label>
        <label for="s-2"></label>
        <label for="s-3"></label>
        <label for="s-4"></label>
        <label for="s-5"></label>
    </div>
    <div class="slideshow_page">
        <label for="s-1"></label>
        <label for="s-2"></label>
        <label for="s-3"></label>
        <label for="s-4"></label>
        <label for="s-5"></label>
    </div>

先程のラジオボタンに対応するラベルです。2つ存在しても同じ#s-Nのラジオボタンに対応しています。
今回は.slideshow_labelの中のタグは進む・戻るボタンとして、.slideshow_pageはぺジテーションとして動作します。

本題のCSS(SCSS)です

codepenから説明に必要部分だけ抜粋しています。

slideshow.scss
@mixin slideLength($length, $show){
    $page: ceil($length / $show);
    content: $page;
    @for $i from 1 through $page {
        &:nth-of-type(#{$i}):checked {
            & ~ .slideshow_list {
                > li:nth-of-type(n+#{$i * $show - $show + 1}):nth-of-type(-n+#{$i * $show}) {
                    opacity: 1;
                    z-index: 10;
                    > a::before {
                        z-index: 11;
                    }
                }
            }
            & ~ .slideshow_label {
                $prev: $i - 1;
                $next: $i + 1;
                @if $prev < 1 {
                    $prev: $prev + $page;
                }
                @if $next > $page {
                    $next: $next - $page;
                }
                > label:nth-of-type(#{$prev}) {
                    display: block;
                    right: auto;
                    &::before {
                        right: auto;
                        -webkit-transform: rotate(45deg);
                        transform: rotate(45deg);
                    }
                }
                > label:nth-of-type(#{$next}) {
                    display: block;
                    left: auto;
                    &::before {
                        left: auto;
                        -webkit-transform: rotate(-135deg);
                        transform: rotate(-135deg);
                    }
                }
            }
            & ~ .slideshow_page {
                > label:nth-of-type(#{$i})::before {
                    content: '';
                }
            }
        }
    }
}

.slideshow {
  position: relative;
    input[type="radio"] {
      display: none;
      opacity: 0;
      width: 0;
      height: 0;
      margin: 0;
      padding: 0;

      $length: 5; //合計の枚数
      $show: 1; //1ページあたりに見せる数
      @include slideLength($length, $show);
  }
  .slideshow_list {
    > li {
      display: block;
      width: 300px;
      height: 150px;
      //~~
      opacity: 0;
      -webkit-transition: opacity 0.3s ease-in-out;
      transition: opacity 0.3s ease-in-out;
    }
  }    
  .slideshow_label {
    > label {
      display: none;
      width: 40px;
      height: 40px;
      background-color: #fff;
      border: 1px solid #f30;
      border-radius: 50%;
      cursor: pointer;
      position: absolute;
      top: 52px;
      left: 20px;
      right: 20px;
    }
  }
  .slideshow_page {
    > label {
      display: inline-block;
      width: 10px;
      height: 10px;
      background-color: #a8b7c6;
      border-radius: 50%;
      cursor: pointer;
      position: relative;
      &::before {
        display: inline-block;
        width: 10px;
        height: 10px;
        background-color: #777;
        cursor: pointer;
        position: relative;
        border-radius: 50%;
        position: absolute;
        top: 0;
        left: 0;
      }
    }
  }
}

解説-スライド画像の部分

slideshow.scss
  .slideshow_list {
    > li {
      display: block;
      width: 300px;
      height: 150px;
      //~~
      opacity: 0;
      -webkit-transition: opacity 0.3s ease-in-out;
      transition: opacity 0.3s ease-in-out;
    }
  }

opacityを0にしていて、transitionでopacityの透過をアニメーションにしています。
後ほど解説するラジオボタンのチェックを判定してopacityを1にしています。

解説-inputタグの部分

slideshow.scss
@mixin slideLength($length, $show){
    $page: ceil($length / $show);
    content: $page;
    @for $i from 1 through $page {
        &:nth-of-type(#{$i}):checked {
            & ~ .slideshow_list {
                > li:nth-of-type(n+#{$i * $show - $show + 1}):nth-of-type(-n+#{$i * $show}) {
                    opacity: 1;
                    z-index: 10;
                    > a::before {
                        z-index: 11;
                    }
                }
            }
            & ~ .slideshow_label {
                $prev: $i - 1;
                $next: $i + 1;
                @if $prev < 1 {
                    $prev: $prev + $page;
                }
                @if $next > $page {
                    $next: $next - $page;
                }
                > label:nth-of-type(#{$prev}) {
                    display: block;
                    right: auto;
                    &::before {
                        right: auto;
                        -webkit-transform: rotate(45deg);
                        transform: rotate(45deg);
                    }
                }
                > label:nth-of-type(#{$next}) {
                    display: block;
                    left: auto;
                    &::before {
                        left: auto;
                        -webkit-transform: rotate(-135deg);
                        transform: rotate(-135deg);
                    }
                }
            }
            & ~ .slideshow_page {
                > label:nth-of-type(#{$i})::before {
                    content: '';
                }
            }
        }
    }
}

.slideshow {
    input[type="radio"] {
      display: none;
      opacity: 0;
      width: 0;
      height: 0;
      margin: 0;
      padding: 0;

      $length: 5; //合計の枚数
      $show: 1; //1ページあたりに見せる数
      @include slideLength($length, $show);
  }
}

input:nth-of-type(1)がチェック付いた時には、.slideshow_list > li:nth-of-type(1)をopacity: 1;にして表示、
input:nth-of-type(2)がチェック付いた時には、.slideshow_list > li:nth-of-type(2)をopacity: 1;にして表示...
のようにラジオボタンでnth-of-typeを利用し、何番目にチェックついたら何番目のスタイルを変更するという動作にしています。

左右の矢印部分は、input:nth-of-type(2)だったら.slideshow_label>label:nth-of-type(1)を戻るボタンとして表示、
.slideshow_label>label:nth-of-type(3)を進むボタンとして表示、というようにチェックした数値の前後を表示としています。

ぺジテーションはそれぞれに対応したラベルに対してチェックを付けるようになっています。

SCSSの場合は上記らを自動的に計算できるので、関数化して「$length」に応じた枚数分スライドできるようになっています。

あとがき

フェードアニメーションのスライドショーはラジオボタンで認識して表示非表示で切り替える簡単な仕組みで作れます。
これが左右に動くスライドショーとなると、なかなか難しくなるので次回挑戦してみたいと思います。

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

juicerで謎のドメインが表示される

知らないドメインが表示されている!

2019-11-27_14h31_29.png

知らないドメイン(仮にhogehoge.comとします)が表示されている。
なんだこれは・・・?

該当のサイトでソースコードを見てみる

まさかとは思うが、当社のアナリティクスタグを使用しているのか?と疑い、このドメインのサイトで「ページのソースを表示」してみたが、アナリティクスタグがあるだけで、当社のものは見当たらなかった。

ソースが表示できないページがある

hogehoge.comとリンクしていないランディングページを見つけたので、同様にソースを確認しようとしたが右クリックできない。

右クリック禁止のサイトのソースコードを表示する

下記の方法でソースの表示しました。

・ソースコードを表示させたいURL
 https://hogehoge.com/

・URLの頭に下記をくっつけます。

 「view-source:URL」

・こうなります
 view-source:https://hogehoge.com/

この状態でChromeならF12(or 右クリックで「検証」)を押すと下記のようになります。

2019-11-27_13h53_56.png

このページを表示させたまま(これ重要)、別のタブで元のURLにアクセスすると右クリックで「ページのソースを表示」が表示できるようになります。

結果

当初疑った通り、当社のアナリティクスタグが設置されていました。恐らく当社のページからソースをコピペして、その際に修正し忘れたものかと思いますが、タグを設置しておいてアナリティクスで確認をしていないのか?と心配になりました。

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