Automatyczne otwieranie zewnętrznych odnośników w nowej karcie przeglądarki

Kategoria: Webmastering Data publikacji: 2 komentarze

Moim preferowanym zachowaniem jest otwieranie zewnętrznych linków w nowej karcie przeglądarki. Oznacza to, że gdy kliknę odnośnik kierujący do strony internetowej innej niż aktualnie przeglądana, oczekuję, iż otworzy się on w nowej karcie. W dzisiejszym wpisie przedstawię, w jaki sposób za pomocą skryptu PHP automatycznie przekonwertować wszystkie odnośniki zewnętrzne w taki sposób, by otwierały się w nowej karcie.

Swoje preferencje respektuję również na własnych stronach internetowych, gdzie zawsze dbam o to, aby linki poza moją witrynę otwierały się w oddzielnej karcie. To rozwiązanie ma swoje wady i zalety. Ja jestem jego zwolennikiem, ale uczciwie zaznaczam, iż warto przed jego wprowadzeniem zapoznać się z argumentami za i przeciw. Serdecznie polecam artykuł „Opening External Links: Same Tab or New?”, który te kwestie dość dobrze objaśnia.

W moim blogu opartym na WordPressie dotychczas politykę otwierania linków zewnętrznych w nowej karcie realizowałem ręcznie — po prostu definiując każdy link, żmudnie wskakiwałem w opcje zaawansowane edytora WYSIWG i zaznaczałem odpowiedniego checkboksa. Uświadomiłem sobie jednak, że sposób ten jest nieefektywny. Nie dość, że zawsze muszę o odpowiedniej zmianie pamiętać i wprowadzać ją ręcznie, to jeszcze, jeśli w przyszłości zechcę zmienić swoją decyzję, będę musiał modyfikować wiele wpisów hurtowo (np. na poziomie bazy danych).

Napisałem więc prosty skrypt, który owe zadanie wykonuje obecnie za mnie. Skrypt jest zbudowany w formie filtra dla WordPressa, jednak bardzo łatwo można go przekształcić tak, by stał się reużywalny poza tym brzydko zaprojektowanym CMS-em. ;-)

Poniższy kod można wrzucić do functions.php swojego motywu, czego nie polecam, lub do własnej wtyczki systemowej — co bardzo rekomenduję. Oto owe cudo.

<?php

function external_anchors_target_blank($html)
{
	$DOMPart = new class($html) extends \DOMDocument
	{
		public function __construct($html)
		{
			@$this->loadHTML(
				'<meta charset="utf-8"><body>' . $html . '</body>',
				LIBXML_NOERROR | LIBXML_NOWARNING
			);
		}

		public function saveHTML(...$args)
		{
			if ($args) {
				return parent::saveHTML(...$args);
			}
			return str_replace(['<body>', '</body>'], '',
				parent::saveHTML($this->getElementsByTagName('body')[0])
			);
		}
	};

	$siteDomain = parse_url(site_url(), PHP_URL_HOST);
	$anchors = $DOMPart->getElementsByTagName('a');

	foreach ($anchors as $anchor) {
		$anchorDomain = parse_url($anchor->getAttribute('href'), PHP_URL_HOST);

		if ($anchorDomain and $anchorDomain != $siteDomain and !$anchor->getAttribute('target')) {
			$anchor->setAttribute('target', '_blank');
			$anchor->setAttribute('rel', trim($anchor->getAttribute('rel') . ' noopener'));
		}
	}

	return $DOMPart->saveHTML();
};

add_filter('the_content',  'external_anchors_target_blank');
add_filter('comment_text', 'external_anchors_target_blank');

Jak widać powyżej, nie mam się z czego spowiadać. Nie zgrzeszyłem — nie zmodyfikowałem kodu HTML wyrażeniem regularnym. Zamiast tego wykorzystałem dość wygodny, choć nieidealny, wbudowany w PHP parser HTML i XML, a dokładniej obiekt DOMDocument owrapperowany przeze mnie w celu uniknięcia drobnych kłopotów z kodowaniem znaków i uzyskiwaniem wynikowego kodu HTML. Oczywiście mógłbym posłużyć się jedną z wielu bibliotek parserów HTML, jednak do tak prostego zadania nie chciałem zaciągać wielkich kombajnów.

Skrypt iteruje po wszystkich odnośnikach znalezionych w treści wpisów i komentarzy, a następnie dodaje atrybut target="_blank" do tych linków, które mają określoną domenę inną niż domena mojej witryny. Dla zwiększenia bezpieczeństwa skrypt dodaje także atrybut rel="noopener" zapobiegający modyfikacji mojej witryny przez obcą stronę otwartą w nowej karcie.
Zostawiłem też sobie tylną furtkę — jeżeli zechcę, aby wybrany odnośnik pomimo zewnętrznej domeny otwierał się w tej samej karcie, mogę manualnie wprowadzić atrybut target="_self".

Kod wykorzystuje klasę anonimową, dlatego zadziała wyłącznie w PHP 7.x.

Żeby całość była spójna, musiałem wprowadzić zmiany w bazie danych WordPressa. Aby pozbyć się z już napisanych wpisów atrybutów target="_blank" manualnie dodanych w edytorze graficznym, musiałem wykonać na bazie witryny zapytania SQL podobne do poniższych.

UPDATE wp_posts SET post_content = REPLACE(post_content, "target=\"_blank\"", "");
UPDATE wp_posts SET post_content = REPLACE(post_content, "rel=\"noopener\"", "");

Nie polecam jednak wprowadzania zmian w bazie danych WordPressa bez posiadania odpowiedniej wiedzy oraz, oczywiście, kopii zapasowej!

Komentarze (2)

  1. 💩

    Głupi pomysł. To użytkownik decyduje gdzie chce otworzyć link, a nie autor strony. Za takie praktyki (podobnie jak za outline: none) powinien być dół z wapnem.

  2. Tomasz Gąsior

    To kontrowersyjna sprawa i obydwie strony mają swoje argumenty. Obydwie perspektywy zostały dość dobrze opisane w podlinkowanym przeze mnie artykule. Ja osobiście lubię target blank na zewnętrznych linkach ze względu na czytelność i swego rodzaju marketing, dlatego na swoich stronach taką praktykę uprawiam.

    Nie jest do końca prawdą, że użytkownik decyduje. Tak, decyduje, ale w zakresie wyznaczonym przez autora strony. To programiści tworzący dany program czy aplikację www decydują, o czym może decydować użytkownik — i moim zdaniem jest to ok, dopóki wszystko poukładane jest rozsądnie.

Dodaj komentarz