我想覆盖JavaScript中所有HTML元素的函数focus()

我怎么做?

最佳答案

该问题仅要求如何为HTML元素覆盖.focus()方法,因此将首先对其进行回答。但是,还有其他触发“关注”元素的方法,下面讨论了覆盖这些方法的方法。

另请注意,覆盖全局原型(prototype)可能不是一个好主意。

在HTML元素上覆盖.focus()方法

您可以通过修改其原型(prototype)来覆盖所有HTMLElement的focus函数。

HTMLElement.prototype.focus = function () {
  // ... do your custom stuff here
}

如果仍然要运行默认的焦点行为并运行自定义替代,则可以执行以下操作:
let oldHTMLFocus = HTMLElement.prototype.focus;
HTMLElement.prototype.focus = function () {
    // your custom code
    oldHTMLFocus.apply(this, arguments);
};

通过HTML元素的处理程序覆盖焦点事件集

以类似于覆盖.focus()方法的方式完成此操作。但是,在这种情况下,我们将针对EventTarget构造函数并修改addEventListener原型(prototype):
let oldAddListener = HTMLElement.prototype.addEventListener;
EventTarget.prototype.addEventListener = function () {
    let scope = this;
    let func = arguments[1];

    if (arguments[0] === 'focus') {
        arguments[1] = function (e) {
            yourCustomFunction(scope);
            func(e);
        };
    }

    oldAddListener.apply(this, arguments);
};

如果您根本不想要原始行为,则可以删除func调用(func(e))。

覆盖通过onfocus属性设置的焦点事件

这样做实际上是很棘手的,并且很可能会有一些无法预见的局限性。就是说,我找不到通过修改原型(prototype)等方法覆盖此方法的唯一方法。我可以通过MutationObservers来完成这项工作的唯一方法。它通过查找所有具有onfocus属性的元素来工作,并将第一个函数设置为运行您的覆盖函数:
let observer = new MutationObserver(handleOnFocusElements);
let observerConfig = {
    attributes: true,
    childList: true,
    subtree: true,
    attributeFilter: ['onfocus']
};
let targetNode = document.body;

observer.observe(targetNode, observerConfig);

function handleOnFocusElements() {
    Array
        .from(document.querySelectorAll('[onfocus]:not([onfocus*="yourCustomFunction"])'))
        .forEach(element => {
            let currentCallbacks = element.getAttribute('onfocus');
            element.setAttribute('onfocus', `yourCustomFunction(this); return; ${currentCallbacks}`);
        });
}

如果要停止原始onfocus完全触发其事件,则可以在任何突变更改时完全清空onfocus属性,并将值设置为所需的功能。

所有代码段都放在一起的示例:

(function() {

  window.yourCustomFunction = function(target) {
    console.log('OVERRIDE on element', target);
  };

  let observer = new MutationObserver(handleOnFocusElements);

  let observerConfig = {
    attributes: true,
    childList: true,
    subtree: true,
    attributeFilter: ['onfocus']
  };

  let targetNode = document.body;

  // Start overriding methods
  // The HTML `.focus()` method
  let oldHTMLFocus = HTMLElement.prototype.focus;
  HTMLElement.prototype.focus = function() {
    yourCustomFunction(this);
    oldHTMLFocus.apply(this, arguments);
  };

  // Event Target's .addEventListener prototype
  let oldAddListener = HTMLElement.prototype.addEventListener;
  EventTarget.prototype.addEventListener = function() {
    let scope = this;
    let func = arguments[1];

    if (arguments[0] === 'focus') {
      arguments[1] = function(e) {
        yourCustomFunction(scope);
        func(e);
      };
    }

    oldAddListener.apply(this, arguments);
  };

  // Handle the onfocus attributes
  function handleOnFocusElements() {
    Array
      .from(document.querySelectorAll('[onfocus]:not([onfocus*="yourCustomFunction"])'))
      .forEach(element => {
        let currentCallbacks = element.getAttribute('onfocus');
        element.setAttribute('onfocus', `yourCustomFunction(this); return; ${currentCallbacks}`);
      });
  }

  window.addEventListener('load', () => {
    handleOnFocusElements();
    observer.observe(targetNode, observerConfig);
  });
})();

let input = document.querySelector('input');

input.addEventListener('focus', function() {
  console.log('Add Event Listener Focus');
});

function attributeHandler() {
  console.log('Called From the Attribute Handler');
}
<input type="text" onfocus="attributeHandler()">

07-24 09:39
查看更多