createdocumentfragment javascript selection range tostring documentfragment

javascript - createdocumentfragment js



Convertir rango o DocumentFragment a cadena (6)

¿Hay alguna manera de obtener la cadena html de un objeto de rango JavaScript en navegadores compatibles con W3C?

Por ejemplo, digamos que el usuario selecciona lo siguiente: Hello <b>World</b>
Es posible obtener "Hello World" como una cadena usando el método Range.toString() . (En Firefox, también es posible usar el método getSelection del documento ).

Pero parece que no puedo encontrar una manera de obtener el HTML interno.

Después de algunas búsquedas, he encontrado que el rango se puede convertir en un objeto DocumentFragment .

Pero DocumentFragments no tiene propiedades innerHTML (al menos en Firefox; no he probado Webkit u Opera).
Lo que me parece extraño: parece obvio que debería haber alguna forma de acceder a los elementos seleccionados.

Me doy cuenta de que puedo crear un documentFragment , añadir el fragmento de documento a otro elemento y luego obtener el innerHTML de ese elemento.
Pero ese método cerrará automáticamente cualquier etiqueta abierta dentro del área que seleccione.
Además, seguramente hay una "mejor manera" obvia que unirla a la dom solo para obtenerla como una cuerda.

Entonces, ¿cómo obtener la cadena del html de un rango o DocFrag?


Entonces, ¿cómo obtener la cadena del html de un rango o DocFrag?

Al contrario de las otras respuestas, es posible convertir directamente un objeto DocumentFragment en un DOMString utilizando el método XMLSerializer.prototype.serializeToString descrito en https://w3c.github.io/DOM-Parsing/#the-xmlserializer-interface .

Para obtener el DOMString de un objeto Range , simplemente Range.prototype.cloneContents en un DocumentFragment usando cualquiera de los métodos Range.prototype.cloneContents o Range.prototype.extractContents y luego siga el procedimiento para un objeto DocumentFragment .

He adjuntado una demostración, pero la esencia de esto está en estas dos líneas:

const serializer = new XMLSerializer(); const document_fragment_string = serializer.serializeToString(document_fragment);

(() => { "use strict"; const HTML_namespace = "http://www.w3.org/1999/xhtml"; document.addEventListener("DOMContentLoaded", () => { /* Create Hypothetical User Range: */ const selection = document.defaultView.getSelection(); const user_range_paragraph = document.getElementById("paragraph"); const user_range = document.createRange(); user_range.setStart(user_range_paragraph.firstChild, 0); user_range.setEnd(user_range_paragraph.lastChild, user_range_paragraph.lastChild.length || user_range_paragraph.lastChild.childNodes.length); selection.addRange(user_range); /* Clone Hypothetical User Range: */ user_range.setStart(selection.anchorNode, selection.anchorOffset); user_range.setEnd(selection.focusNode, selection.focusOffset); const document_fragment = user_range.cloneContents(); /* Serialize the User Range to a String: */ const serializer = new XMLSerializer(); const document_fragment_string = serializer.serializeToString(document_fragment); /* Output the Serialized User Range: */ const output_paragraph = document.createElementNS(HTML_namespace, "p"); const output_paragraph_code = document.createElementNS(HTML_namespace, "code"); output_paragraph_code.append(document_fragment_string); output_paragraph.append(output_paragraph_code); document.body.append(output_paragraph); }, { "once": true }); })();

<p id="paragraph">Hello <b>World</b></p>


¿Podría DocumentFragment.textContent darle lo que necesita?

var frag = document.createRange().createContextualFragment("Hello <b>World</b>."); console.log(frag.textContent)


FWIW, la forma jQuery:

$(''<div>'').append(fragment).html()


No, esa es la única manera de hacerlo. Las especificaciones de DOM Nivel 2 de hace aproximadamente 10 años no tenían casi nada en términos de serialización y deserialización de nodos hacia y desde texto HTML, por lo que se ve obligado a confiar en extensiones como innerHTML .

Respecto a tu comentario que

Pero ese método cerrará automáticamente cualquier etiqueta abierta dentro del área que seleccione.

... ¿de qué otra manera podría funcionar? El DOM está formado por nodos dispuestos en un árbol. Copiar contenido desde el DOM solo puede crear otro árbol de nodos. Los nodos de elementos están delimitados en HTML por un inicio y, a veces, una etiqueta final. Una representación HTML de un elemento que requiere una etiqueta final debe tener una etiqueta final, de lo contrario no es un HTML válido.


Otra forma de hacerlo sería iterar sobre childNodes:

Array.prototype.reduce.call( documentFragment.childNodes, (result, node) => result + (node.outerHTML || node.nodeValue), '''' );

No funcionaría para SVG en línea, pero se podría hacer algo para que funcione. También ayuda si necesita dominar una manipulación encadenada con los nodos y obtener una cadena html como resultado.


Para explicar un ejemplo desde here :

//Example setup of a fragment var frag = document.createDocumentFragment(); //make your fragment var p = document.createElement(''p''); //create <p>test</p> DOM node p.textContent = ''test''; frag.appendChild( p ); //Outputting the fragment content using a throwaway intermediary DOM element (div): var div = document.createElement(''div''); div.appendChild( frag.cloneNode(true) ); console.log(div.innerHTML); //output should be ''<p>test</p>''