This one is still with dom Related methods , The emphasis is on operation dom Methods .

read Zepto The source code series has been put in github On , welcome star: reading-zepto

Source version

The source code of this article is zepto1.2.0

.remove()

remove: function() {
return this.each(function() {
if (this.parentNode != null)
this.parentNode.removeChild(this)
})
},

Delete the elements in the current collection .

If the parent node exists , The removeChild Method to delete the current element .

Similar method generator

zepto in afterprependbeforeappendinsertAfterinsertBeforeappendTo and prependTo It's all generated through this similar method generator .

Define the container

adjacencyOperators = ['after', 'prepend', 'before', 'append']

First , Defines an array of similar operations , Notice that there are only afterprependbeforeappend The names of these methods , You'll see that in the back , After generating these methods ,insertAfterinsertBeforeappendTo and prependTo Will call the previous generated methods .

Auxiliary method traverseNode

function traverseNode(node, fun) {
fun(node)
for (var i = 0, len = node.childNodes.length; i < len; i++)
traverseNode(node.childNodes[i], fun)
}

This method recursively traverses node Child nodes of , Leave the node to the callback function fun Handle . This auxiliary method will be used later .

Core source code

adjacencyOperators.forEach(function(operator, operatorIndex) {
var inside = operatorIndex % 2 //=> prepend, append $.fn[operator] = function() {
// arguments can be nodes, arrays of nodes, Zepto objects and HTML strings
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 // convert all methods to a "before" operation
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)
}
})
})
})
}

Call mode

Before analysis , Let's look at the usage of these methods first :

after(content)
prepend(content)
before(content)
append(content)

Parameters content It can be for html character string ,dom node , Or an array of nodes .after Is to insert after each set element content , before Just the opposite , Insert... Before each set element content,prepend Is to insert... At the initial position of each set element content, append Is to insert... At the end of each set element content.before and after Inserted content Outside the element , and prepend and append Inserted content Inside the element , This is something that needs attention .

The parameter content convert to node Nodes in the array

var inside = operatorIndex % 2 //=> prepend, append

Traverse adjacencyOperators, Get the corresponding method name operator And the index of the method name in the array operatorIndex.

Defined a inside Variable , When operatorIndex For even when ,inside The value of is true, That is to say operator The value of is prepend or append when ,inside The value of is true . This can be used to distinguish content Methods that are inserted inside or outside the element .

$.fn[operator] That is to say $.fn Object to set the corresponding property value ( Method name ).

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)
}),

Variable argType The type used to hold variables , That is to say content The type of .nodes It's based on content Converted node Nodes in the array .

This is used here. $.map arguments To get parameters content , There is only one parameter , It's not necessary to arguments[0] To get it ? This is because $.map You can flatten the array , See here for the specific implementation 《 read zepto Source of the tool function 》.

First, use the inner function type To get the type of the parameter , About type The implementation of the , stay 《 read Zepto Internal methods of source code 》 It has been analyzed .

