Skip to content

Commit ee6e874

Browse files
authored
Manipulation: Extract domManip to a separate file
We've already had `buildFragment` extracted to a separate file long ago. `domManip` is quite a complex & crucial API and so far it has existed within the `manipulation.js` module. Extracting it makes the module shorter and easier to understand. A few comments / messages in tests have also been updated to not suggest there's a public `jQuery.domManip` API - it's been private since 3.0.0. Closes gh-5138
1 parent aa231cd commit ee6e874

File tree

3 files changed

+113
-107
lines changed

3 files changed

+113
-107
lines changed

src/manipulation.js

Lines changed: 1 addition & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
import jQuery from "./core.js";
22
import isAttached from "./core/isAttached.js";
3-
import flat from "./var/flat.js";
43
import isIE from "./var/isIE.js";
54
import push from "./var/push.js";
65
import access from "./core/access.js";
76
import rtagName from "./manipulation/var/rtagName.js";
8-
import rscriptType from "./manipulation/var/rscriptType.js";
97
import wrapMap from "./manipulation/wrapMap.js";
108
import getAll from "./manipulation/getAll.js";
9+
import domManip from "./manipulation/domManip.js";
1110
import setGlobalEval from "./manipulation/setGlobalEval.js";
12-
import buildFragment from "./manipulation/buildFragment.js";
1311
import dataPriv from "./data/var/dataPriv.js";
1412
import dataUser from "./data/var/dataUser.js";
1513
import acceptData from "./data/var/acceptData.js";
16-
import DOMEval from "./core/DOMEval.js";
1714
import nodeName from "./core/nodeName.js";
1815

1916
import "./core/init.js";
@@ -38,21 +35,6 @@ function manipulationTarget( elem, content ) {
3835
return elem;
3936
}
4037

41-
// Replace/restore the type attribute of script elements for safe DOM manipulation
42-
function disableScript( elem ) {
43-
elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type;
44-
return elem;
45-
}
46-
function restoreScript( elem ) {
47-
if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) {
48-
elem.type = elem.type.slice( 5 );
49-
} else {
50-
elem.removeAttribute( "type" );
51-
}
52-
53-
return elem;
54-
}
55-
5638
function cloneCopyEvent( src, dest ) {
5739
var i, l, type, pdataOld, udataOld, udataCur, events;
5840

@@ -85,91 +67,6 @@ function cloneCopyEvent( src, dest ) {
8567
}
8668
}
8769

