No he visto una respuesta satisfactoria para esta pregunta. Esto básicamente es un duplicado de esta pregunta , pero se cerró incorrectamente y las respuestas dadas no son suficientes.
He encontrado mi propia solución que publicaré a continuación.
Esto puede ser útil para el web scraping o, en mi caso, para ejecutar pruebas en una biblioteca de JavaScript que maneja elementos personalizados. Me aseguro de que esté produciendo el resultado que quiero, luego uso esta función para raspar el HTML para un resultado de prueba dado y uso ese HTML copiado como el resultado esperado para comparar la prueba en el futuro.
Aquí hay una función que puede hacer lo que se solicita. Tenga en cuenta que ignora los comentarios html y otras cosas marginales. Pero recupera elementos regulares, nodos de texto y elementos personalizados con shadowRoots. También maneja el contenido de la plantilla ranurada. No se ha probado exhaustivamente, pero parece estar funcionando bien para mis necesidades.
Úselo como extractHTML(document.body) o extractHTML(document.getElementByID('app')) .
function extractHTML(node) { // return a blank string if not a valid node if (!node) return '' // if it is a text node just return the trimmed textContent if (node.nodeType===3) return node.textContent.trim() //beyond here, only deal with element nodes if (node.nodeType!==1) return '' let html = '' // clone the node for its outer html sans inner html let outer = node.cloneNode() // if the node has a shadowroot, jump into it node = node.shadowRoot || node if (node.children.length) { // we checked for children but now iterate over childNodes // which includes #text nodes (and even other things) for (let n of node.childNodes) { // if the node is a slot if (n.assignedNodes) { // an assigned slot if (n.assignedNodes()[0]){ // Can there be more than 1 assigned node?? html += extractHTML(n.assignedNodes()[0]) // an unassigned slot } else { html += n.innerHTML } // node is not a slot, recurse } else { html += extractHTML(n) } } // node has no children } else { html = node.innerHTML } // insert all the (children's) innerHTML // into the (cloned) parent element // and return the whole package outer.innerHTML = html return outer.outerHTML }Solo si shadowRoots se crean con el mode:"open" puede acceder a shadowRoots desde el exterior.
Luego puede sumergirse en elementos y shadowRoots con algo como:
const shadowDive = ( el, selector, match = (m, r) => console.warn('match', m, r) ) => { let root = el.shadowRoot || el; root.querySelector(selector) && match(root.querySelector(selector), root); [...root.children].map(el => shadowDive(el, selector, match)); }Nota: la extracción de HTML sin procesar no tiene sentido si el estilo del componente web se basa en el comportamiento shadowDOM; perderás todo el estilo correcto.