简体   繁体   中英

Why is creating elements from a text/html Document slower than creating them from an application/xml Document?

Scenario : I want to create a form and append 20k+ input fields in steps of 10 at a time.

Implementation : I use the JS DOMParser to create a Document and use the Document.createElement method to create these elements.

Issue : Using the mimetype "text/html" is often more than 5 times slower than using "application/xml".

Questions :

  • Should I continue with the "application/xml" mimetype to create large HTML DOM hierarchies?
  • Is there a reason "text/html" is so slow?
  • Is there a downside to using "application/xml" when building an HTML DOM?

Example Test

The following snippet is a basic example of what I'm trying to accomplish. It has tests for both mimetype options and outputs the elapsed time to the console.

JSFiddle link

 // Controls const htmlTest = document.getElementById('html'); const xmlTest = document.getElementById('xml'); const progress = document.getElementById('progress'); const formContainer = document.getElementById('form'); // Generate input field data for test, 2000 sets of 10 inputs each. const inputSets = []; for (let i = 0; i < 2000; i++) { const inputSet = []; for (let j = 0; j < 10; j++) { inputSet.push({ name: `abc[${i}]`, value: "123" }); } inputSets.push(inputSet); } // Each set will be created in a task so that we can track progress function runTask(task) { return new Promise(resolve => { setTimeout(() => { task(); resolve(); }); }); } // The actual create form function function createForm(isXML, callback) { formContainer.innerHTML = ''; const domparser = new DOMParser(); let doc; if (isXML) { doc = domparser.parseFromString('<?xml version="1.0" encoding="UTF-8"?><form method="POST" action="targetAction" target="_blank"></form>', "application/xml"); } else { doc = domparser.parseFromString('<form method="POST" action="targetAction" target="_blank"></form>', "text/html"); } const form = doc.getElementsByTagName('form')[0]; const start = Date.now(); console.log('==================='); console.log(`Started @: ${(new Date(start)).toISOString()}`); let i = 0; const processTasks = () => { runTask(() => { for (let input of inputSets[i]) { const inputNode = doc.createElement('input'); inputNode.setAttribute('type', 'hidden'); inputNode.setAttribute('name', input.name); inputNode.setAttribute('value', input.value); form.appendChild(inputNode); } }).then(() => { i++; if (i < inputSets.length) { progress.innerHTML = `Progress: ${Math.floor((i / inputSets.length) * 100)} %`; processTasks(); } else { progress.innerHTML = 'Progress: 100 %' const serializer = new XMLSerializer(); // EDIT: By using the xml serializer you can append valid HTML formContainer.innerHTML = serializer.serializeToString(form); const end = Date.now(); console.log(`Ended @: ${(new Date(end)).toISOString()}`); console.log(`Time Elapsed: ${(end - start) / 1000} seconds`); console.log('==================='); callback && callback(); } }); }; // Append all the inputs processTasks(); } htmlTest.onclick = () => { createForm(false, () => { const tForm = formContainer.getElementsByTagName('form')[0]; tForm.submit(); }); }; xmlTest.onclick = () => { createForm(true, () => { const tForm = formContainer.getElementsByTagName('form')[0]; tForm.submit(); }); }; 
 <button id="html">text/html test</button> <button id="xml">application/xml test</button> <div id="progress">Progress: 0 %</div> <div id="form"></div> 

EDIT : I edited the example with the new information provided in the answer. I was able to keep the application/xml and create a valid HTML form by using the XMLSerializer to set the xml to innerHTML as a string. This way I can generate the form faster but still be able to submit it as if it were created by window.document.createElement (text/html Document).

Should I continue with the "application/xml" mimetype to create large HTML DOM hierarchies?

I don't really understand what you are trying to do (the big picture) so it's quite hard to say.

Is there a reason "text/html" is so slow?"

Yes. First just creating an HTML document is way more complicated than creating an XML one.
Simply check both DOMs you created, the HTML one has way more elements.

 const markup = '<form></form>' console.log( 'text/html', new XMLSerializer().serializeToString( new DOMParser().parseFromString(markup, 'text/html') ) ); console.log( 'application/xml', new XMLSerializer().serializeToString( new DOMParser().parseFromString(markup, 'application/xml') ) ); 

Now, your case doesn't even only create documents, it does then after create elements and set attributes to these.

The attributes you will set in HTML are IDL attributes that are triggering a lot of side-effects, while in the XML version they correspond to nothing and thus faster to set.

 const xmldoc = new DOMParser().parseFromString('<div/>', 'application/xml'); const xmlinput = xmldoc.createElement('input'); xmlinput.setAttribute('type', 'text'); console.log("xml input", xmlinput.type) // undefined const htmldoc = new DOMParser().parseFromString('<div/>', 'text/html'); const htmlinput = htmldoc.createElement('input'); htmlinput.setAttribute('type', 'text'); console.log("html input", htmlinput.type) // "text" 

Is there a downside to using "application/xml" when building an HTML DOM?

Yes : you are not building an HTML DOM. None of the elements you are creating are inheriting from HTMLElement, and none will behave the same as their HTML correspondence.

 const xmldoc = new DOMParser().parseFromString('<div/>', 'application/xml'); const xmlinput = xmldoc.createElement('input'); xmlinput.setAttribute('type', 'text'); console.log("is HTMLElement", xmlinput instanceof HTMLElement) // false 

So if you need an HTML DOM, you can not parse it as an XML one.

I hope you'll be able to answer yourself the first question from there.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM