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
这个是一个广为人知的英雄。

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

document.querySelector = function(selectors) {
var elements = document.querySelectorAll(selectors);
return (elements.length) ? elements[0] : null;
};
有了document.querySelectorAll自然想到了取第一项。
document.getElementsByClassName

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;
}
}
利用attachEvent和detachEvent这俩IE方案。
[Window, HTMLDocument, Element].forEach(function(o) {
o.prototype.addEventListener = addEventListener;
o.prototype.removeEventListener = removeEventListener;
});
结尾把addEventListener 和removeEventListener注入Window、HTMLDocument、Element。