const phrasingContent = [
'#text', 'a', 'abbr', 'area', 'audio', 'b', 'bdi', 'bdo', 'br', 'button',
'canvas', 'cite', 'code', 'data', 'datalist', 'del', 'dfn', 'em', 'embed',
'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'keygen', 'label', 'map', 'mark',
'math', 'meter', 'noscript', 'object', 'output', 'picture', 'progress', 'q',
'ruby', 's', 'samp', 'script', 'select', 'small', 'span', 'strong', 'sub',
'sup', 'svg', 'template', 'textarea', 'time', 'u', 'var', 'video', 'wbr'
]
function sanitize(text) {
const t = document.createElement('template')
t.innerHTML = text
whitelist(t.content, phrasingContent)
return t.content
}
function whitelist(parent, names) {
for (const node of parent.childNodes) {
whitelist(node, names)
if (!names.includes(node.nodeName.toLowerCase())) {
unwrap(node)
}
}
}
function unwrap(node) {
const parent = node.parentNode
while (node.firstChild) {
parent.insertBefore(node.firstChild, node)
}
parent.removeChild(node)
}
function empty(node) {
while (node.firstChild) {
node.removeChild(node.firstChild)
}
}
const form = document.querySelector('form')
const input = document.querySelector('textarea')
const output = document.querySelector('output')
form.addEventListener('submit', e => {
e.preventDefault()
empty(output)
output.appendChild(sanitize(marked(input.value)))
}, false)
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/0.3.6/marked.min.js"></script>
<form>
<p>
<textarea name="input" cols="30" rows="10">*foo*</textarea>
</p>
<button type="submit">Test</button>
</form>
<p> text <output></output> text </p>