UserScript: wyścig szczurów przy wstrzykiwaniu styli

tech • 540 słów • 3 minuty czytania

Przed świętami zrobiłem kilka poprawek w moich skryptach użytkownika, a teraz po świętach znów czekają mnie hurtowe poprawki, bo po wyczyszczeniu cache-u przeglądarki niektóre skrypty przestały poprawnie działać.

W logach konsoli często pojawiał się komunikat o problemach z dodawaniem jakiś elementów do dokumentu, na przykład jak ten poniżej, z jednego z moich innych skryptów próbujący wstrzyknąć jakieś CSS-y do nagłówka dokumentu:

Uncaught (in promise) TypeError: Cannot read property 'appendChild' of null
    at eval (userscript...:48)
    at Window.eval (userscript...:51)
    ...

Co ewidentnie świadczyłoby, że skrypt jest niepoprawnie wstrzykiwany do strony lub odpala się szybciej niż dokument zacznie się ładować. Wrzuciłem kilka logów do skryptu, aby się upewnić, czy tak rzeczywiście jest. No i miałem rację!

W zakładkach, gdzie występują problemy, w logach widzę null-e:

document.head:			null
document.body:			null
document.readyState:	loading

A na tych poprawnie działających dostępny jest już <head/>, tak jak to miało miejsce do tej pory:

document.head:			<head>...</head>
document.body:			null
document.readyState:	loading

Pierwsze skojarzenie to nowa wersja Tampermonkey, bo coś mi mignęło z tą wtyczką po uruchomieniu przeglądarki1, ale nowej wersji nie było od 19 marca. Za to w jej changelogu znalazłem taki wpis “Speed up injection”. Może to jest jakoś związane z tym moim “problemem” i ukazało się przypadkiem właśnie teraz po wyczyszczeniu pamięci podręcznej?

Niezależnie od przyczyn takiego stanu rzeczy, problem ten mógłbym rozwiązać dodając style już po sparsowaniu drzewa strony - DOMContentLoaded. No, ale głównym celem “odpalania” skryptu z @run-at ustawionym na document-start było jak najszybsze wystrzykniecie styli. Po to, aby zaczęły być widoczne dla silnika przeglądarki jeszcze przed jakimkolwiek renderowaniem, unikając widocznych zmian i migotania…

Potestowałem różne opcje i eventy i najlepiej sprawdza się obserwowanie zmian w DOM-ie i działanie wtedy, gdy potrzebny element, tutaj , będzie już dostępny. To zapewnia mi jak najszybszą operację, gdy tylko jest to możliwe.

Do tego cel napisałem sobie prostą funkcję OnHeadReady opakowującą implementacyjne bebechy:

function OnHeadReady(handler) {

	if (document.head) {
		handler();
		return;
	}

	(new MutationObserver((mutations, observer) => {
		if (document.head) {
			handler();
			observer.disconnect();
		}
	}).observe(document, {childList: true, subtree: true});
}

W nieco skompresowanej formie kod ten wylądował w moich skryptach wstrzykujących style do strony, gdzie mniej więcej użyty jest w takiej postaci, zapewniając jak najwcześniejsze dołączenie własnych styli na etapie ładowania strony:

const style = document.createElement('style');
style.textContent = `...`;

OnHeadReady(() => {
	document.head.appendChild(style);
});

Rozwiązało to ten niespodziewany problem “wyścigu” zachowując tą istotną tutaj cechę najszybszego wstrzykiwania.

Podbiłem wersje skryptów, które wymagały takiego zabiegu i została wydana nowa wersja “OLX.pl Modern UI Fix” (0.1.2) oraz “YouTube Polymer UI Fix” (0.1.2). Aktualne kody moich skryptów dostepne są w repo UserScript.


Tym razem zdecydowałem się na jeden wpis dokumentujący wydanie, bo dotyczy to tego samego problemu i rozwiązania. A ostatnio trochę tutaj na blogu się rozwodziłem nad poprawkami skryptów użytkownika. W sumie taka jest rola wpisów z wydań, gdzie jednak chciałbym sobie głównie dla celów archiwalnych zawrzeć trochę więcej informacji, niż samą wzmiankę o jakiś poprawkach i nowych wydaniach.

Z drugiej strony zabawa w JavaScripcie z tymi skryptami to szczerze mówiąc trochę taka wielka prowizorka, ale też całkiem niezły sposób na dobry relaks i oderwanie się od innych bardziej wymagających działań programistycznych, kodów czy projektów. A przy okazji tej zabawy powstaje coś przydatnego, a przynajmniej użytecznego dla mnie ;)


Przypisy

  1. Załadowała się strona startowa rozszerzania, jak to ma często miejsce po instalacji… ↩︎

Komentarze (0)

Dodaj komentarz

/dozwolony markdown/

/nie zostanie opublikowany/