{"id":731,"date":"2024-02-23T07:02:04","date_gmt":"2024-02-23T07:02:04","guid":{"rendered":"https:\/\/webdevs.blog\/pl\/?p=731"},"modified":"2026-02-20T07:28:07","modified_gmt":"2026-02-20T07:28:07","slug":"wzorzec-obserwatora-w-javascripcie","status":"publish","type":"post","link":"https:\/\/webdevs.blog\/pl\/wzorzec-obserwatora-w-javascripcie\/","title":{"rendered":"Wzorzec obserwatora w Javascripcie"},"content":{"rendered":"\n<figure class=\"wp-block-image size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/webdevs.blog\/pl\/wp-content\/uploads\/sites\/2\/2024\/02\/js-observers.jpg\" alt=\"\" class=\"wp-image-737\" width=\"1118\" height=\"671\" srcset=\"https:\/\/webdevs.blog\/pl\/wp-content\/uploads\/sites\/2\/2024\/02\/js-observers.jpg 1000w, https:\/\/webdevs.blog\/pl\/wp-content\/uploads\/sites\/2\/2024\/02\/js-observers-300x180.jpg 300w, https:\/\/webdevs.blog\/pl\/wp-content\/uploads\/sites\/2\/2024\/02\/js-observers-768x461.jpg 768w\" sizes=\"auto, (max-width: 1118px) 100vw, 1118px\" \/><\/figure>\n\n\n\n<p>Wzorzec obserwatora jest bardzo prosty. W swoim za\u0142o\u017ceniu polega na tym, \u017ce jeden obiekt powiadamia inne obiekty o swojej zmianie. W JavaScript istniej\u0105 predefiniowane observators, kt\u00f3re u\u0142atwiaj\u0105 \u017cycie programistom.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">MutationObserver<\/h2>\n\n\n\n<p>Pozwala na \u015bledzenie zmian, takich jak zmiana atrybut\u00f3w czy dodanie nowego w\u0119z\u0142a w strukturze DOM.<\/p>\n\n\n\n<p>Konstruktor klasy <strong><a rel=\"noreferrer noopener\" href=\"https:\/\/dom.spec.whatwg.org\/#mutationobserver\" data-type=\"URL\" data-id=\"https:\/\/dom.spec.whatwg.org\/#mutationobserver\" target=\"_blank\">MutationObserver<\/a><\/strong> przyjmuje jako parametr callback. Callback z kolei otrzyma tablic\u0119 z obiektami <strong><a rel=\"noreferrer noopener\" href=\"https:\/\/dom.spec.whatwg.org\/#mutationrecord\" data-type=\"URL\" data-id=\"https:\/\/dom.spec.whatwg.org\/#mutationrecord\" target=\"_blank\">MutationRecord<\/a><\/strong> oraz jako drugi parametr instancj\u0119 MutationObserver.<\/p>\n\n\n\n<p>Aby rozpocz\u0105\u0107 obserwacj\u0119, nale\u017cy wywo\u0142a\u0107 metod\u0119 observe, kt\u00f3ra jako argumenty przyjmuje <a rel=\"noreferrer noopener\" href=\"https:\/\/dom.spec.whatwg.org\/#node\" data-type=\"URL\" data-id=\"https:\/\/dom.spec.whatwg.org\/#node\" target=\"_blank\">Node<\/a> (np. HTMLElement) oraz <a rel=\"noreferrer noopener\" href=\"https:\/\/dom.spec.whatwg.org\/#dictdef-mutationobserverinit\" data-type=\"URL\" data-id=\"https:\/\/dom.spec.whatwg.org\/#dictdef-mutationobserverinit\" target=\"_blank\">konfiguracj\u0119<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Przyk\u0142ad<\/h3>\n\n\n\n<p>Za\u0142\u00f3\u017cmy, \u017ce mamy pole tekstowe i chcemy zlicza\u0107 ilo\u015b\u0107 znak\u00f3w.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">mutationObserverCallback<\/span>(<span class=\"hljs-params\">mutations<\/span>) <\/span>{\n  mutations.forEach(<span class=\"hljs-function\">(<span class=\"hljs-params\">mutation<\/span>) =&gt;<\/span> {\n    <span class=\"hljs-keyword\">if<\/span> (mutation.type === <span class=\"hljs-string\">'characterData'<\/span>) {\n      updateCounter(mutation.target.value.length);\n    }\n  });\n}\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">updateCounter<\/span>(<span class=\"hljs-params\">length<\/span>) <\/span>{\n  <span class=\"hljs-built_in\">document<\/span>.getElementById(<span class=\"hljs-string\">'char-counter'<\/span>)?.innerText = length;\n}\n\n<span class=\"hljs-keyword\">const<\/span> observeConfig = {\n  <span class=\"hljs-attr\">characterData<\/span>: <span class=\"hljs-literal\">true<\/span>,\n};\n\n(<span class=\"hljs-keyword\">new<\/span> MutationObserver(mutationObserverCallback))\n  .observe(<span class=\"hljs-built_in\">document<\/span>.getElementById(<span class=\"hljs-string\">'text-area'<\/span>), observeConfig);\n\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Przy wprowadzaniu tekstu do <code>textarea<\/code> za ka\u017cdym razem b\u0119dzie aktualizowana warto\u015b\u0107 w elemencie <code>#char-counter<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">IntersectionObserver<\/h2>\n\n\n\n<p>Sta\u0142ym problemem we frontend developmencie od zawsze by\u0142o ustalenie, czy dany element jest widoczny w obszarze u\u017cytkownika, czy nie. Stara szko\u0142a uczy\u0142a, aby nas\u0142uchiwa\u0107 zdarzenia <code>scroll<\/code>, a nast\u0119pnie ustala\u0107 przesuni\u0119cie elementu wzgl\u0119dem g\u00f3ry dokumentu i przesuni\u0119cia. By\u0142o to obci\u0105\u017caj\u0105ce zadanie, zw\u0142aszcza gdy musieli\u015bmy \u015bledzi\u0107 wiele takich element\u00f3w w ogromnej strukturze DOM. Ten problem rozwi\u0105zuje <strong><a rel=\"noreferrer noopener\" href=\"https:\/\/w3c.github.io\/IntersectionObserver\/#intersection-observer-interface\" target=\"_blank\">IntersectionObserver<\/a><\/strong>.<\/p>\n\n\n\n<p>Podobnie jak inne interfejsy, konstruktor IntersectionObserver przyjmuje Callback, kt\u00f3ry reaguje na zmiany oraz <strong><a rel=\"noreferrer noopener\" href=\"https:\/\/w3c.github.io\/IntersectionObserver\/#dictdef-intersectionobserverinit\" target=\"_blank\">konfiguracj\u0119<\/a><\/strong>. Callback przyjmuje tablic\u0119 <strong><a rel=\"noreferrer noopener\" href=\"https:\/\/w3c.github.io\/IntersectionObserver\/#intersectionobserverentry\" data-type=\"URL\" data-id=\"https:\/\/w3c.github.io\/IntersectionObserver\/#intersectionobserverentry\" target=\"_blank\">IntersectionObserverEntry<\/a><\/strong> oraz instancj\u0119 obserwatora.<\/p>\n\n\n\n<p>Do uruchomienia obserwacji s\u0142u\u017cy metoda <strong>observe<\/strong>, kt\u00f3ra jako argument przyjmuje obiekt dziedzicz\u0105cy po <a rel=\"noreferrer noopener\" href=\"https:\/\/dom.spec.whatwg.org\/#element\" data-type=\"URL\" data-id=\"https:\/\/dom.spec.whatwg.org\/#element\" target=\"_blank\">Element<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Przyk\u0142ad<\/h3>\n\n\n\n<p>Za\u0142\u00f3\u017cmy, \u017ce chcemy wykrywa\u0107 elementy <code>.ad<\/code>, w kt\u00f3rych wyst\u0119puj\u0105 reklamy.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">itersectionObserverCallback<\/span>(<span class=\"hljs-params\">entries<\/span>) <\/span>{\n  entries.forEach(<span class=\"hljs-function\"><span class=\"hljs-params\">entry<\/span> =&gt;<\/span> {\n    <span class=\"hljs-keyword\">if<\/span> (entry.isIntersecting) {\n      <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">'Element jest widoczny'<\/span>);\n    } <span class=\"hljs-keyword\">else<\/span> {\n      <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">'Element jest poza obszarem widzenia'<\/span>);\n    }\n  });\n}\n\n<span class=\"hljs-comment\">\/\/ konfiguracja domy\u015blna<\/span>\n<span class=\"hljs-keyword\">const<\/span> observer = <span class=\"hljs-keyword\">new<\/span> IntersectionObserver(itersectionObserverCallback);\n\n<span class=\"hljs-built_in\">document<\/span>.querySelectorAll(<span class=\"hljs-string\">'.ad'<\/span>).forEach(<span class=\"hljs-function\"><span class=\"hljs-params\">element<\/span> =&gt;<\/span> observer.observe(element));<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Kod wybierze wszystkie elementy z klas\u0105 <code>.ad<\/code> i zacznie je obserwowa\u0107. Gdy znikn\u0105 lub pojawi\u0105 si\u0119 na ekranie, w konsoli zostanie zalogowany odpowiedni komunikat. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">ResizeObserver<\/h2>\n\n\n\n<p>Ostatnim prezentowanym dzisiaj obserwatorem jest <strong><a rel=\"noreferrer noopener\" href=\"https:\/\/drafts.csswg.org\/resize-observer\/#resize-observer-interface\" data-type=\"URL\" data-id=\"https:\/\/drafts.csswg.org\/resize-observer\/#resize-observer-interface\" target=\"_blank\">ResizeObserver<\/a><\/strong> i s\u0142u\u017cy do obserwowania rozmiaru elementu. Chocia\u017c wi\u0119kszo\u015b\u0107 zada\u0144 zwi\u0105zanych z rozmiarem mo\u017cna wykona\u0107 za pomoc\u0105 <code>media query<\/code>, czasem istniej\u0105 specyficzne przypadki, kiedy ResizeObserver znajdzie zastosowanie.<\/p>\n\n\n\n<p>Konstruktor jako parametr otrzymuje Callback, kt\u00f3ry przyjmuje dwa argumenty: tablic\u0119 z <strong><a rel=\"noreferrer noopener\" href=\"https:\/\/drafts.csswg.org\/resize-observer\/#resizeobserverentry\" data-type=\"URL\" data-id=\"https:\/\/drafts.csswg.org\/resize-observer\/#resizeobserverentry\" target=\"_blank\">ResizeObserverEntry<\/a><\/strong> oraz instancj\u0119 obserwatora.<\/p>\n\n\n\n<p>Do zainicjowania obserwacji s\u0142u\u017cy metoda <strong>observe<\/strong>, kt\u00f3ra jako argument przyjmuje obiekt dziedzicz\u0105cy po <a rel=\"noreferrer noopener\" href=\"https:\/\/dom.spec.whatwg.org\/#element\" data-type=\"URL\" data-id=\"https:\/\/dom.spec.whatwg.org\/#element\" target=\"_blank\">Element<\/a> oraz <a rel=\"noreferrer noopener\" href=\"https:\/\/drafts.csswg.org\/resize-observer\/#dictdef-resizeobserveroptions\" data-type=\"URL\" data-id=\"https:\/\/drafts.csswg.org\/resize-observer\/#dictdef-resizeobserveroptions\" target=\"_blank\">konfiguracj\u0119<\/a>. W chwili pisania tego artyku\u0142u, konfiguracja ma tylko jedn\u0105 w\u0142a\u015bciwo\u015b\u0107 <strong>box <\/strong>z mo\u017cliwymi parametrami: <code>border-box, content-box, device-pixel-content-box<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Przyk\u0142ad<\/h3>\n\n\n\n<p>Za\u0142\u00f3\u017cmy, \u017ce na stronie znajduj\u0105 si\u0119 reklamy, kt\u00f3re s\u0105 pobierane dynamicznie. Zanim zostan\u0105 pobrane i wy\u015bwietlany jest obraz zast\u0119pczy. Chcemy dynamicznie zamienia\u0107 obraz zast\u0119pczy z prawdziw\u0105 reklam\u0105.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">resizeObserverCallback<\/span>(<span class=\"hljs-params\">entries, observer<\/span>) <\/span>{\n  entries.forEach(<span class=\"hljs-function\"><span class=\"hljs-params\">entry<\/span> =&gt;<\/span> {\n    <span class=\"hljs-keyword\">if<\/span> (entry.contentRect.height &gt; <span class=\"hljs-number\">20<\/span>) {\n      entry.target.nextElementSibling.remove();\n      observer.unsubscribe(entry.target);\n    }\n  });\n}\n\n<span class=\"hljs-keyword\">const<\/span> observer = <span class=\"hljs-keyword\">new<\/span> ResizeObserver(resizeObserverCallback);\n\n<span class=\"hljs-built_in\">document<\/span>.querySelectorAll(<span class=\"hljs-string\">'.ad'<\/span>).forEach(<span class=\"hljs-function\"><span class=\"hljs-params\">element<\/span> =&gt;<\/span> observer.observe(element));<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><br>Je\u015bli reklama b\u0119dzie mia\u0142a wysoko\u015b\u0107 wi\u0119ksz\u0105 ni\u017c <code>20px<\/code>, wtedy zostanie usuni\u0119ty element wyst\u0119puj\u0105cy po nim. W naszym przyk\u0142adzie jest to domy\u015blna reklama. Zadbali\u015bmy r\u00f3wnie\u017c o zaprzestanie obserwacji na elemencie <code>.ad<\/code>, gdy reklama zostanie wy\u015bwietlona.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Podsumowanie<\/h2>\n\n\n\n<p>Observators w JavaScript to pot\u0119\u017cne narz\u0119dzia, kt\u00f3re pozwalaj\u0105 wprowadzi\u0107 jeszcze lepsz\u0105 interakcj\u0119 z stron\u0105 internetow\u0105. Jednak niew\u0142a\u015bciwe lub nadmierne ich stosowanie mo\u017ce pogorszy\u0107 wydajno\u015b\u0107. Dlatego powinny by\u0107 stosowane z ostro\u017cno\u015bci\u0105, a konfiguracja powinna by\u0107 dostosowana do wymaga\u0144.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Istnieje kilka interfejs\u00f3w w JS do obserwacji element\u00f3w. W tym artykule troch\u0119 si\u0119 im przyjrzymy.<\/p>\n","protected":false},"author":17,"featured_media":737,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[10,4],"tags":[89,58,36,35],"class_list":["post-731","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-js","category-web-development","tag-it","tag-js","tag-web-development","tag-wordpress"],"_links":{"self":[{"href":"https:\/\/webdevs.blog\/pl\/wp-json\/wp\/v2\/posts\/731","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/webdevs.blog\/pl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/webdevs.blog\/pl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/webdevs.blog\/pl\/wp-json\/wp\/v2\/users\/17"}],"replies":[{"embeddable":true,"href":"https:\/\/webdevs.blog\/pl\/wp-json\/wp\/v2\/comments?post=731"}],"version-history":[{"count":9,"href":"https:\/\/webdevs.blog\/pl\/wp-json\/wp\/v2\/posts\/731\/revisions"}],"predecessor-version":[{"id":762,"href":"https:\/\/webdevs.blog\/pl\/wp-json\/wp\/v2\/posts\/731\/revisions\/762"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/webdevs.blog\/pl\/wp-json\/wp\/v2\/media\/737"}],"wp:attachment":[{"href":"https:\/\/webdevs.blog\/pl\/wp-json\/wp\/v2\/media?parent=731"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/webdevs.blog\/pl\/wp-json\/wp\/v2\/categories?post=731"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/webdevs.blog\/pl\/wp-json\/wp\/v2\/tags?post=731"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}