Focus input text field on Esc

Focus the first visible input text field when you press Esc key, or restore the previously focused element on second press

当前为 2020-04-10 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name          Focus input text field on Esc
// @description   Focus the first visible input text field when you press Esc key, or restore the previously focused element on second press
// @version       1.0.11
// @include       *
// @author        wOxxOm
// @namespace     wOxxOm.scripts
// @license       MIT License
// @run-at        document-start
// @grant         none
// ==/UserScript==

var TEXT_FIELD = ' search text number url textarea ';
var previousElement;
var scrollPos;
var first;

window.addEventListener('keydown', function (e) {
  if (e.defaultPrevented || e.which !== 27 || e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) {
    return;
  }
  if (window !== top) {
    rememberFocus();
    window.addEventListener('message', maybeRestoreFocus);
    top.postMessage(GM_info.script.name, '*');
    e.preventDefault();
    e.stopPropagation();
    return;
  }
  run();
}, true);

if (window === top) {
  window.addEventListener('message', function (e) {
    if (e.data === GM_info.script.name) {
      run({relayedFromFrame: true});
    }
  });
}

function run(params) {
  // find text inputs inside visible DOM containers
  var inputs = [];
  populateInputs(inputs);
  for (var i = 0, input, il = inputs.length; i < il && (input = inputs[i]); i++) {
    var priority = TEXT_FIELD.indexOf(' ' + input.type + ' ');
    if (priority < 0) continue;
    var n = input, style;
    while (n && n.style && (style = getComputedStyle(n)) && style.display !== 'none' && style.visibility !== 'hidden') {
      n = n.parentNode;
    }
    // visible if reached DOM root
    if (n && n.style) continue;
    // set the first OR if it's empty, try to select an identically named input field with some text (happens on some sites)
    if (!first || (
      input.value &&
      input.name === first.name && (
        !input.form && !first.form ||
        input.form && first.form && input.form.action === first.form.action
      )
    )) {
      first = input;
      if (first.value) break;
    }
  }

  if (!first) return;

  var invoke = params && params.relayedFromFrame ? passthru : onkeyup;

  if (first !== getActiveElement()) {
    rememberFocus();
    invoke(setFocus);
  } else if (previousElement) {
    invoke(restoreFocus);
    if (previousElement && previousElement.localName === 'iframe') {
      previousElement.contentWindow.postMessage(GM_info.script.name, '*');
    }
  }
}

function populateInputs(inputs, root) {
  var walker = document.createTreeWalker(root || document, NodeFilter.SHOW_ELEMENT);
  var el;
  while ((el = walker.nextNode())) {
    if (el.shadowRoot)
      populateInputs(inputs, el.shadowRoot);
    if (/^(input|textarea)$/.test(el.localName))
      inputs.push(el);
  }
}

function getActiveElement() {
  var el = document.activeElement;
  while (el) {
    if (!el.shadowRoot)
      return el;
    el = el.shadowRoot.activeElement;
  }
}

function rememberFocus() {
  previousElement = document.activeElement;
  scrollPos = [scrollX, scrollY];
}

function setFocus() {
  first.focus();
}

function restoreFocus() {
  // in case document.body (page "background") was previously selected
  document.activeElement.blur();
  previousElement.focus();
  scrollTo(scrollPos[0], scrollPos[1]);
}

function maybeRestoreFocus(e) {
  if (e.data === GM_info.script.name) {
    restoreFocus();
  }
}

// focusing should be done at key-up to prevent the Esc-keydown being also chain-handled by the just focused element
function onkeyup(cb) {
  window.addEventListener('keyup', function keyup(e) {
    if (e.which !== 27) return;
    window.removeEventListener('keyup', keyup);
    if (e.defaultPrevented) return;
    cb(e);
  });
}

function passthru(fn) {
  return fn.apply(this, arguments);
}