CATEGORIES:CSS JAVA
虫眼鏡をクリックで検索フォーム表示

検索フォームを、通常は虫眼鏡だけにして、押すと右へスライドして入力欄が開くようにする。
空のまま押すと元の位置に戻るし、入力があればそのまま検索する。そんな検索フォーム。

状態虫眼鏡を押すと
閉じている(最初)右へスライドして入力欄が開く。カーソルも入る
開いていて、入力なし送信せず、元の位置に戻る
開いていて、入力ありそのまま送信(検索ページへ移動)

※ 開いているとき、空のまま Esc キーを押すか、フォームの外をクリックしても閉じる。
※ JavaScriptが動かない環境では閉じる設定が効かないので入力欄は出しっぱなしで今まで通り検索できる。

<!-- 検索フォーム:最初は虫眼鏡だけ → 押すと右へスライドして入力欄が開く -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">

<style>
/* ===== ここから CSS ===== */
.search-slide {
 --sf-width: 160px;  /* ★ 開いたときの入力欄の幅 */
 --sf-speed: .35s;   /* ★ スライドの速さ(約0.35秒) */
 display: flex;
 align-items: center;
}

/* 入力欄:開閉の動きは幅・透明度・下線の色をいっしょに動かす */
.search-slide input[type="text"] {
 width: var(--sf-width);
 padding: 0;
 border: none;
 border-bottom: 1px solid #A6A6A6;
 background: #F0F0EE;
 overflow: hidden;
 transition: width var(--sf-speed) ease, opacity var(--sf-speed) ease, border-color var(--sf-speed) ease;
}
.search-slide input[type="text"]:focus {
 outline: 0;
}

/* 虫眼鏡:Font Awesome 4.7 の文字()を表示している */
.search-slide input[type="submit"] {
 padding: 0 0 0 6px;
 border: none;
 background: none;
 color: #59443C;
 font-family: FontAwesome;
 cursor: pointer;
 transition: padding var(--sf-speed) ease;
}

/* 閉じている状態
   ・.is-js と .is-closed の両方があるときだけ効く
   ・.is-js は JavaScript が付ける印。JS が動かない環境では付かないので、入力欄は出しっぱなしになる(検索はできる) */
.search-slide.is-js.is-closed input[type="text"] {
 width: 0;
 opacity: 0;
 border-bottom-color: transparent;
 pointer-events: none;
}
.search-slide.is-js.is-closed input[type="submit"] {
 padding-left: 0;   /* 閉じているときは、虫眼鏡を左端ぴったりに置く */
}

/* OS側で「動きを減らす」設定のときは、アニメなしで開閉する */
@media (prefers-reduced-motion: reduce) {
 .search-slide input[type="text"],
 .search-slide input[type="submit"] {
  transition: none;
 }
}
/* ===== ここまで CSS ===== */
</style>

<!-- ===== HTML ===== -->
<form action="/" class="search-slide" method="get" role="search">
 <input id="search-slide-s" name="s" type="text" value="" aria-label="サイト内検索">
 <input type="submit" value="" aria-label="検索" aria-expanded="false" aria-controls="search-slide-s">
</form>

<script>
/* ===== JavaScript =====
   ・閉じている                → 虫眼鏡を押すと開く(送信しない)
   ・開いている・入力なし      → 虫眼鏡を押すと閉じる(送信しない)
   ・開いている・入力あり      → そのまま送信(検索ページへ移動)
   ・読み込み時:入力が空なら閉じた状態、値が入っていれば開いた状態 */
