Is it possible to have DocumentFragments contain tr, th or td tags?
If I do this:
var template = document.createRange().createContextualFragment(
'<table></table>'
);
console.log(template.childNodes);
I get the output of [table]
.
If I do this:
var template = document.createRange().createContextualFragment(
'<td></td>'
);
console.log(template.childNodes);
I get the output of []
!!!?!?
If I do this:
var template = document.createRange().createContextualFragment(
'<td><p></p></td>'
);
console.log(template.childNodes);
I get [p]
??!?!?!??!?!??!
And finally if I do this:
var template = document.createRange().createContextualFragment(
'<span><td></td></span>'
);
console.log(template.childNodes);
I get [span]
- where's the td gone??!
I don't understand the inconsistency here. Is it possible for document fragments to only hold certain elements? What I would like to do is do something akin to the second this above, and then retrieve the td using querySelector
.
Thanks
Is it possible to have DocumentFragments contain tr, th or td tags?
If I do this:
var template = document.createRange().createContextualFragment(
'<table></table>'
);
console.log(template.childNodes);
I get the output of [table]
.
If I do this:
var template = document.createRange().createContextualFragment(
'<td></td>'
);
console.log(template.childNodes);
I get the output of []
!!!?!?
If I do this:
var template = document.createRange().createContextualFragment(
'<td><p></p></td>'
);
console.log(template.childNodes);
I get [p]
??!?!?!??!?!??!
And finally if I do this:
var template = document.createRange().createContextualFragment(
'<span><td></td></span>'
);
console.log(template.childNodes);
I get [span]
- where's the td gone??!
I don't understand the inconsistency here. Is it possible for document fragments to only hold certain elements? What I would like to do is do something akin to the second this above, and then retrieve the td using querySelector
.
Thanks
Share Improve this question edited Dec 4, 2017 at 19:11 Supersharp 31.3k11 gold badges102 silver badges147 bronze badges asked Mar 29, 2017 at 19:54 Raiden616Raiden616 1,5643 gold badges19 silver badges46 bronze badges 3-
Why are you using
document.createRange()…
and not justdocument.createDocumentFragment()
? The later can contain just<td>
s perfectly well. – Matijs Commented Mar 29, 2017 at 20:02 - I wasn't awRe there was a difference to be honest.. I was using the former to allow me to specify xhtml direct. Is there a way of doing that with the latter method? – Raiden616 Commented Mar 29, 2017 at 20:15
- @Matijs I just tried it with createDocumentFragment() and get the same result... – Raiden616 Commented Mar 29, 2017 at 20:34
3 Answers
Reset to default 7Solution 1
Use createDocumentFragment(), create a <td>
element, and add it to the DocumentFragment with appendChild()
:
var frag = document.createDocumentFragment()
frag.appendChild( document.createElement( 'td' ) )
console.log( frag.childNodes ) // output => [td]
Solution 2
Create a <template>
element, add HTML content to innerHTML
, then get the DocumentFragment from the content
property:
var template = document.createElement( 'template' )
template.innerHTML = '<td></td>'
var frag = template.content
console.log( frag.childNodes ) // output => [td]
I have written a library for doing just this (creating document fragments from an HTML string). It is called html-fragment
.
This small library attempts to use the range API (as you are doing), but if the top level node is a node that needs a specific parent node (such as a td
), it will attempt to try some other solutions, such as a template
if it is supported, otherwise it does it the "old fashioned way" where it wraps it first with the correct parent tag, and then adds the nodes you wanted to the document fragment (without the temporary parent nodes).
You would use it like so:
var html = '<td><p>Hello World</p></td>';
var fragment = HtmlFragment(html);
console.log(fragment.firstChild) //td
<script src="https://unpkg./[email protected]/lib/html-fragment.min.js"></script>
const range = new Range()
const tableRange = new Range()
const table = document.createElement('table')
const tbody = document.createElement('tbody')
const tr = document.createElement('tr')
const colgroup = document.createElement('colgroup')
/**
* https://developer.mozilla/en-US/docs/Web/HTML/Element#Table_content
*/
const tableTags = [
'tbody',
'thead',
'tfoot',
'caption',
'colgroup',
'col',
'tr',
'td',
'th'
]
export function createFragment(str: string) {
const firstTag = str.match(/^<(([a-z]|-)+)/)?.[1]
if (firstTag && tableTags.includes(firstTag)) {
switch (firstTag) {
case 'tbody':
case 'thead':
case 'tfoot':
case 'caption':
case 'colgroup':
tableRange.selectNodeContents(table)
break
case 'tr':
tableRange.selectNodeContents(tbody)
break
case 'td':
case 'th':
tableRange.selectNodeContents(tr)
break
case 'col':
tableRange.selectNodeContents(colgroup)
break
default:
break
}
return tableRange.createContextualFragment(str)
}
return range.createContextualFragment(str)
}