Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mutation observer #167

Merged
merged 2 commits into from
Mar 8, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 73 additions & 73 deletions 2-ui/99-ui-misc/01-mutation-observer/article.md
Original file line number Diff line number Diff line change
@@ -1,52 +1,52 @@

# Mutation observer
# মিউটেশন অভজার্ভার

`MutationObserver` is a built-in object that observes a DOM element and fires a callback when it detects a change.
`MutationObserver` হল একটি বিল্ট-ইন-অবজেক্ট যা DOM এলিমেন্টকে অবজার্ভ করে এবং এলিমেন্টে এ কোন পরিবর্তন হলে একটি কলব্যাক কল করে।

We'll first take a look at the syntax, and then explore a real-world use case, to see where such thing may be useful.
প্রথমে আমরা এটির সিনট্যাক্সটি দেখব, এরপর দেখব এটি কোন ক্ষেত্রে ব্যবহার করতে পারি।

## Syntax
## সিনট্যাক্স

`MutationObserver` is easy to use.
`MutationObserver` ডিক্লেয়ারের নিয়ম হল।

First, we create an observer with a callback-function:
প্রথমে, একটি কলব্যাক সহ অবজার্ভার ডিক্লেয়ার করা:

```js
let observer = new MutationObserver(callback);
```

And then attach it to a DOM node:
তারপর এর সাথে একটি DOM নোড সংযুক্ত করব:

```js
observer.observe(node, config);
```

`config` is an object with boolean options "what kind of changes to react on":
- `childList` -- changes in the direct children of `node`,
- `subtree` -- in all descendants of `node`,
- `attributes` -- attributes of `node`,
- `attributeFilter` -- an array of attribute names, to observe only selected ones.
- `characterData` -- whether to observe `node.data` (text content),
`config` একটি অবজেক্ট যার কিছু বুলিয়ান টাইপের প্রপার্টি আছে, প্রপার্টি গুলো দ্বারা বুঝায় DOM এ কোন ধরণের পরিবর্তনের জন্য কলব্যাকটি কল হবে:
- `childList` -- `node` এর ইমিডিয়েট চাইল্ড এলিমেন্ট এ কোন পরিবর্তন হলে,
- `subtree` -- `node` এর সাব-ট্রিতে কোন পরিবর্তন হলে,
- `attributes` -- `node` এর অ্যাট্রিবিউটে কোন পরিবর্তন হলে,
- `attributeFilter` -- অ্যাট্রিবিউটের একটি অ্যারে, অ্যারের মধ্যে উল্লেখিত অ্যাট্রিবিউটসমূহের মধ্যে কোন পরিবর্তন হলে,
- `characterData` -- `node.data` প্রপার্টির কোন পরিবর্তন হলে (text content),

Few other options:
- `attributeOldValue` -- if `true`, pass both the old and the new value of attribute to callback (see below), otherwise only the new one (needs `attributes` option),
- `characterDataOldValue` -- if `true`, pass both the old and the new value of `node.data` to callback (see below), otherwise only the new one (needs `characterData` option).
আরো কিছু প্রপার্টি:
- `attributeOldValue` -- যদি `true` হয় ও `attributes` এর মান `true`, তখন কলব্যাকে অ্যাট্রিবিউটের নতুন এবং পুরনো দুটিরই মান পাব অন্যথায় শুধু নতুন মানটি পাব,
- `characterDataOldValue` -- যদি `true` হয়, তাহলে পরিবর্তনের পূর্বের মান এবং পরিবর্তীত মান `node.data` দুটিই রিটার্ন করে (নিচে দেখুন), অন্যথায় শুধু পরিবর্তীত মানটি দেখায়(এটি হয় যদি কনফিগ এ `characterData` `true` হয়)।

