javascript オブジェクト - RangeまたはDocumentFragmentをstringに変換する




継承 配列 (5)

DocumentFragment.textContentは必要なものを提供できますか?

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

console.log(frag.textContent)

W3C準拠のブラウザでJavaScript Rangeオブジェクトのhtml文字列を取得する方法はありますか?

たとえば、ユーザーが次の項目を選択したとします: Hello <b>World</b>
Range.toString()メソッドを使用して "Hello World"を文字列として取得することは可能です。 (Firefoxでは、 ドキュメントgetSelectionメソッドを使用することもできます)。

しかし、私は内部のHTMLを取得する方法を見つけるように見えることはできません。

いくつかの検索の後、範囲をDocumentFragmentオブジェクトに変換できることがわかりました。

しかし、 DocumentFragmentsinnerHTMLプロパティはありません(Firefoxでは少なくとも、WebkitやOperaは試していません)。
私にとっては奇妙に思える:選択されたアイテムにアクセスするには何らかの方法があるはずです。

私はdocumentFragmentを作成し、別の要素にドキュメントフラグメントを追加し、その要素のinnerHTMLを取得できることを認識します。
しかし、その方法では、選択した領域内の開いているタグは自動的に閉じられます。
それだけでなく、それを文字列として取得するためにDOMに接続するよりも、明らかに「良い方法」があります。

だから、範囲やDocFragのhtmlの文字列を取得する方法は?


だから、範囲やDocFragのhtmlの文字列を取得する方法は?

他の応答と XMLSerializer.prototype.serializeToStringhttps://w3c.github.io/DOM-Parsing/#the-xmlserializer-interface XMLSerializer.prototype.serializeToString説明されているXMLSerializer.prototype.serializeToStringメソッドを使用して、 DocumentFragmentオブジェクトをDOMStringに直接変換すること可能です。

RangeオブジェクトのDOMStringを取得するには、 Range.prototype.extractContentsメソッドまたはRange.prototype.extractContentsメソッドのいずれかを使用してDocumentFragment変換し、 DocumentFragmentオブジェクトのプロシージャに従います。

私はデモを添付しましたが、その要点は次の2つのラインにあります:

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>


hereからの例を説明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>'

いいえ、それがそれを行う唯一の方法です。 約10年前のDOM Level 2の仕様は、HTMLテキストとの間でノードをシリアル化およびデシリアライズするという点でほとんど何も持っていなかったので、 innerHTMLような拡張機能に頼らざるを得ません。

あなたのコメントについて

しかし、その方法では、選択した領域内の開いているタグは自動的に閉じられます。

...それ以外はどうすればいい? DOMは、ツリー内に配置されたノードで構成されています。 DOMからコンテンツをコピーすると、別のノードツリーしか作成できません。 要素ノードは、開始タグと終了タグによってHTMLで区切られます。 終了タグを必要とする要素のHTML表現は終了タグを持っていなければなりません。それ以外の場合は有効なHTMLではありません。


完全を期すために、私はこれを行うためにどの方法を使うべきか考えなければなりません。 このページの他の回答で示唆されているように、基本的に2つの方法があります。

注:一般に、組み込みのプロトタイプをJavaScriptで拡張することは一般的に推奨されていません。 私はStringのプロトタイプの拡張として、単にイラストレーションの目的で提供していますString組み込みのプロトタイプに関する仮説的な標準メソッドのさまざまな実装を示しています。

正規表現ベースの実装

String.prototype.replaceAll = function(search, replacement) {
    var target = this;
    return target.replace(new RegExp(search, 'g'), replacement);
};

分割結合(機能)実装

String.prototype.replaceAll = function(search, replacement) {
    var target = this;
    return target.split(search).join(replacement);
};

効率性の面で正規表現がどのように裏で働いているか分かりませんでしたが、私は分割に傾き、パフォーマンスを考えずに過去の実装に加わりました。 私は、より効率的で、どれだけの利益を得たのか不思議に思ったとき、それを見つけ出す言い訳として使用しました。

私のChromeのWindows 8マシンでは、正規表現ベースの実装が最も速く分割と結合の実装は53%遅くなりました 。 つまり、私が使用したlorem ipsum入力の正規表現は2倍高速です。

これらの2つの実装をお互いに実行しているこのbenchmarkをチェックしてください。

下の@ThomasLeducや他のコメントで述べたように、 search に正規表現で特殊文字として予約されている特定の文字が含まれていると、正規表現ベースの実装に問題がある可能性があります 。 実装では、呼び出し元が事前に文字列をエスケープするか、 正規表現 (MDN)の表に文字が含まれていない文字列のみを渡すことが前提です。

MDNは、文字列をエスケープするための実装も提供します。 これもRegExp.escape(str)として標準化されていればいいですが、 RegExp.escape(str)ながらそれは存在しません:

function escapeRegExp(str) {
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
}

私たちはString.prototype.replaceAll実装内でescapeRegExpを呼び出すことができますが、パフォーマンスにどの程度影響するかはわかりません(英数字の文字列のように、エスケープが不要な文字列でも可能です)。





javascript selection range tostring documentfragment