Based on our understanding of the concept, we will analyze the working principle of element.html () from the source code. Why can't innerHTML execute scripts while jQuery's html () does, zepto. javaScript can only execute inline scripts, but cannot load external scripts.
We use the source code of jQuery 3.1.0 and Zepto. js 1.2.0 for analysis.
JQuery Github
Zepto. js Github
JQuery
First, let's look at the main portal of the html () method:
01
// JQuery/src/manipulation. js
02
03
Html: function (value ){
04
Return access (this, function (value ){
05
Var elem = this [0] | | {},
06
I = 0,
07
L = this. length;
08
09
If (value = undefined & elem. nodeType = 1 ){
10
Return elem. innerHTML;
11
}
12
13
// See if we can take a shortcut and just use innerHTML
14
If (typeof value = "string "&&! RnoInnerhtml. test (value )&&
15
! WrapMap [(rtagName.exe c (value) | ["", ""]) [1]. toLowerCase ()]) {
16
17
Value = jQuery.html Prefilter (value );
18
19
Try {
20
For (; I <l; I ++ ){
21
Elem = this [I] || {};
22
23
// Remove element nodes and prevent memory leaks
24
If (elem. nodeType = 1 ){
25
JQuery. cleanData (getAll (elem, false ));
26
Elem. innerHTML = value;
27
}
28
}
29
30
Elem = 0;
31
32
// If using innerHTML throws an exception, use the fallback method
33
} Catch (e ){}
34
}
35
36
If (elem ){
37
This. empty (). append (value );
38
}
39
}, Null, value, arguments. length );
40
},
The html () method returns a closure function, which is a set/get multi-function.
Line 9th-11 indicates that the content in the current element is directly returned when element.html () is called directly without adding any parameters.
The rnoInnerhtml of row 3 is defined earlier. rnoInnerhtml =/<script | <style | <link/I, used to match strings without the <script>, <style>, and <link> labels
The 15th-row wrapMap is used to handle compatibility issues with browsers earlier than IE 9. For details, see The jQuery/src/manipulation/wrapMap. js file. This function is not in the scope of this discussion.
The specified tag is not matched. The code is transferred to lines 17-30. This code first filters out the HTML code (/<(?! Area | br | col | embed | hr | img | input | link | meta | param) ([a-z] [^ \/\ 0> \ x20 \ t \ r \ n \ f] *) [^>] *) \/>/gi ), then, clear the content of each element and the bound event, and add the content with innerHTML. If no exception is caught, assign the elem variable to 0 to skip the code block in lines 36th-38.
Okay, front ?? Are you sure you want? Why? What do lazy people ask? Evil? After <script>, <style>, and <link>, empty () is executed on the current element, and then append () is used for subsequent operations.
Check the implementation of the empty () method and find that the binding event of the current element is removed directly, the memory is released, and all nodes in the element are deleted:
01
// JQuery/src/manipulation. js
02
03
Empty: function (){
04
Var elem,
05
I = 0;
06
07
For (; (elem = this [I])! = Null; I ++ ){
08
If (elem. nodeType = 1 ){
09
10
// Prevent memory leaks
11
JQuery. cleanData (getAll (elem, false ));
12
13
// Remove any remaining nodes
14
Elem. textContent = "";
15
}
16
}
17
18
Return this;
19
},
Next we turn to the append () method:
01
// JQuery/src/manipulation. js
02
03
Append: function (){
04
Return domManip (this, arguments, function (elem ){
05
If (this. nodeType = 1 | this. nodeType = 11 | this. nodeType = 9 ){
06
Var target = manipulationTarget (this, elem );
07
Target. appendChild (elem );
08
}
09
});
10
},
Show source
The append () method is too short to be harmless to humans and animals, but it calls the domManip () function. It seems malicious to post this function on a blog because it is a little long. & Uarr; refers to the things folded above. Do not open it easily (fog
In fact, we do not need to analyze the append () method or domManip () function line by line, because we see what we are really interested in. Well, paste it below:
01
// JQuery/src/manipulation. js
02
If (hasScripts ){
03
Doc = scripts [scripts. length-1]. ownerDocument;
04
05
// Reenable scripts
06
JQuery. map (scripts, restoreScript );
07
08
// Evaluate executable scripts on first document insertion
09
For (I = 0; I 10
Node = scripts [I];
11
If (rscriptType. test (node. type | "")&&
12
! Datatev. access (node, "globalEval ")&&
13
JQuery. contains (doc, node )){
14
15
If (node. src ){
16
17
// Optional AJAX dependency, but won't run scripts if not present
18
If (jQuery. _ evalUrl ){
19
JQuery. _ evalUrl (node. src );
20
}
21
} Else {
22
DOMEval (node. textContent. replace (rcleanScript, ""), doc );
23
}
24
}
25
}
26
}
After making a simple judgment on lines 12 to 12, we finally took the following exciting steps to execute the script:
If the tag does not contain the src attribute, pass the pure script for tag removal to the DOMEval () method for execution, as shown below.
Create a new script element, write the pure script to this element, and add it to the end of
01
// JQuery/src/core/DOMEval. js
02
03
Function DOMEval (code, doc ){
04
Doc = doc | document;
05
06
Var script = doc. createElement ("script ");
07
08
Script. text = code;
09
Doc. head. appendChild (script). parentNode. removeChild (script );
10
}
If the tag contains the src attribute, first determine whether the jQuery. _ evalUrl function exists. If it exists, call it. In other words, jQuery. ajax is called again.
01
// JQuery/src/manipulation/_ evalUrl. js
02
03
JQuery. _ evalUrl = function (url ){
04
Return jQuery. ajax ({
05
Url: url,
06
07
// Make this explicit, since user can override this through ajaxSetup (#11264)
08
Type: "GET ",
09
DataType: "script ",
10
Cache: true,
11
Async: false,
12
Global: false,
13
"Throws": true
14
});
15
};
JQuery has been called. ajax, so this exploration of jQuery's html () method is over, because jQuery. when dataType is set to script and jsonp, ajax can request and execute JavaScript files across domains.
Zepto. js
Next, let's take a look at how Zepto. js of the lightweight (suoshui) works.
I can't see a semicolon in the whole code. Is that how you reduce the size? I am so tired of formatting a code, I don't know how you work... (? ° Port ° )? (Accept-encoding
Still find the main html () portal:
01
// Zepto. js/src/zepto. js
02
03
Html: function (html ){
04
Return 0 in arguments?
05
This. each (function (idx ){
06
Var originHtml = this. innerHTML
07
$ (This). empty (). append (funcArg (this, html, idx, originHtml ))
08
}):
09
(0 in this? This [0]. innerHTML: null)
10
}
Compared with jQuery's html () method, this is actually shrinking.
Check row 4th. It uses 0 in arguments to determine whether a parameter is passed in. After performance testing, this method has better performance than arguments when the array length is large. length is better, but after all, this only determines whether the array is null and does not calculate the length of the specific body. It is also appropriate to have better performance.
If no parameter is input, jump to the 9th line and get the content directly through innerHTML, consistent with jQuery. Otherwise, empty () is called before append () for subsequent operations.
You don't need to care about the funcArg () function of row 3, because Zepto. js allows parameters of the html () method to be a function. The Function only calls the parameter when the parameter is a function. If the parameter is not a function, the current string is directly returned:
1
// Zepto. js/src/zepto. js
2
3
Function funcArg (context, arg, idx, payload ){
4
Return isFunction (arg )? Arg. call (context, idx, payload): arg
5
}
The append () implementation uses a multi-function that integrates the "after", "prepend", "before", and "append" functions:
01
// Zepto. js/src/zepto. js
02
03
// Generate the 'after', 'prepend', 'before', 'append ',
04
// 'Insertafter ', 'insertbefore', 'appendto', and 'prependto' methods.
05
AdjacencyOperators. forEach (function (operator, operatorIndex ){
06
Var inside = operatorIndex % 2 // => prepend, append
07
08
$. Fn [operator] = function (){
09
// Arguments can be nodes, arrays of nodes, Zepto objects and HTML strings
10
Var argType, nodes = $. map (arguments, function (arg ){
11
Var arr = []
12
ArgType = type (arg)
13
If (argType = "array "){
14
Arg. forEach (function (el ){
15
If (el. nodeType! = Undefined) return arr. push (el)
16
Else if ($. zepto. isZ (el) return arr = arr. concat (el. get ())
17
Arr = arr. concat (zepto. fragment (el ))
18
})
19
Return arr
20
}
21
Return argType = "object" | arg = null?
22
Arg: zepto. fragment (arg)
23
}),
24
Parent, copyByClone = this. length> 1
25
If (nodes. length <1) return this
26
27
Return this. each (function (_, target ){
28
Parent = inside? Target: target. parentNode
29
30
// Convert all methods to a "before" operation
31
Target = operatorIndex = 0? Target. nextSibling:
32
OperatorIndex = 1? Target. firstChild:
33
OperatorIndex = 2? Target:
34
Null
35
36
Var parentInDocument = pai.contains(document.doc umentElement, parent)
37
38
Nodes. forEach (function (node ){
39
If (copyByClone) node = node. cloneNode (true)
40
Else if (! Parent) return $ (node). remove ()
41
42
Parent. insertBefore (node, target)
43
If (parentInDocument) traverseNode (node, function (el ){
44
If (el. nodeName! = Null & el. nodeName. toUpperCase () === 'script '&&
45
(! El. type | el. type ==== 'text/javascript ')&&! El. src ){
46
Var target = el. ownerDocument? El. ownerDocument. defaultView: window
47
Target ['eval']. call (target, el. innerHTML)
48
}
49
})
50
})
51
})
52
}
53
54
// After => insertAfter
55
// Prepend => prependTo
56
// Before => insertBefore
57
// Append => appendTo
58
$. Fn [inside? Operator + 'to': 'insert' + (operatorIndex? 'Before': 'after')] = function (html ){
59
$ (Html) [operator] (this)
60
Return this
61
}
62
})
This is the hot chicken code above. You cannot see a semicolon... I'm also a server. I really admire it...
Well, this code is not very nice, that is, the last 44-48 lines of content, is the focus !!
The <script> label does not have the src attribute !!! Why? So there is a src attribute, so there is no response, right...
Therefore, the inline script directly calls the eval () function to execute...