polyfill源码阅读(四)cssom和dom一

2019-04-22  本文已影响0人  Atlas_lili

cssom

Element.getBoundingClientRect()方法返回元素的大小及其相对于视口的位置。

// rect 是一个具有四个属性left、top、right、bottom的DOMRect对象
//译者注:DOMRect 是 TextRectangle或 ClientRect 的别称,他们是相同的。var rect = obj.getBoundingClientRect();

// Fix for IE8-'s Element.getBoundingClientRect()
  if ('TextRectangle' in global && !('width' in global.TextRectangle.prototype)) {
    Object.defineProperties(global.TextRectangle.prototype, {
      width: { get: function() { return this.right - this.left; } },
      height: { get: function() { return this.bottom - this.top; } }
    });
  }

填了两个宽高属性。

dom

document.querySelectorAll

这个是一个广为人知的英雄。


兼容性.png
document.querySelectorAll = function(selectors) {
      var style = document.createElement('style'), elements = [], element;
      document.documentElement.firstChild.appendChild(style);
      document._qsa = [];
      style.styleSheet.cssText = selectors + '{x-qsa:expression(document._qsa && document._qsa.push(this))}';
      window.scrollBy(0, 0);
      style.parentNode.removeChild(style);

      while (document._qsa.length) {
        element = document._qsa.shift();
        element.style.removeAttribute('x-qsa');
        elements.push(element);
      }
      document._qsa = null;
      return elements;
    };

利用了expression表达式存入document._qsa数组(因为在expression表达式可以操作),再转入elements数组,去掉刚创建的style标签,去掉自定义属性x-qsa。

document.querySelector
兼容性.png
document.querySelector = function(selectors) {
      var elements = document.querySelectorAll(selectors);
      return (elements.length) ? elements[0] : null;
};

有了document.querySelectorAll自然想到了取第一项。

document.getElementsByClassName
兼容性.png
document.getElementsByClassName = function(classNames) {
      classNames = String(classNames).replace(/^|\s+/g, '.');
      return document.querySelectorAll(classNames);
    };

正则匹配在classname前加.,利用document.querySelectorAll匹配。

nodeType
[
    ['ELEMENT_NODE', 1],
    ['ATTRIBUTE_NODE', 2],
    ['TEXT_NODE', 3],
    ['CDATA_SECTION_NODE', 4],
    ['ENTITY_REFERENCE_NODE', 5],
    ['ENTITY_NODE', 6],
    ['PROCESSING_INSTRUCTION_NODE', 7],
    ['COMMENT_NODE', 8],
    ['DOCUMENT_NODE', 9],
    ['DOCUMENT_TYPE_NODE', 10],
    ['DOCUMENT_FRAGMENT_NODE', 11],
    ['NOTATION_NODE', 12]
  ].forEach(function(p) { if (!(p[0] in global.Node)) global.Node[p[0]] = p[1]; });

像上一篇注入readyState一样,注入了nodeType。

DOMException
[
    ['INDEX_SIZE_ERR', 1],
    ['DOMSTRING_SIZE_ERR', 2],
    ['HIERARCHY_REQUEST_ERR', 3],
    ['WRONG_DOCUMENT_ERR', 4],
    ['INVALID_CHARACTER_ERR', 5],
    ['NO_DATA_ALLOWED_ERR', 6],
    ['NO_MODIFICATION_ALLOWED_ERR', 7],
    ['NOT_FOUND_ERR', 8],
    ['NOT_SUPPORTED_ERR', 9],
    ['INUSE_ATTRIBUTE_ERR', 10],
    ['INVALID_STATE_ERR', 11],
    ['SYNTAX_ERR', 12],
    ['INVALID_MODIFICATION_ERR', 13],
    ['NAMESPACE_ERR', 14],
    ['INVALID_ACCESS_ERR', 15]
  ].forEach(function(p) { if (!(p[0] in global.DOMException)) global.DOMException[p[0]] = p[1]; });
Event

注入了异常事件列表。

    Event.CAPTURING_PHASE = 1;
    Event.AT_TARGET  = 2;
    Event.BUBBLING_PHASE  = 3;

    Object.defineProperties(Event.prototype, {
      CAPTURING_PHASE: { get: function() { return 1; } },
      AT_TARGET:       { get: function() { return 2; } },
      BUBBLING_PHASE:   { get: function() { return 3; } },
      target: {
        get: function() {
          return this.srcElement;
        }},
      currentTarget: {
        get: function() {
          return this._currentTarget;
        }},
      eventPhase: {
        get: function() {
          return (this.srcElement === this.currentTarget) ? Event.AT_TARGET : Event.BUBBLING_PHASE;
        }},
      bubbles: {
        get: function() {
          switch (this.type) {
          case 'click':
            // 各种事件类型
            return true;
          }
          return false;
        }},
      cancelable: {
        get: function() {
          switch (this.type) {
          case 'click':
            // 各种事件类型
            return true;
          }
          return false;
        }}
    });
currentTarget和target

currentTarget指的是事件触发后,冒泡到绑定处理程序的元素,就是绑定事件处理程序的元素,target指的是触发事件的元素。

eventPhase

指示事件流正在处理哪个阶段。

bubbles和cancelable

前者用来表示该事件是否在DOM中冒泡。
后者用来表示这个事件是否可以取消。
主要判断事件类型是自定义事件还是原生的。

Object.defineProperties(Event.prototype, {
     timeStamp: {
        get: function() {
          return this._timeStamp;
        }},
      stopPropagation: {
        value: function() {
          this.cancelBubble = true;
        }},
      preventDefault: {
        value: function() {
          this.returnValue = false;
        }},
      defaultPrevented: {
        get: function() {
          return this.returnValue === false;
        }}
    });
timeStamp

事件创建时的时间戳,毫秒级别。按照规定,这个时间戳是距离某个特定时刻的差值,但实际上在浏览器中此处的事件戳的定义有所不同。

另外三个方法写法就是IE的兼容方案。
addEventListener和removeEventListener
function addEventListener(type, listener, useCapture) {
      if (typeof listener !== 'function') return;
      if (type === 'DOMContentLoaded') type = 'load';
      var target = this;
      var f = function(e) {
        e._timeStamp = Date.now();
        e._currentTarget = target;
        listener.call(this, e);
        e._currentTarget = null;
      };
      this['_' + type + listener] = f;
      this.attachEvent('on' + type, f);
    }

    function removeEventListener(type, listener, useCapture) {
      if (typeof listener !== 'function') return;
      if (type === 'DOMContentLoaded') type = 'load';
      var f = this['_' + type + listener];
      if (f) {
        this.detachEvent('on' + type, f);
        this['_' + type + listener] = null;
      }
    }

利用attachEventdetachEvent这俩IE方案。


[Window, HTMLDocument, Element].forEach(function(o) {
      o.prototype.addEventListener = addEventListener;
      o.prototype.removeEventListener = removeEventListener;
    });

结尾把addEventListener 和removeEventListener注入Window、HTMLDocument、Element。

上一篇 下一篇

猜你喜欢

热点阅读