(function () {
 var form = document.querySelector('.search-slide');
 if (!form) { return; }
 var input = form.querySelector('input[type="text"]');
 var btn = form.querySelector('input[type="submit"]');
 if (!input || !btn) { return; }

 /* 開く(focus が true のときだけ、入力欄にカーソルを置く) */
 function openField(focus) {
  form.classList.remove('is-closed');
  btn.setAttribute('aria-expanded', 'true');
  input.tabIndex = 0;
  if (focus) { input.focus(); }
 }

 /* 閉じる(虫眼鏡が左端に戻る) */
 function closeField() {
  form.classList.add('is-closed');
  btn.setAttribute('aria-expanded', 'false');
  input.tabIndex = -1;
 }

 function isEmpty() { return input.value.replace(/\s+/g, '') === ''; }
 function isClosed() { return form.classList.contains('is-closed'); }

 /* 読み込み時の初期状態 */
 form.classList.add('is-js');
 if (isEmpty()) { closeField(); } else { openField(false); }

 /* 虫眼鏡を押したとき(Enter キーでの送信もここを通る) */
 btn.addEventListener('click', function (e) {
  if (isClosed()) {
   e.preventDefault();
   openField(true);
  } else if (isEmpty()) {
   e.preventDefault();
   closeField();
  }
  /* 入力あり → 何もしない(そのまま送信される) */
 });

 /* 念のため:空のまま送信されそうなときは止める */
 form.addEventListener('submit', function (e) {
  if (isEmpty()) {
   e.preventDefault();
   if (!isClosed()) { closeField(); }
  }
 });

 /* Esc キー:入力が空なら閉じて、虫眼鏡にフォーカスを戻す */
 document.addEventListener('keydown', function (e) {
  if (e.key === 'Escape' && !isClosed() && isEmpty()) {
   closeField();
   btn.focus();
  }
 });

 /* フォームの外をクリック:入力が空なら閉じる */
 document.addEventListener('click', function (e) {
  if (!isClosed() && isEmpty() && !form.contains(e.target)) {
   closeField();
  }
 });
})();
</script>

コピーライトの横とかに配置する場合は、文字の上に重なるようにTMLを少し組み替えてCSSを足すだけでできる。JavaScriptは1文字も変えなくていい。

WWW.EXAMPLE.COM | COPYRIGHT 2020 SAMPLE

form を .search-overlay-wrap の div で囲んでその中に文字も入れる。入力欄と虫眼鏡は <span class="search-inner"> で囲む。
※ 外側は <p> ではなく <div>で。form を <p> の中に入れるとブラウザが p を閉じてしまい崩れてしまう。

<div class="search-overlay-wrap">
<form action="/" class="search-slide" method="get" role="search">
 <span class="search-inner">
  <input id="search-slide-s" name="s" type="text" value="" aria-label="サイト内検索">
  <input type="submit" value="" aria-label="検索" aria-expanded="false" aria-controls="search-slide-s">
 </span>
</form>
WWW.EXAMPLE.COM | COPYRIGHT 2020 SAMPLE
</div>

元のCSSに書きを追加。.search-slide の display:flex を上書きするので必ず下に置く。

/* 重なる幅の基準(この幅いっぱいに広がる) */
.search-overlay-wrap { position: relative; display: inline-block; }

.search-slide {
 --sf-icon: 18px;   /* ★ 虫眼鏡の幅(閉じているときに場所をとる分) */
 --sf-bg: #F2F2F2;  /* ★ 下の文字を隠す背景色(ページの背景色に合わせる) */
 display: inline-block;
 vertical-align: middle;
 height: 1.6em;
}
.search-slide .search-inner { display: flex; align-items: center; height: 100%; }
.search-slide input[type="text"] { flex: 1 1 0; min-width: 0; }
.search-slide input[type="submit"] { flex: 0 0 var(--sf-icon); width: var(--sf-icon); padding: 0; }

/* form は虫眼鏡ぶんの場所取りだけ。中身を浮かせて文字の上に重ねる */
.search-slide.is-js { width: var(--sf-icon); }
.search-slide.is-js .search-inner {
 position: absolute; left: 0; top: 0; z-index: 10;
 width: 100%;                      /* 開いたとき:文字を含めた幅いっぱい */
 background: var(--sf-bg);
 transition: width var(--sf-speed) ease;
}
.search-slide.is-js.is-closed .search-inner { width: var(--sf-icon); }

@media (prefers-reduced-motion: reduce) {
 .search-slide.is-js .search-inner { transition: none; }
}
  • form の幅は虫眼鏡ぶん(18px)だけにして、右の文字はその分だけ右にずれる。
  • 中身の .search-inner を position:absolute で浮かせる。
  • 開いたとき .search-inner を width:100% にして、.search-overlay-wrap の幅いっぱいに広げ、文字を隠す。
  • 虫眼鏡は flex の右端に残るので、自然に右端へ移動する。

JSが動かない環境では .is-js が付かない。この時は重ならず、入力欄(160px)が出しっぱなしになり、文字は右に押される。検索はできる。

NOTICES

  • 記事内容は実装させたものがほとんどですが自己責任で参考にしてください。

TO HEADER