88-
function domManip( collection, args, callback, ignored ) {
89-
90-
// Flatten any nested arrays
91-
args = flat( args );
92-
93-
var fragment, first, scripts, hasScripts, node, doc,
94-
i = 0,
95-
l = collection.length,
96-
iNoClone = l - 1,
97-
value = args[ 0 ],
98-
valueIsFunction = typeof value === "function";
99-
100-
if ( valueIsFunction ) {
101-
return collection.each( function( index ) {
102-
var self = collection.eq( index );
103-
args[ 0 ] = value.call( this, index, self.html() );
104-
domManip( self, args, callback, ignored );
105-
} );
106-
}
107-
108-
if ( l ) {
109-
fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored );
110-
first = fragment.firstChild;
111-
112-
if ( fragment.childNodes.length === 1 ) {
113-
fragment = first;
114-
}
115-
116-
// Require either new content or an interest in ignored elements to invoke the callback
117-
if ( first || ignored ) {
118-
scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
119-
hasScripts = scripts.length;
120-
121-
// Use the original fragment for the last item
122-
// instead of the first because it can end up
123-
// being emptied incorrectly in certain situations (trac-8070).
124-
for ( ; i < l; i++ ) {
125-
node = fragment;
126-
127-
if ( i !== iNoClone ) {
128-
node = jQuery.clone( node, true, true );
129-
130-
// Keep references to cloned scripts for later restoration
131-
if ( hasScripts ) {
132-
jQuery.merge( scripts, getAll( node, "script" ) );
133-
}
134-
}
135-
136-
callback.call( collection[ i ], node, i );
137-
}
138-
139-
if ( hasScripts ) {
140-
doc = scripts[ scripts.length - 1 ].ownerDocument;
141-
142-
// Reenable scripts
143-
jQuery.map( scripts, restoreScript );
144-
145-
// Evaluate executable scripts on first document insertion
146-
for ( i = 0; i < hasScripts; i++ ) {
147-
node = scripts[ i ];
148-
if ( rscriptType.test( node.type || "" ) &&
149-
!dataPriv.access( node, "globalEval" ) &&
150-
jQuery.contains( doc, node ) ) {
151-
152-
if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) {
153-
154-
// Optional AJAX dependency, but won't run scripts if not present
155-
if ( jQuery._evalUrl && !node.noModule ) {
156-
jQuery._evalUrl( node.src, {
157-
nonce: node.nonce,
158-
crossOrigin: node.crossOrigin
159-
}, doc );
160-
}
161-
} else {
162-
DOMEval( node.textContent, node, doc );
163-
}
164-
}
165-
}
166-
}
167-
}
168-
}
169-
170-
return collection;
171-
}
172-
17370
function remove( elem, selector, keepData ) {
17471
var node,
17572
nodes = selector ? jQuery.filter( selector, elem ) : elem,

src/manipulation/domManip.js

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import jQuery from "../core.js";
2+
import flat from "../var/flat.js";
3+
import rscriptType from "./var/rscriptType.js";
4+
import getAll from "./getAll.js";
5+
import buildFragment from "./buildFragment.js";
6+
import dataPriv from "../data/var/dataPriv.js";
7+
import DOMEval from "../core/DOMEval.js";
8+
9+
// Replace/restore the type attribute of script elements for safe DOM manipulation
10+
function disableScript( elem ) {
11+
elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type;
12+
return elem;
13+
}
14+
function restoreScript( elem ) {
15+
if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) {
16+
elem.type = elem.type.slice( 5 );
17+
} else {
18+
elem.removeAttribute( "type" );
19+
}
20+
21+
return elem;
22+
}
23+
24+
function domManip( collection, args, callback, ignored ) {
25+
26+
// Flatten any nested arrays
27+
args = flat( args );
28+
29+
var fragment, first, scripts, hasScripts, node, doc,
30+
i = 0,
31+
l = collection.length,
32+
iNoClone = l - 1,
33+
value = args[ 0 ],
34+
valueIsFunction = typeof value === "function";
35+
36+
if ( valueIsFunction ) {
37+
return collection.each( function( index ) {
38+
var self = collection.eq( index );
39+
args[ 0 ] = value.call( this, index, self.html() );
40+
domManip( self, args, callback, ignored );
41+
} );
42+
}
43+
44+
if ( l ) {
45+
fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored );
46+
first = fragment.firstChild;
47+
48+
if ( fragment.childNodes.length === 1 ) {
49+
fragment = first;
50+
}
51+
52+
// Require either new content or an interest in ignored elements to invoke the callback
53+
if ( first || ignored ) {
54+
scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
55+
hasScripts = scripts.length;
56+
57+
// Use the original fragment for the last item
58+
// instead of the first because it can end up
59+
// being emptied incorrectly in certain situations (trac-8070).
60+
for ( ; i < l; i++ ) {
61+
node = fragment;
62+
63+
if ( i !== iNoClone ) {
64+
node = jQuery.clone( node, true, true );
65+
66+
// Keep references to cloned scripts for later restoration
67+
if ( hasScripts ) {
68+
jQuery.merge( scripts, getAll( node, "script" ) );
69+
}
70+
}
71+
72+
callback.call( collection[ i ], node, i );
73+
}
74+
75+
if ( hasScripts ) {
76+
doc = scripts[ scripts.length - 1 ].ownerDocument;
77+
78+
// Reenable scripts
79+
jQuery.map( scripts, restoreScript );
80+
81+
// Evaluate executable scripts on first document insertion
82+
for ( i = 0; i < hasScripts; i++ ) {
83+
node = scripts[ i ];
84+
if ( rscriptType.test( node.type || "" ) &&
85+
!dataPriv.access( node, "globalEval" ) &&
86+
jQuery.contains( doc, node ) ) {
87+
88+
if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) {
89+
90+
// Optional AJAX dependency, but won't run scripts if not present
91+
if ( jQuery._evalUrl && !node.noModule ) {
92+
jQuery._evalUrl( node.src, {
93+
nonce: node.nonce,
94+
crossOrigin: node.crossOrigin
95+
}, doc );
96+
}
97+
} else {
98+
DOMEval( node.textContent, node, doc );
99+
}
100+
}
101+
}
102+
}
103+
}
104+
}
105+
106+
return collection;
107+
}
108+
109+
export default domManip;

test/unit/ajax.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2502,9 +2502,9 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re
25022502
} );
25032503
} );
25042504

2505-
//----------- jQuery.domManip()
2505+
//----------- domManip()
25062506

2507-
QUnit.test( "trac-11264 - jQuery.domManip() - no side effect because of ajaxSetup or global events", function( assert ) {
2507+
QUnit.test( "trac-11264 - domManip() - no side effect because of ajaxSetup or global events", function( assert ) {
25082508
assert.expect( 1 );
25092509

25102510
jQuery.ajaxSetup( {
@@ -2558,7 +2558,7 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re
25582558
);
25592559

25602560
QUnit.test(
2561-
"trac-11402 - jQuery.domManip() - script in comments are properly evaluated",
2561+
"trac-11402 - domManip() - script in comments are properly evaluated",
25622562
function( assert ) {
25632563
assert.expect( 2 );
25642564
jQuery( "#qunit-fixture" ).load( baseURL + "cleanScript.html", assert.async() );

0 commit comments

Comments
 (0)