- Mô-đun này xác định cấu trúc chuỗi nguyên mẫu của thư viện, tạo biến Zepto và đăng ký nó vào window với tên 'Zepto' và '$', sau đó bắt đầu triển khai các mô-đun khác.
- Bên trong mô-đun, ngoài việc thực hiện chọn lọc và đối tượng zepto, còn có nhiều phương thức công cụ và phương thức nguyên mẫu được định nghĩa.
- Đáng chú ý là, nhiều thực hiện bên trong sử dụng các phương thức mảng gốc, và nhiều API cũng được mở rộng dựa trên các phương thức nội bộ hoặc công khai.
Nội dung thực hiện
var Zepto = (function () {
// 1. Định nghĩa biến cơ bản
// 2. Thực hiện phương thức nội bộ
// 3. Thực hiện đối tượng zepto - $('chọn lọc')
// 4. Thực hiện phương thức $.extend
// 5. Phương thức nội bộ zepto.qsa và 5 phương thức nội bộ khác
// 6. Thực hiện phương thức toàn cục - $.phương_thức
// 7. Thực hiện phương thức nguyên mẫu - $().phương_thức
// 8. Thiết lập cấu trúc chuỗi nguyên mẫu
}();
- Trên đây là nội dung thực hiện chính, thứ tự định nghĩa mã tương tự như trên, một số nơi sẽ xen kẽ định nghĩa.
Phân tích chi tiết
Chọn lọc
Logic thực hiện chọn lọc được đặt trong phương thức zepto.init, được gọi từ $() nội bộ, xử lý chọn lọc để tạo bộ dom, sau đó chuyển bộ này vào phương thức nội bộ tạo đối tượng zepto, logic chính như sau:
zepto.init = function(selector, context) {
var dom
if (!selector) return zepto.Z()
else if (typeof selector == 'string') {
selector = selector.trim()
if (selector[0] == '<' && fragmentRE.test(selector))
dom = zepto.fragment(selector, RegExp.$1, context), selector = null
else if (context !== undefined) return $(context).find(selector)
else dom = zepto.qsa(document, selector)
}
else if (isFunction(selector)) return $(document).ready(selector)
else if (zepto.isZ(selector)) return selector
else {
if (isArray(selector)) dom = compact(selector)
else if (isObject(selector))
dom = [selector], selector = null
else if (fragmentRE.test(selector))
dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null
else if (context !== undefined) return $(context).find(selector)
else dom = zepto.qsa(document, selector)
}
return zepto.Z(dom, selector)
}
// Xử lý chọn lọc CSS
zepto.qsa = function(element, selector){
var found,
maybeID = selector[0] == '#',
maybeClass = !maybeID && selector[0] == '.',
nameOnly = maybeID || maybeClass ? selector.slice(1) : selector,
isSimple = simpleSelectorRE.test(nameOnly)
return (element.getElementById && isSimple && maybeID) ?
( (found = element.getElementById(nameOnly)) ? [found] : [] ) :
(element.nodeType !== 1 && element.nodeType !== 9 && element.nodeType !== 11) ? [] :
slice.call(
isSimple && !maybeID && element.getElementsByClassName ?
maybeClass ? element.getElementsByClassName(nameOnly) :
element.getElementsByTagName(selector) :
element.querySelectorAll(selector)
)
}
// Xử lý chuỗi thẻ
zepto.fragment = function(html, name, properties) {
var dom, nodes, container
if (singleTagRE.test(html)) dom = $(document.createElement(RegExp.$1))
if (!dom) {
if (html.replace) html = html.replace(tagExpanderRE, "<$1></$2>")
if (name === undefined) name = fragmentRE.test(html) && RegExp.$1
if (!(name in containers)) name = '*'
container = containers[name]
container.innerHTML = '' + html
dom = $.each(slice.call(container.childNodes), function(){
container.removeChild(this)
})
}
if (isPlainObject(properties)) {
nodes = $(dom)
$.each(properties, function(key, value) {
if (methodAttributes.indexOf(key) > -1) nodes[key](value)
else nodes.attr(key, value)
})
}
return dom
}
Đối tượng Zepto
Đối tượng Zepto được tạo ra thông qua một loạt các phương thức nội bộ liên quan, mã nguồn thực hiện như sau:
function Z(dom, selector) {
var i, len = dom ? dom.length : 0
for (i = 0; i < len; i++) this[i] = dom[i]
this.length = len
this.selector = selector || ''
}
zepto.Z = function(dom, selector) {
return new Z(dom, selector)
}
zepto.isZ = function(object) {
return object instanceof zepto.Z
}
zepto.init = function(selector, context) {
return zepto.Z(dom, selector)
}
$ = function(selector, context){
return zepto.init(selector, context)
}
$.fn = {}
zepto.Z.prototype = Z.prototype = $.fn
Một số phương thức API
- Có quá nhiều phương thức công cụ và nguyên mẫu, chỉ chọn một số điển hình.
- Thực hiện của extend và ready khá đơn giản.
- Phương thức css chủ yếu dựa trên element.style và getComputedStyle, xem xét việc chuyển đổi tên thuộc tính và tự động thêm đơn vị kiểu.
$.extend
function extend(target, source, deep) {
for (key in source)
if (deep && (isPlainObject(source[key]) || isArray(source[key]))) {
if (isPlainObject(source[key]) && !isPlainObject(target[key]))
target[key] = {}
if (isArray(source[key]) && !isArray(target[key]))
target[key] = []
extend(target[key], source[key], deep)
}
else if (source[key] !== undefined) target[key] = source[key]
}
$.extend = function(target){
var deep, args = slice.call(arguments, 1)
if (typeof target == 'boolean') {
deep = target
target = args.shift()
}
args.forEach(function(arg){ extend(target, arg, deep) })
return target
}
$.fn.ready
$.fn = {
ready: function(callback){
if (readyRE.test(document.readyState) && document.body) callback($)
else document.addEventListener('DOMContentLoaded', function(){ callback($) }, false)
return this
}
}
$.fn.css
$.fn = {
css: function(property, value){
if (arguments.length < 2) {
var element = this[0]
if (typeof property == 'string') {
if (!element) return
return element.style[camelize(property)] || getComputedStyle(element, '').getPropertyValue(property)
} else if (isArray(property)) {
if (!element) return
var props = {}
var computedStyle = getComputedStyle(element, '')
$.each(property, function(_, prop){
props[prop] = (element.style[camelize(prop)] || computedStyle.getPropertyValue(prop))
})
return props
}
}
var css = ''
if (type(property) == 'string') {
if (!value && value !== 0)
this.each(function(){ this.style.removeProperty(dasherize(property)) })
else
css = dasherize(property) + ":" + maybeAddPx(property, value)
} else {
for (key in property)
if (!property[key] && property[key] !== 0)
this.each(function(){ this.style.removeProperty(dasherize(key)) })
else
css += dasherize(key) + ':' + maybeAddPx(key, property[key]) + ';'
}
return this.each(function(){ this.style.cssText += ';' + css })
}
}
$.fn.insert
$.fn = {
traverseNode: function(node, fun) {
fun(node)
for (var i = 0, len = node.childNodes.length; i < len; i++)
traverseNode(node.childNodes[i], fun)
}
adjacencyOperators.forEach(function(operator, operatorIndex) {
var inside = operatorIndex % 2
$.fn[operator] = function(){
var argType, nodes = $.map(arguments, function(arg) {
var arr = []
argType = type(arg)
if (argType == "array") {
arg.forEach(function(el) {
if (el.nodeType !== undefined) return arr.push(el)
else if ($.zepto.isZ(el)) return arr = arr.concat(el.get())
arr = arr.concat(zepto.fragment(el))
})
return arr
}
return argType == "object" || arg == null ?
arg : zepto.fragment(arg)
}),
parent, copyByClone = this.length > 1
if (nodes.length < 1) return this
return this.each(function(_, target){
parent = inside ? target : target.parentNode
target = operatorIndex == 0 ? target.nextSibling :
operatorIndex == 1 ? target.firstChild :
operatorIndex == 2 ? target :
null
var parentInDocument = $.contains(document.documentElement, parent)
nodes.forEach(function(node){
if (copyByClone) node = node.cloneNode(true)
else if (!parent) return $(node).remove()
parent.insertBefore(node, target)
if (parentInDocument) traverseNode(node, function(el){
if (el.nodeName != null && el.nodeName.toUpperCase() === 'SCRIPT' &&
(!el.type || el.type === 'text/javascript') && !el.src){
var target = el.ownerDocument ? el.ownerDocument.defaultView : window
target['eval'].call(target, el.innerHTML)
}
})
})
})
}
$.fn[inside ? operator+'To' : 'insert'+(operatorIndex ? 'Before' : 'After')] = function(html){
$(html)[operator](this)
return this
}
})
}