Qiitaの狭幅レイアウト時のモーダル検索バーにフォーカスするユーザースクリプト


トップページだとちゃんとfocusされているのに、他では効かずに2個めの画像の罠にひっかかる。

動作検証環境

  • Windows 7
  • Firefox 59
  • Tampermonkey v4.6.5709

コード

Qiita-focus-on-search-modal.user.js
// ==UserScript==
// @name         Qiita focus on search modal
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  検索バーポップアップ時にフォーカスする。(ホーム以外でも)
// @author       khsk
// @match        https://qiita.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';
    const button = document.querySelector('.st-Header_searchButton');
    const input  = document.querySelector('.st-Header_searchModalInput');
    if (!button || !input) {
        console.log('ナイデース');
        return;
    }
    button.addEventListener('click', () => {
        // display none の間はfocusが効かないので、Qiita側のEventで表示されるのを待つ。(予想)
        setTimeout(() => {
            input.focus();
        },0);
    });
})();

コード予想

モーダル自体のHTMLがほぼ共通でこんなの


// 虫眼鏡
<div class="st-Header_searchButton"><span class="fa fa-search"></span></div>

// モーダル
<form class="st-Header_searchModal" action="/search" method="get">
    <input class="st-Header_searchModalInput" autocomplete="off" placeholder="キーワードを入力" name="q" required="" type="text">
</form>

で、トップページのJavaScriptがこれ

 f('div', {
      class: 'st-Header_searchButton',
      onclick: t.openSearchForm
    }, 

// ~~~

 f('input', {
      class: 'st-Header_searchModalInput',
      type: 'text',
      autocomplete: 'off',
      placeholder: r('searchPlaceholder'),
      value: n.searchQuery,
      name: 'q',
      required: !0,
      oninput: function (e) {
        return t.setQuery(e.target.value)
      },
      onupdate: function (e) {
        n.isSearchFormOpen && e.focus()
      }
    }))))
  },

虫眼鏡をクリックすると、openSearchFormisSearchFormOpen!0になって、onupdateが走ってfocusされる、かな。

対して、記事画面がこう

})), f.default.createElement("div", {
                        className: "st-Header_searchButton",
                        onClick: this.openSearchForm
                    }, f.default.createElement("span", {
                        className: "fa fa-search"
                    }))), this.renderHeaderEnd(), f.default.createElement("div", {
                        className: "st-Header_overlay",
                        onClick: function() {
                            t.closeCommunityDropdown(), t.closeHomeDropdown(), t.closeNotification(), t.closeProfileDropdown(), t.closeSearchForm(), t.closeRealmSelector()
                        }
                    }), f.default.createElement("form", {
                        className: "st-Header_searchModal",
                        action: "/search",
                        method: "get"
                    }, f.default.createElement("input", {
                        className: "st-Header_searchModalInput pl-1",
                        type: "text",
                        autoComplete: "off",
                        placeholder: I18n.t("js.views.new_header.searchPlaceholder"),
                        name: "q",
                        required: !0
                    }))))
                }
            }, {

浅学なのでなんとも言えませんが、一見onChangeがないし、
openSearchForm!0にされたisSearchFormOpenfocus()とは絡みそうになさそうなので、環境依存で動かないというよりは実装されてない可能性もあるかも…?

しかしどうしてこう実装が異なるのかな?
記事画面はReactらしいが…とここで

新QiitaでReactをやめてhyperappを採用した背景 - Qiita

でトップページだけhyperapp製になったんだと思いだした。
なので現在は少なくともhyperappとReactが並行稼動しているわけで、見た目がすごく同じでも動きが異なるのにも納得できた。

setTimeoutのわけ(参考)

コメントにある通り、display: 'none'にはfocus()できない説
PHPマニュアルから関数をコピーするボタンを追加するユーザースクリプト - Qiita (node選択もできない)
html - Why changing visibility/display on focus does not work? - Stack Overflow

と、イベント中にfocusイベント起こせない説がある?(こっちはonFocusでfocus()とは無関係?)
【js】firefoxでfocusイベントが動かない - Qiita