Then after any changes, the `callback` is executed: changes are passed in the first argument as a list of [MutationRecord](https://dom.spec.whatwg.org/#mutationrecord) objects, and the observer itself as the second argument.
যখন নোডে কোন পরিবর্তন হয়, তখন `callback` এক্সিকিউট হয়: এবং প্রথম আর্গুমেন্টটি হয় [MutationRecord](https://dom.spec.whatwg.org/#mutationrecord) অবজেক্ট ও দ্বিতীয় আর্গুমেন্ট হল observer অবজেক্টটি।

[MutationRecord](https://dom.spec.whatwg.org/#mutationrecord) objects have properties:
`MutationRecord` অবজেক্টের প্রপার্টিসমূহ:

- `type` -- mutation type, one of
- `"attributes"`: attribute modified
- `"characterData"`: data modified, used for text nodes,
- `"childList"`: child elements added/removed,
- `target` -- where the change occurred: an element for `"attributes"`, or text node for `"characterData"`, or an element for a `"childList"` mutation,
- `addedNodes/removedNodes` -- nodes that were added/removed,
- `previousSibling/nextSibling` -- the previous and next sibling to added/removed nodes,
- `attributeName/attributeNamespace` -- the name/namespace (for XML) of the changed attribute,
- `oldValue` -- the previous value, only for attribute or text changes, if the corresponding option is set `attributeOldValue`/`characterDataOldValue`.
- `type` -- মিউটেশন টাইপ
- `"attributes"`: পরিবর্তিত অ্যাট্রিবিউট
- `"characterData"`: পরিবর্তিত ডাটা, টেক্সট নোডে ব্যবহার করা হয়,
- `"childList"`: added/removed চাইল্ড এলিমেন্টসমূহ,
- `target` -- এলিমেন্টের যে প্রপার্টিতে পরিবর্তন হয়: `"attributes"`, `"characterData"`, `"childList"`,
- `addedNodes/removedNodes` -- added/removed নোড,
- `previousSibling/nextSibling` -- *previous sibling nodes* বা *next sibling nodes* এর added/removed এর জন্য,
- `attributeName/attributeNamespace` -- পরিবর্তিত attribute name/namespace (XML এর জন্য),
- `oldValue` -- পরিবর্তনের আগের মানটি, যদি `config``attributeOldValue`/`characterDataOldValue` সেট হয়।

For example, here's a `<div>` with a `contentEditable` attribute. That attribute allows us to focus on it and edit.
যেমন, এখানে একটি `<div>` এলিমেন্ট আছে, এবং এর অ্যাট্রিবিউট `contentEditable` সেট করছি। যার ফলে আমরা কন্টেন্ট কে পরিবর্তন করতে পারব।

```html run
<div contentEditable id="elem">Click and <b>edit</b>, please</div>
@@ -65,7 +65,7 @@ observer.observe(elem, {
</script>
```

If we run this code in the browser, then focus on the given `<div>` and change the text inside `<b>edit</b>`, `console.log` will show one mutation:
যদি আমরা কোডটি রান করি, এবং `<div>` এ ফোকাস করে চাইল্ড নোড `<b>edit</b>` এর কন্টেন্ট পরিবর্তন করি তাহলে `console.log` এ এই mutationRecords টি দেখব:

```js
mutationRecords = [{
@@ -76,7 +76,7 @@ mutationRecords = [{
}];
```

If we make more complex editing operations, e.g. remove the `<b>edit</b>`, the mutation event may contain multiple mutation records:
যদি আমরা `<b>edit</b>` নোডটি রিমুভ করে দেয়, তখন mutationRecords টি হবে:

```js
mutationRecords = [{
@@ -95,29 +95,29 @@ mutationRecords = [{
}];
```

So, `MutationObserver` allows to react on any changes within DOM subtree.
সুতরাং আমরা দেখলাম, `MutationObserver` দ্বারা আমরা DOM এর সাবট্রির পরিবর্তনও অবজার্ভ করতে পারি।

## Usage for integration
## থার্ড পার্টি লাইব্রেরী ইন্ট্রিগেশন

When such thing may be useful?
কখন `MutationObserver` ব্যবহার করতে পারি?

Imagine the situation when you need to add a third-party script that contains useful functionality, but also does something unwanted, e.g. shows ads `<div class="ads">Unwanted ads</div>`.
মনে করুন আপনি একটি দারুণ থার্ড পার্টি লাইব্রেরী ব্যবহার করছেন, তবে এর কিছু ফাংশনালিটি আছে যা আপনি চান না, যেমন এড দেখানো `<div class="ads">Unwanted ads</div>`

Naturally, the third-party script provides no mechanisms to remove it.
সাধারণত, এই ফাংশনালিটিগুলো রিমুভের জন্য লাইব্রেরী গুলো কোন API প্রভাইড করে না।

Using `MutationObserver`, we can detect when the unwanted element appears in our DOM and remove it.
এক্ষেত্রে `MutationObserver` এর সাহায্যে আমরা আমাদের DOM এর পরিবর্তন নির্ণয় করে তা রিমুভ করতে পারি।

There are other situations when a third-party script adds something into our document, and we'd like to detect, when it happens, to adapt our page, dynamically resize something etc.
এছাড়াও আমরা যখন কোন থার্ড পার্টি স্ক্রিপ্ট ব্যবহার করি এবং তা যদি DOM এ কোন এলিমেন্ট সংযুক্ত করে তখন তা নির্ণয় করে ডায়নামিক্যালি DOM এলিমেন্টের সাইজ পরিবর্তন করতে হয়।

`MutationObserver` allows to implement this.
এইসব ক্ষেত্রে `MutationObserver` এর সাহায্যে এমন ফাংশনালিটি ইমপ্লিমেন্ট করতে পারি।

## Usage for architecture
## আর্কিটেকচার

There are also situations when `MutationObserver` is good from architectural standpoint.
এছাড়াও আমাদের প্রজেক্টের ভাল স্ট্রাকচারের জন্য `MutationObserver` ব্যবহার করতে পারি।

Let's say we're making a website about programming. Naturally, articles and other materials may contain source code snippets.
মনে করুন আমরা প্রোগ্রামিং সম্পর্কিত একটি ওয়েবসাইট বানাব। সাধারণত, এক্ষেত্রে আমাদের আর্টিকেল বা অন্যান্য রিসোর্স দেখাতে সোর্স কোড স্নিপেট ব্যবহার করা লাগে।

Such snippet in an HTML markup looks like this:
যেমন HTML মার্কআপ স্নিপেটে দেখতে এমন হবে:

```html
...
@@ -128,29 +128,29 @@ Such snippet in an HTML markup looks like this:
...
```

For better readability and at the same time, to beautify it, we'll be using a JavaScript syntax highlighting library on our site, like [Prism.js](https://prismjs.com/). To get syntax highlighting for above snippet in Prism, `Prism.highlightElem(pre)` is called, which examines the contents of such `pre` elements and adds special tags and styles for colored syntax highlighting into those elements, similar to what you see in examples here, on this page.
কোড কে পঠনযোগ্য এবং সুন্দরভাবে দেখাতে, আমরা আমাদের সাইটে একটি সিনট্যাক্স হাইলাইটিং লাইব্রেরী ব্যবহার করছি, যেমন [Prism.js](https://prismjs.com/)। সিনট্যাক্স হাইলাইটিং এর জন্য প্রিজমার একটি ফাংশন `Prism.highlightElem(pre)` কল করতে হয়, যা `pre` এলিমেন্টকে অবজার্ভ করে এবং ঐ এলিমেন্টের কোডের বৈশিষ্ট্যের উপর নির্ভর বিশেষ ট্যাগ এবং স্ট্যাইল সংযুক্ত করে, যেমন উপরের কোডে দেখুন।

When exactly should we run that highlighting method? Well, we can do it on `DOMContentLoaded` event, or put the script at the bottom of the page. The moment our DOM is ready, we can search for elements `pre[class*="language"]` and call `Prism.highlightElem` on them:
এখন প্রশ্ন, কখন `Prism.highlightElem(pre)` মেথডটি কল করব? আমরা চাইলে ফাংশনটি `DOMContentLoaded` ইভেন্ট ট্রিগারে বা পেজের একদম নিচে স্ক্রিপটি সংযুক্ত করতে পারি, তাহল DOM রেডি হওয়ার পর আমরা `pre[class*="language"]` এলিমেন্টগুলো খুঁজে `Prism.highlightElem` কে কল করতে পারি:

```js
// highlight all code snippets on the page
document.querySelectorAll('pre[class*="language"]').forEach(Prism.highlightElem);
```

Everything's simple so far, right? We find code snippets in HTML and highlight them.
এ পর্যন্ত সব ঠিকঠাক তাই না? আমরা স্নিপেট প্রয়োগ হবে এমন এলিমেন্টগুলো খুঁজে বের করে তাদের জন্য সিনট্যাক্স হাইলাইটার ব্যবহার করব।

Now let's go on. Let's say we're going to dynamically fetch materials from a server. We'll study methods for that [later in the tutorial](info:fetch). For now it only matters that we fetch an HTML article from a webserver and display it on demand:
এখন ধরা যাক আমরা ডায়নামিক্যালি কোন কন্টেন্ট সার্ভার হতে রেন্ডার করব। [পরবর্তীতে এই অধ্যায়ে শিখব](info:fetch)। আপাতত ধরে নিন আমরা সার্ভার হতে কোন ডাটা রেন্ডার করে DOM এ দেখাচ্ছি:

```js
let article = /* fetch new content from server */
articleElem.innerHTML = article;
```

The new `article` HTML may contain code snippets. We need to call `Prism.highlightElem` on them, otherwise they won't get highlighted.
এখন ধরুন `article` এর কন্টেন্টে কিছু কোড স্নিপেট আছে। যার জন্য আমাদের `Prism.highlightElem` কে কল করতে হবে, অন্যথায় আমরা সিনট্যাক্স হাইলাইটিং দেখব না.

**Where and when to call `Prism.highlightElem` for a dynamically loaded article?**
**তাহলে কিভাবে ডায়নামিক্যালি লোড হওয়া আর্টিকেলের জন্য আমরা `Prism.highlightElem` কে কল করব?**

We could append that call to the code that loads an article, like this:
আমরা চাইলে আর্টিকেল লোড হওয়ার পর এভাবে সংযুক্ত করতে পারি:

```js
let article = /* fetch new content from server */
@@ -162,21 +162,21 @@ snippets.forEach(Prism.highlightElem);
*/!*
```

...But, imagine if we have many places in the code where we load our content - articles, quizzes, forum posts, etc. Do we need to put the highlighting call everywhere, to highlight the code in content after loading? That's not very convenient.
...কিন্তু, ভাবুন আমাদের আরো অনেক জায়গায় এমন ডায়নামিক কন্টেন্ট আছে যেমন আর্টিকেল, কুইজ, ফোরাম পোস্ট, ইত্যাদি। এজন্য কি আমরা প্রতিটি কন্টেন্ট লোড হওয়ার পর সিনট্যাক্স হাইলাইটারকে কল করব? এটি তেমন ভালো কোন সমাধান হতে পারে না।

And what if the content is loaded by a third-party module? For example, we have a forum written by someone else, that loads content dynamically, and we'd like to add syntax highlighting to it. No one likes patching third-party scripts.
আবার মনে করুন আমাদের একটি ফোরাম আছে যা লোড হয় একটি থার্ড-পার্টি লাইব্রেরী থেকে, এবং এর মধ্যে বিভিন্ন ধরণের কোড থাকতে পারে এখন কন্টেন্টে আমরা সিনট্যাক্স হাইলাইট করতে চাই, কিন্তু আমাদের থার্ড পার্টি লাইব্রেরীটি সিনট্যাক্স হাইলাইটিং ফিচার প্রদান করে না।

Luckily, there's another option.
তবে এটি আমরা আরেকভাবে করতে পারি।

We can use `MutationObserver` to automatically detect when code snippets are inserted into the page and highlight them.
আমরা DOM কে অবজার্ভ করে `MutationObserver` এর সাহায্যে স্বয়ংক্রিয়ভাবে কন্টেন্টকে হাইলাইট করতে পারি।

So we'll handle the highlighting functionality in one place, relieving us from the need to integrate it.
তাহলে আমরা একটি জায়গায় ফাংশনালিটি ইমপ্লিমেন্ট করলে হবে, এরফলে আমাদের কোড হবে সহজে পরিবর্তনযোগ্য।

### Dynamic highlight demo
### ডায়নামিক সিনট্যাক্স হাইলাইটার

Here's the working example.
এখানে একটি উদাহরন দেয়া হল।

If you run this code, it starts observing the element below and highlighting any code snippets that appear there:
কোডটি রান করুন, এটি আমাদের এলিমেন্টকে অবজার্ভ করে এবং কোড স্নিপেটের জন্য `Prism.highlightElement` কে কল করবে:

```js run
let observer = new MutationObserver(mutations => {
@@ -207,13 +207,13 @@ let demoElem = document.getElementById('highlight-demo');
observer.observe(demoElem, {childList: true, subtree: true});
```

Here, below, there's an HTML-element and JavaScript that dynamically fills it using `innerHTML`.
এখন আমরা ডায়নামিক্যালি কন্টেন্ট `demoElem.innerHTML` এ অ্যাসাইন করব।

Please run the previous code (above, observes that element), and then the code below. You'll see how `MutationObserver` detects and highlights the snippet.
প্রথমে উপরের কোডটি রান করুন, এবং তারপর নিচেরটি রান করুন। এখন দেখবেন `MutationObserver` আমাদের কন্টেন্ট অবজার্ভ করে কোড স্নিপেটকে হাইলাইট করবে।

<p id="highlight-demo" style="border: 1px solid #ddd">A demo-element with <code>id="highlight-demo"</code>, run the code above to observe it.</p>

The following code populates its `innerHTML`, that causes the `MutationObserver` to react and highlight its contents:
নিচের কোডটিতে `MutationObserver` `highlight-demo` নোডটিকে অবজার্ভ করে এবং দেখে তার কন্টেন্ট `innerHTML` পরিবর্তন হয়েছে, ফলে `MutationObserver` এক্সিকিউট হবে এবং এটি `Prism.highlightElement` কে কল করে:

```js run
let demoElem = document.getElementById('highlight-demo');
@@ -228,19 +228,19 @@ demoElem.innerHTML = `A code snippet is below:
`;
```

Now we have `MutationObserver` that can track all highlighting in observed elements or the whole `document`. We can add/remove code snippets in HTML without thinking about it.
এভাবে আমরা `MutationObserver` দ্বারা আমাদের `document` এ যেকোন পরিবর্তন ট্র্যাক করে তার উপর নির্ভর করে কোন একটি ফাংশন কল করতে পারি।

## Additional methods
## অন্যান্য মেথড

There's a method to stop observing the node:
`MutationObserver` এর আরো কিছু মেথড আছে:

- `observer.disconnect()` -- stops the observation.
- `observer.disconnect()` -- অবজার্ভেশন করা বন্ধ করে।

When we stop the observing, it might be possible that some changes were not yet processed by the observer. In such cases, we use
যখন আমরা ``observer.disconnect()`` কে কল করি, সেসময় এমন হতে পারে DOM এ কোন কিছু পরিবর্তন হয়েছে কিন্তু তাদের জন্য তখনো observer কল হয়নি। এক্ষেত্রে আমরা ব্যবহার করতে পারি

- `observer.takeRecords()` -- gets a list of unprocessed mutation records - those that happened, but the callback has not handled them.
- `observer.takeRecords()` -- যা QUEUE তে থাকা যেসব মিউটেশনের জন্য আমাদের কলব্যাক হ্যান্ডেলার কল হয়নি তাদের তালিকাটি রিটার্ন করে।

These methods can be used together, like this:
এক্ষেত্রে মেথডদুটিকে এভাবে ব্যবহার করতে পারি:

```js
// get a list of unprocessed mutations
@@ -254,8 +254,8 @@ observer.disconnect();
```


```smart header="Records returned by `observer.takeRecords()` are removed from the processing queue"
The callback won't be called for records, returned by `observer.takeRecords()`.
```smart header="`observer.takeRecords()` দ্বারা রিটার্নকৃত মিউটেশন রেকর্ডগুলো QUEUE থেকে রিমুভড হয়"
`observer.takeRecords()` এর জন্য যেসব মিউটেশন রিটার্ন হয় তাদের জন্য কলব্যাকটি এক্সিকিউট হবে না।
```
```smart header="Garbage collection interaction"
@@ -264,10 +264,10 @@ Observers use weak references to nodes internally. That is, if a node is removed
The mere fact that a DOM node is observed doesn't prevent the garbage collection.
```

## Summary
## সারাংশ

`MutationObserver` can react to changes in DOM - attributes, text content and adding/removing elements.
`MutationObserver` দ্বারা DOM এ কোন এলিমেন্টকে অবজার্ভ করতে পারি, যার সাহায্যে নির্ণয় করতে পারি কোন অ্যাট্রিবিউট, কন্টেন্ট বা এলিমেন্ট সংযুক্ত হয়েছে বা বাদ পরেছে কিনা।

We can use it to track changes introduced by other parts of our code, as well as to integrate with third-party scripts.
এটি দ্বারা আমরা আমাদের নিজস্ব স্ক্রিপ্ট বা থার্ড পার্টি স্ক্রিপ্ট দ্বারা DOM এর পরিবর্তন ট্র্যাক করতে পারি।

`MutationObserver` can track any changes. The config "what to observe" options are used for optimizations, not to spend resources on unneeded callback invocations.
`MutationObserver` যেকোন পরিবর্তন ট্র্যাক করতে পারে, তবে অপ্টিমাইজেশনের জন্য আমরা যা যা অবজার্ভ করতে হবে কনফিগারেশনে শুধুমাত্র তাদের পাঠাতে পারি, এভাবে আমরা অপ্রয়োজনীয় মিউটেশন কল এড়াতে পারি।