If parameters content , That is to say arg When the type of is array , Traverse arg , If the elements in the array exist nodeType attribute , It is concluded that node node , Just put it push Into the container arr in ; If the elements in the array are zepto object ( use $.zepto.isZ Judge , This method has been used in 《 read Zepto The magic of source code $》 There has been an analysis ), Call without parameters get Method , It returns an array , Then we call the array concat Method to merge arrays ,get Method in 《 read Zepto Source set operation 》 There has been an analysis ; otherwise , by html character string , call zepto.fragment Handle , And merge the returned arrays ,``zepto.fragment` stay 《 read Zepto The magic of source code $》 There's been an analysis of .

If the parameter type is object ( That is to say zepto object ) perhaps null , Then return directly .

Otherwise html character string , call zepto.fragment Handle .

parent, copyByClone = this.length > 1
if (nodes.length < 1) return this

It also defines parent Variable , For preservation content The parent of the insert ; When the number of elements in a set is greater than 1 when , Variable copyByClone The value of is true , The function of this variable will be discussed later .

If nodes The quantity ratio of 1 Small , That is, when the node to be inserted is empty , No further processing , return this , So that chain operation can be carried out .

use insertBefore To simulate all operations

return this.each(function(_, target) {
parent = inside ? target : target.parentNode // convert all methods to a "before" operation
target = operatorIndex == 0 ? target.nextSibling :
operatorIndex == 1 ? target.firstChild :
operatorIndex == 2 ? target :
null var parentInDocument = $.contains(document.documentElement, parent)
...
})

On the assembly each Traverse

parent = inside ? target : target.parentNode

If node The node needs to insert the target element target Internal , be parent Set to target element target, Otherwise, set to the parent of the current element .

target = operatorIndex == 0 ? target.nextSibling :
operatorIndex == 1 ? target.firstChild :
operatorIndex == 2 ? target :
null

This section is to use all the operations dom A native method insertBefore To simulate the . If operatorIndex == 0 That is to say after when ,node The node should be inserted into the target element target Behind , namely target In front of the next sibling element of ; When operatorIndex == 1 That is to say prepend when ,node The node should be inserted at the beginning of the target element , namely target In front of the first child element of ; When operatorIndex == 2 That is to say before when ,insertBefore It just corresponds to , It's the element itself . When insertBefore The second parameter of is null when ,insertBefore Will node Insert at the end of the child node , It happens to be with append Corresponding . See the document for details :Node.insertBefore()

var parentInDocument = $.contains(document.documentElement, parent)

call $.contains Method , Detect the parent node parent Whether in document in .$.contains Method in 《 read zepto Source of the tool function 》 It has been analyzed in .

take node The array of nodes is inserted into the element

nodes.forEach(function(node) {
if (copyByClone) node = node.cloneNode(true)
else if (!parent) return $(node).remove() parent.insertBefore(node, target)
...
})

If you need to copy nodes ( That is, the number of set elements is greater than 1 when ), use node Node method cloneNode To copy nodes , Parameters true It means to copy the child nodes and attributes of the node together . Why is the set element larger than 1 You need to copy nodes when you do ? because insertBefore It inserts a reference to the node , Traversal of all elements in a collection , If you don't clone nodes , Each element inserts the same reference , Finally, only the node is inserted into the last element .

If the parent node does not exist , Will node Delete , No further operations .

Use the node as insertBefore Method is inserted into the element .

Handle script The script in the tag

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)
}
})

If the parent element is in document Inside , Call traverseNode To deal with it node Nodes and node All children of a node . Mainly testing node Whether the node or its children are not directed to an external script script label .

el.nodeName != null && el.nodeName.toUpperCase() === 'SCRIPT'

This passage is used to judge whether it is script label , adopt node Of nodeName Whether the property is script To judge .

!el.type || el.type === 'text/javascript'

non-existent type attribute , perhaps type The attribute is 'text/javascript'. This means that only javascript, because type Property is not necessarily specified as text/javascript , Only specified as test/javascript Or empty , Only in accordance with javascript To deal with it . see MDN file script

!el.src

And there are no external scripts .

var target = el.ownerDocument ? el.ownerDocument.defaultView : window

Whether there is ownerDocument attribute ,ownerDocument Returns the root node of the element , That is to say document object ,document Object's defaultView Property returns document Object is associated with window object , This is mainly dealing with iframe Inside script, Because in iframe There are independent window object . If the attribute does not exist , The current window object .

target['eval'].call(target, el.innerHTML)

Last call window Of eval Method , perform script The script in the , Script use el.innerHTML obtain .

Why is it right script The element is treated like this alone ? Because for safety reasons , The script by insertBefore To insert into dom In the middle of the day , The script will not be executed , So we need to use eval To process .

Generate insertAfterprependToinsertBefore and appendTo Method

Let's take a look at how these methods are called

insertAfter(target)
insertBefore(target)
appendTo(target)
prependTo(target)

These methods insert the elements in the collection into the target element target in , Follow afterbeforeappend and prepend It's just the opposite .

Their corresponding relationship is as follows :

after => insertAfter
prepend => prependTo
before => insertBefore
append => appendTo

So you can call the corresponding methods to generate these methods .

$.fn[inside ? operator + 'To' : 'insert' + (operatorIndex ? 'Before' : 'After')] = function(html) {
$(html)[operator](this)
return this
}
inside ? operator + 'To' : 'insert' + (operatorIndex ? 'Before' : 'After')

This is actually the generation method name , If it is prepend or append , It's stitched at the back To , If it is Before or After, It's stitched at the front insert.

$(html)[operator](this)

Simply call the corresponding method backwards , That's all right. .

Here we are , This similar method generator generates afterprependbeforeappendinsertAfterinsertBeforeappendTo and prependTo Wait eight ways , Quite efficient .

.empty()

empty: function() {
return this.each(function() { this.innerHTML = '' })
},

empty The function of is to clear the contents of all collection elements , It's called node Of innerHTML Property is set to null .

.replaceWith()

replaceWith: function(newContent) {
return this.before(newContent).remove()
},

Replace all collection elements with the specified contents newContent , newContent It's the same type as before The type of the parameter is the same .

replaceWidth First call before take newContent Insert in front of the corresponding element , Then delete the element , In this way, we can achieve the goal of replacement .

.wrapAll()

wrapAll: function(structure) {
if (this[0]) {
$(this[0]).before(structure = $(structure))
var children
// drill down to the inmost element
while ((children = structure.children()).length) structure = children.first()
$(structure).append(this)
}
return this
},

Wrap all the elements in the collection into the specified structure structure in .

If the set element exists , namely this[0] There is , Then follow up , Otherwise return to this , For chain operation .

call before Method , Inserts the specified structure before the first collection element , That is, in front of all set elements

while ((children = structure.children()).length) structure = children.first()

lookup structure Child elements , If the subelements exist , Will structure The assignment is structure The first sub element of , Straight to find structrue The first sub element of the deepest layer .

Insert all the elements in the collection into structure At the end of , If structure There are subelements , Insert at the end of the first child element in the deepest layer . This wraps all the elements in the collection into structure Inside .

.wrap()

wrap: function(structure) {
var func = isFunction(structure)
if (this[0] && !func)
var dom = $(structure).get(0),
clone = dom.parentNode || this.length > 1 return this.each(function(index) {
$(this).wrapAll(
func ? structure.call(this, index) :
clone ? dom.cloneNode(true) : dom
)
})
},

Wrap the specified structure on each element in the collection structure,structure It can be a single element or a nested element , It can also be for html Element or dom node , You can also call back functions , The callback function receives the current element and the index of the current element in the collection , Return the qualified package structure .

var func = isFunction(structure)

Judge structure Is it a function

if (this[0] && !func)
var dom = $(structure).get(0),
clone = dom.parentNode || this.length > 1

If the set is not empty , also structure It's not a function , Will structure Convert to node node , adopt $(structure).get(0) To convert , And assign it to the variable dom. If dom Of parentNode The number of existence or set is greater than 1 , be clone The value of is true.

return this.each(function(index) {
$(this).wrapAll(
func ? structure.call(this, index) :
clone ? dom.cloneNode(true) : dom
)
})

The collection is traversed , call wrapAll Method , If structure For the function , The result returned by the callback function is passed as a parameter to wrapAll ;

otherwise , If clone by true , Will dom That is, a copy of the wrapped element is passed to wrapAll , Otherwise directly dom Pass to wrapAll. The reason for passing copies here is the same as in the generator , It's also to avoid dom Reference to node . If dom Of parentNode In existence , indicate dom It belongs to a node , If used directly dom , Will destroy the original structure .

.wrapInner()

wrapInner: function(structure) {
var func = isFunction(structure)
return this.each(function(index) {
var self = $(this),
contents = self.contents(),
dom = func ? structure.call(this, index) : structure
contents.length ? contents.wrapAll(dom) : self.append(dom)
})
},

The content of each element in the collection is structured as specified structure The parcel . structure The parameter type of is similar to wrap equally .

The collection is traversed , call contents Method , Get the content of the element ,contents Method in 《 read Zepto Source set element search 》 There has been an analysis .

If structure For the function , Then assign the result returned by the function to dom , Otherwise, it will directly structure Assign a value to dom.

If contents.length There is , That is, the element is not empty , call wrapAll Method , Wrap the content of the element in dom in ; If it's an empty element , Will directly dom Insert at the end of the element , It has also achieved the goal dom Wrapped inside the element .

.unwrap()

unwrap: function() {
this.parent().each(function() {
$(this).replaceWith($(this).children())
})
return this
},

When the wrapping of all elements in the set is removed , That is to say, remove the parent element , But keep the child elements of the parent element .

The method of implementation is also very simple , Is to traverse the parent element of the current element , Replace the parent with the child of the parent .

.clone()

clone: function() {
return this.map(function() { return this.cloneNode(true) })
},

Each element in each collection creates a copy , And return the replica set to .

Traversing the set of elements , call node The original method of cloneNode Create copy . it is to be noted that ,cloneNode The element's original data and event handlers are not copied to the replica .

Series articles

  1. read Zepto The code structure of the source code
  2. read Zepto Internal methods of source code
  3. read Zepto Source of the tool function
  4. read Zepto The magic of source code $
  5. read Zepto Source set operation
  6. read Zepto Source set element search

Reference resources

License

author : On the other side of the corner

read Zepto Operation of source code DOM More articles about

  1. read Zepto The style operation of source code

    This one is still with dom Related methods , The emphasis is on how to manipulate styles . read Zepto The source code series has been put in github On , welcome star: reading-zepto Source version The source code of this article is zepto1.2. ...

  2. read Zepto The attribute operation of source code

    This one is still with dom Related methods , The focus is on the methods of manipulating properties . read Zepto The source code series has been put in github On , welcome star: reading-zepto Source version The source code of this article is zepto1.2. ...

  3. read Zepto Source code Event modular

    Event The module is Zepto One of the essential modules , Due to Event Api Not quite ripe. ,Event The objects are also quite complicated , So at first glance Event Module source code , mentally , Take a closer look , It's not too complicated . read Zepto Source code ...

  4. read Zepto Source code Callbacks modular

    Callbacks Modules are not required , Its role is to manage callback functions , by Defferred Modules provide support ,Defferred The module is Ajax Modular promise Style provides support , It will soon be analyzed that ...

  5. read Zepto Source code Deferred modular

    Deferred Modules are not required modules , however ajax Module , Want to use promise style , It is necessary to introduce Deferred modular .Deferred Also used in the last article < read Zepto Source code Callb ...

  6. read Zepto Source code Ajax modular

    Ajax Modules are also commonly used modules ,Ajax The module contains jsonp Reality , and XMLHttpRequest Encapsulation . read Zepto The source code series has been put in github On , welcome star: rea ...

  7. read Zepto Source code Selector modular

    Selector The module is right Zepto The extension of the selector , bring Zepto Selectors can also support partial CSS3 Selectors and eq etc. Zepto Defined selector . Before reading this article , It's better to read first < read Zept ...

  8. read Zepto Source code Touch modular

    Everybody knows , For historical reasons , Click events on the mobile end will have 300ms Left right delay ,Zepto Of touch The module solves the problem of click delay on the mobile end , It also provides sliding swipe event . read Zepto Source code ...

  9. read Zepto Source code Gesture modular

    Gesture The module is based on IOS Upper Gesture Encapsulation of events , utilize scale attribute , To encapsulate pinch A series of events . read Zepto The source code series has been put in github On , welcome star: rea ...

Random recommendation

  1. partnerv2.1

    2.1. Get product prices in real time (action=queryprice) request { "useDate": "2016-04-05T19:56", // Starting time ( Local time ...

  2. Linux The server didn't respond when it was turned on ,BIOS There's no information

    On 2015-10-16, Remember it was 4 Server installed in the month , above ineedle All deployed , It didn't come in handy at the time , This Huawei test needs a ineedle test machine , So I installed this ineedle Please come out , Plug in the power , Connect the network cable , ...

  3. Servlet Refresh to another page regularly response.setHeader(&quot;refresh&quot;, &quot;3;url=/...&quot;)

    Want to achieve , visit Responsedemo11 When ,3 Seconds later , Jump to ResponseDemo10 use   response.setHeader("refresh", "3; ...

  4. iOS The original map and the gaude map

    Native maps 1. What is? LBS LBS: Location based services Location Based Service The practical application : Public comment , Mo Mo , WeChat , Meituan and others need to use maps or positioning App 2. Positioning mode 1.GPS location 2. ...

  5. [python] PyMouse、PyKeyboard use python Operate the mouse and keyboard

      1.PyUserInput brief introduction PyUserInput It's a use python Cross platform operation of the mouse and keyboard module , Very easy to use . The supported platforms and dependencies are as follows : Linux - Xlib Mac - Quart ...

  6. dubbo Monitoring center and admin Manage the use of projects

    Monitoring center and admin Management projects are monitored for specific registries , So you need to configure the address of the corresponding registry , Or in dubbo.properties Or in applications.properties File configuration . == ...

  7. burp Grab ios equipment https Data packets

    Refer to other related online tutorials , I tried it myself , The effective method can be determined as : 1. Give Way PC The mobile terminal and the mobile terminal are in the same LAN , 2.burp Set to listen to all interfaces , And listen to a port 3. Set the agent on the mobile terminal , The mode is manual ,ip Address filling PC In the local area ...

  8. 【NOI2018 simulation 5】 Triangulation Bsh

    [NOI2018 simulation 5] Triangulation Bsh Description Given a positive n Edges and their triangulation , common 2n - 3 side (n The edges of a polygon and n-3 Diagonals ), The length of each side is 1. common q Time to ask , Every time ...

  9. [ long-range ] windows 2008 server Set up a shared folder , And shared it with everyone, But still can't access , How to solve it ?

    Another place needs to be set up , Add users to MSAppAccess In this group

  10. JS Achieve copy to the clipboard ( compatible FF/Chrome/Safari All browsers )

    Now there are more and more kinds of browsers , Such as  IE.Firefox.Chrome.Safari wait , So now it's time to achieve a js The small function of copying content to the clipboard is not so easy . stay FLASH 9  Time , There is a way to kill all Liu ...