Element (rozszerzenie)#

Element.outerHTML#

Właściwość outerHTML parsuje (przy ustawianiu) lub serializuje (przy pobieraniu) dany element wraz z całą jego zawartością.

Opis działania#

Samo wywołanie i poszczególne jego części najlepiej objaśnić na zapisie składniowym:

  1. L
  2. K
  3. T'
  4. T
  5. A
  6. O
  7. Z'
  8. Z
  9. #
var actual_content = element.outerHTML; // getting
element.outerHTML = new_content; // setting

gdzie poszczególne człony oznaczają:

W zależności od podejmowanej akcji muszą zostać wykonane następujące kroki:

Odczyt wartości atrybutu (getting):

  1. Zwróć wynik działania algorytmu serializacji fragmentu na fikcyjnym węźle, którego jedynym dzieckiem jest obiekt kontekstu z przekazaniem boolowskiej wartości true dla flagi wymaganego dobrego sformułowani.

    W przypadku dokumentów XML krok ten może zrzucić wyjątek "InvalidStateError" zamiast zwrócenia łańcucha znakowego, kiedy obiekt kontekstu i/lub jego zawartość nie może zostać zserializowany zgodnie z wymogami XML-a (kwestia nierozstrzygnięta).

Zmiana wartości atrybutu: (setting):

  1. Niech parent będzie rodzicem posiadanym przez obiekt kontekstu.
  2. Jeśli parent ma wartość null to pomiń kolejne kroki. Nie ma żadnego sposobu pozyskania referencji do utworzonych węzłów, nawet jeśli pozostałe kroki zostałyby wykonane.
  3. Jeśli parent jest węzłem typu Document to zrzuć wyjątek "NoModificationAllowedError".
  4. Jeśli parent jest węzłem typu DocumentFragment to niech parent będzie nowym węzłem typu Element z:

  5. Niech fragment będzie wynikiem działania algorytmu parsowania fragmentu z łańcuchem znakowym będącym ustawianą wartością oraz z elementem kontekstu będącym parent.
  6. Zastąp nasz obiekt kontekstu przez fragment w rodzicu posiadanym przez obiekt kontekstu.

Właściwość outerHTML bierze pod uwagę dany element i jego zawartość (content), czyli uwzględnia także jego znacznik otwierający i znacznik zamykający. Jeśli nie zależy nam na takim zachowaniu, to można skorzystać z właściwości Element.innerHTML.

Z analizy drugiego algorytmu widać wyraźnie, że ustawianie właściwości outerHTML nie będzie możliwe w przypadku elementów, które nie posiadają rodzica, lub których rodzicem jest węzeł typu Document (czyli dla elementu dokumentowego, np. <html>). W tym drugim przypadku dodatkowo zrzucony zostaje specjalny wyjątek "NoModificationAllowedError", chociaż Chrome (w przeciwieństwie do Firefoksa i IE11) zrzuca błąd także dla elementów bez rodzica lub z rodzicem będącym fragmentem dokumentu.

Warto podkreślić, że przy serializacji węzłów tekstowych (ewentualnie atrybutów w elementach) może nastąpić samoczynne zastąpienie w danych tekstowych lub w wartościach atrybutów znaków &, <, > i " na odpowiadające im encje nazwane. Szczegóły tego zachowania są zależne od rodzaju przetwarzanego dokumentu, i w razie potrzeby należy je samodzielnie ustalić poprzez dokładną analizę poszczególnych algorytmów. Odczyt danych w oryginalnej postaci można przeprowadzić alternatywnymi poleceniami, np. Node.textContent czy Attr.value.

Parsowanie danych tekstowych pochodzących od osób trzecich stwarza duże zagrożenie i jeśli nie jest niezbędne należy rozważyć alternatywne rozwiązania.

Prosty przykład:

  1. L
  2. K
  3. T'
  4. T
  5. A
  6. O
  7. Z'
  8. Z
  9. #
<script>

	var html = document.documentElement; // referencja do elementu HTML

	alert(html.outerHTML); // Zwróci serializację elementu HTML i jego zawartości (z nim samym włącznie)
	document.write(html.parentNode); // [object HTMLDocument]

	try{
		html.outerHTML = "Błąd!";
	}
	catch(e){
		document.write("<br><br>" + "Zastąpienie elementu HTML z rodzicem Document zrzuca błąd:" + "<br>");
		document.write(e); // Wyjątek typu "NoModificationAllowedError"
	}

	try{ // Firefox i IE11
		var newEl = document.createElement("div");
		newEl.textContent = "Jakaś zawartość elementu";
		newEl.outerHTML = "Nowa zawartość";
		document.write("<br><br>" + "Zastąpienie elementu DIV bez rodzica nie zaskoczy ale i nie zrzuca błędu:" + "<br>");
		document.write(newEl + "<br>");
		document.write(newEl.textContent + "<br>"); // W IE11 pusty łańcuch (błędnie)
		document.write(newEl.parentNode + "<br>");
	}
	catch(e){// Chrome (błędnie)
		document.write("<br><br>" + "Zastąpienie elementu DIV bez rodzica zrzuca błąd:" + "<br>");
		document.write(e);
	}

</script>

Co ciekawe, kiedy element ze swoją zawartością zostanie już zastąpiony w drzewie węzłów, to zmienna która wskazywała na ten element nie ulega automatycznej aktualizacji, dlatego wywołanie właściwości outerHTML na tej zmiennej zwraca starszą wartość. Tak naprawdę zmienna wciąż pozostaje referencją do usuwanego obiektu z drzewa węzłów. Jedynym odstępstwem będzie IE11, gdzie zwrócony zostanie element bez żadnej zawartości. Warto o tym pamiętać, gdyż w przypadku właściwości Element.innerHTML i metody Element.insertAdjacentHTML() sytuacja taka nie musi występować:

  1. L
  2. K
  3. T'
  4. T
  5. A
  6. O
  7. Z'
  8. Z
  9. #
<script>

	var el = document.documentElement.firstElementChild; // referencja do pierwszego elementu w HTML

	alert(el.outerHTML); // Zwróci serializację pierwszego elementu w HTML i jego zawartości (z nim samym włącznie)

	el.outerHTML = "<p style='color: blue'>Nowa zawartość!</p>";

	alert(el.outerHTML); // Zwróci to samo, co pierwszy alert()

</script>

Starsze przeglądarki internetowe mogą obsługiwać właściwość outerHTML z drobnymi uchybieniami względem aktualnych standardów. Przykładowo, przeglądarka Internet Explorer (do wersji 8 włącznie) serializuje wpisaną przez użytkownika wartość do pola <input type="text">, kiedy pozostałe przeglądarki tego nie robią. Od wersji IE9 wszystko działa prawidłowo:

  1. L
  2. K
  3. T'
  4. T
  5. A
  6. O
  7. Z'
  8. Z
  9. #
<!DOCTYPE html>
<html>

<head>

	<script>

		function getOuterHTML(){

			var box = document.getElementById("box"); // referencja do kontenera

			alert(box.outerHTML); // Po wpisaniu tekstu do pola input starsze przeglądarki IE uwzględnią zmianę

		}

	</script>

</head>

<body>
	<div id="box">
		<p>Kliknij przycisk by zserializować kontener DIV i jego zawartość.</p>
		<input type="text">
		<input type="button" value="div.outerHTML" onclick="getOuterHTML()">
	</div>
</body>

</html>

Właściwości outerHTML nigdy nie powinno się używać do zapisu części tabeli - w takiej sytuacji parser przeglądarki internetowej może automatycznie zamknąć część tagów przy aktualizacji drzewa węzłów, aby uzyskać jego poprawną strukturę, a dalszą część (dodawaną w kolejnym wywołaniu polecenia) wstawić dopiero za tabelą. Nie ma natomiast problemów w przypadku zapisywania całej tabeli w jednym kroku lub modyfikacji zawartości pojedynczych komórek.

W związku z powyższym przeglądarka IE specjalnie blokuje możliwość zmiany niektórych elementów i ich zawartości za pomocą właściwości outerHTML, żeby nie dopuścić do problematycznych sytuacji. Na dzień dzisiejszy jest to niestandardowe zachowanie i nie musi być powielane przez pozostałe programy.

Składnia Web IDL#

  1. L
  2. K
  3. T'
  4. T
  5. A
  6. O
  7. Z'
  8. Z
  9. #
partial interface Element {
	[TreatNullAs=EmptyString] attribute DOMString outerHTML;
};

Specyfikacje i inne materiały#

Pasek społecznościowy

SPIS TREŚCI AKTUALNEJ STRONY

Element (rozszerzenie) (H1) Element.outerHTML (H2) Opis działania (H3) Składnia Web IDL (H3) Specyfikacje i inne materiały (H3)