JavaScript’te MutationObserver’ın Gücü – JSManifest

JavaScript'te MutationObserver'ın Gücü – JSManifest

Modern programlamada, ne olarak bilinen şey hakkında çok fazla katılım ve tartışma vardır. durum, en yaygın olarak kullanıcı etkileşimi etrafında döner. bu Mutasyon Gözlemcisi kullanıcı etkileşimine tepki vermek için kullanabileceğimiz için bu konuda bize yardımcı olabilecek güçlü bir API’dir. Mirasın yerini alan şey bu Mutasyon Olayları DOM3 Events spesifikasyonundan API.

Oluşturmak için MutationObserver ile somutlaştırmamız gerekiyor new şöyle bir geri arama ile birlikte anahtar kelime:

const observer = new MutationObserver(function onMutation(mutations, observer) {
  console.log('Mutations', mutations)
})

bu onMutation geri arama, aşağıda (sırasıyla) belirtildiği gibi iki argüman alır:

  1. Mutasyonlar – Bir liste Mutasyon Kaydı nesneler
  2. Gözlemci – MutationObserver bu inşa edildi. En son örneğimizde, observer değişken.

bu onMutation DOM düğümlerinde her değişiklik veya mutasyon olduğunda geri arama başlatılır. observe yöntem.

bu observe yöntem, ilk argümanı olarak “dinlemek” için bir DOM öğesini ve ikinci argümanı olarak bir seçenekler nesnesini alır:

const container = document.getElementById('root')
const options = { childList: true }

observer.observe(container, options)

DOM düğümünde herhangi bir değişiklik olduğunda, geri arama başlatılır ve bir liste alır. MutationRecord nesneleri içeren ilk argümanı olarak tip meydana gelen mutasyon ve ne olduğu hakkında ek bilgi. Kullanıcı deneyiminde düzgün iyileştirmeler geliştirmek için kullanabileceğimiz bu kayıt nesneleridir.

Örneğin, bir öğeyi gözlemlemeye başlayabilir ve çocukları eklediğimizde, çıkardığımızda ve hatta başka çocuklarla değiştirdiğimizde haberdar olabiliriz, ancak bu bilgiyi ileterek istemek zorundayız. childList: true içinde seçenekler argüman:

const observer = new MutationObserver(function onMutation(mutations, observer) {
  console.log('Mutations', mutations)
})

observer.observe(root, {
  childList: true,
})

const textarea = document.createElement('textarea')
const select = document.createElement('select')
root.appendChild(textarea)
root.replaceChild(select, textarea)

Başka bilgiler de isteyebiliriz. Geçebileceğimiz seçeneklerin tam listesi:

const observer = new MutationObserver(function onMutation(mutations, observer) {
  console.log('Mutations', mutations)
})

observer.observe(root, {
  attributes: true,
  attributeOldValue: true,
  attributeFilter: [],
  childList: true,
  characterData: true,
  characterDataOldValue: true,
  subtree: false,
})

istediğimizde attributes ve bir özniteliğin değeri değiştirildiyse, bir önceki değeri değiştirilmeden önce girerek gözlemleyebiliyoruz. attributeOldValue: true şöyle:

observer.observe(root, {
  attributes: true,
  attributeOldValue: true,
})

root.style.visibility = 'hidden'
root.style.visibility = 'visible'

2

bu attributeFilter seçeneği, umursamadığımız mutasyonları filtrelemek için kullanılır. Mantıksal olarak, uygulama ne kadar büyükse, bu o kadar arzu edilir hale gelir, aksi takdirde, büyük olasılıkla o gözlemcide asla ilişkilendirmeyeceğimiz bir dizi değişiklik için hızlı bir şekilde çağrı yapabiliriz:

root.hidden = true
root.hidden = false

root.id = 'myid123'
root.id = 'yourid345'

root.title = 'root-document'
root.title = 'Root document'

root.onclick = () => {}

root.tabIndex = 1
root.option = 'f'

const someHttpRequest = async () => {
  const res = await fetch('https://www.google.com')
  return res.text()
}

someHttpRequest().then((text) => {
  const span = document.createElement('span')
  span.innerHTML += text.substring(0, 100)
  root.dataset.something = span.innerHTML
  root.style.backgroundColor = 'red'
})

3

Yalnızca ne zaman bildirim almayı umursarsak hidden ve title değişiklikler, bunu filtre seçeneğine koyabiliriz:

observer.observe(root, {
  attributes: true,
  attributeOldValue: true,
  attributeFilter: ['hidden', 'title'],
})

Bu, sonuçları azaltır ve geri çağırma uygulamasında onu filtrelememiz gerekmeyecektir (bu, işlev bloğunu şişirecek ve gelecekte kodumuzu korumayı zorlaştıracaktır):

4

ayarladığımızda subtree: true seçeneklerde, gözlemlenen öğenin tüm alt öğelerinde meydana gelen değişikliklerden haberdar oluruz:

observer.observe(root, {
  attributes: true,
  attributeOldValue: true,
  subtree: true,
})
someHttpRequest().then((text) => {
  const span = document.createElement('span')
  span.innerHTML += text.substring(0, 100)
  root.dataset.something = span.innerHTML
  root.style.backgroundColor = 'red'
})

const form = document.createElement('form')
const input = document.createElement('input')
const select = document.createElement('select')

root.appendChild(form)
form.appendChild(input)
form.appendChild(select)

const options = ['100', '200', '300']
options.forEach((value) => {
  const option = document.createElement('option')
  select.appendChild(option)
  option.text = value
  option.value = value
  option.style.color = 'red'
  option.style.position = 'relative'
})
select.selectedIndex = 0

5

eğer ayarlarsak characterData true olarak, aşağıdaki gibi öğelerin içindeki metin içeriği değişikliklerinden haberdar olabiliriz:

observer.observe(root, {
  characterData: true,
  characterDataOldValue: true,
  subtree: true,
})

const root = document.createElement('root')
const form = document.createElement('form')
const select = document.createElement('select')
const selectLabel = document.createTextNode('Select an option')
const br = document.createElement('br')
const label = document.createElement('label')

label.appendChild(selectLabel)
root.appendChild(form)
form.append(br)
form.appendChild(selectLabel)
form.appendChild(select)

const options = ['100', '200', '300']
options.forEach((value) => {
  const option = document.createElement('option')
  select.appendChild(option)
  option.value = value
  option.text = value
  option.style.color = 'red'
  option.style.position = 'relative'
})
select.selectedIndex = 0
select.onchange = (e) => (selectLabel.data = `You selected:  ${e.target.value}`)

Şimdi, seçeneklerin seçilmesi, gözlemcideki geri aramamızı arayacaktır:

6

Not: characterData seçeneği, yalnızca metin içeriği değiştiğinde bizi bilgilendirecektir. Metin düğüm.

Yani bunun yerine:

const elem = document.createElement('div')
root.appendChild(elem)
elem.innerHTML = ''
elem.innerHTML = 'hello'

bir oluşturmalıyız Text düğümü ve metnini üzerine ayarlayın databu geri aramayı tetikler:

const elem = document.createElement('div')
const textNode = document.createTextNode('')
elem.appendChild(textNode)
root.appendChild(elem)
textNode.data = 'hello'

Artık nasıl olduğuna dair temel bir anlayışa sahip olduğunuza göre, MutationObserver çalışır, kullanıcı deneyimini geliştirmek için onu kullanan bir şeyi hızlı bir şekilde oluşturalım!

Kullanıcıların profil sayfalarını özelleştirmelerine olanak tanıyan bir sayfa oluşturduğumuzu varsayalım. Onlara görsel yapıya karar vermeleri için kutu ekleme seçeneği veriyoruz ve şuna benziyor:

7

Stil sayfasını, DOM yapısını ve JavaScript’i ( MutationObserver henüz kodda yok):

Stil sayfası:

.grid {
  display: flex;
}

.grid-item {
  flex-grow: 1;
  background-color: rgba(0, 0, 0, 0.2);
  margin: 3px;
  padding: 2px;
  border-radius: 5px;
  height: 150px;
  border: 1px solid rgba(50, 20, 255, 0.8);
}

HTML:

<div id="root">
  <div class="grid"></div>
  <button id="add">Add box</button>
</div>

JavaScript:

const grids = []
const root = document.getElementById('root')
const addBtn = document.getElementById('add')

function createGrid() {
  const grid = document.createElement('div')
  root.appendChild(grid)
  grid.classList.add('grid')
  grids.push(grid)
  return grid
}

function createGridBox(tagName, attrs) {
  const elem = document.createElement(tagName)
  elem.classList.add('grid-item')
  if (attrs !== null && typeof attr === 'object') {
    Object.entries(attrs).forEach(([attr, value]) => {
      if (attr === 'style') {
        Object.entries(value).forEach(([styleKey, styleValue]) => {
          elem.style[styleKey] = styleValue
        })
      } else {
        elem[attr] = value
      }
    })
  }
  return elem
}

function getMostRecentGrid() {
  if (grids.length === 1) return grids[0]
  if (grids.length > 1) return grids[grids.length - 1]
  return null
}

addBtn.addEventListener('click', function onClick(e) {
  let box = createGridBox('div')
  let grid = getMostRecentGrid() || createGrid()

  if (grid.children.length >= 3) {
    grid = createGrid()
  }

  grid.appendChild(box)
})

Şimdi butona tıkladığımızda ve kutular göründüğünde, nereden başlayacağımız konusunda biraz kafamız karışmış, hatta hangi kutuya odaklanmamız gerektiği konusunda biraz kaybolmuş hissediyoruz. Kullanıcıya az önce ekledikleri kutuya bakmaları gerektiğini bildirmek için en son kutuya bir vurgu ekleyerek bunu düzeltebiliriz. Daha sonra normal bir uygulamanın yaptığı gibi girişler, seçimler vb. Gibi kontroller ekleyebiliriz.

En yeni kutuyu vurgulamak, kullanıcıya en yeni kutunun aktif kutu olduğunu işaret etmek, her kutuya bakmak ve nereden başlayacağını şaşırmış hissetmek yerine kullanıcıyı ona yönlendirmek anlamına gelir:

8

kolayca kullanabiliriz MutationObserver Kutuları sayfaya eklenirken dinamik olarak vurgulamak (ve hatta vurgulamayı kaldırmak) için.

Böylece, daha iyi bir kullanıcı deneyimini geliştirmek için bir adım daha ileri gitmek için önceki örneğe ekleyebiliriz. MutationObserver api.

Önce aktif kutu için sınıf adı stilini ekleyelim:

.focused-grid-item {
  border: 1px solid magenta;
}

Daha sonra, sınıf adını eklendikçe kutulara eklemek/kaldırmak için bazı yardımcı işlevler oluşturabiliriz:

const isActive = (elem) => elem.classList.contains('focused-grid-item')
const highlight = (elem) =>
  !isActive(elem) && elem.classList.add('focused-grid-item')
const unhighlight = (elem) =>
  isActive(elem) && elem.classList.remove('focused-grid-item')

Şimdi geliyor MutationObserver kurtarmak için!

const observer = new MutationObserver(function onMutation(mutations, observer) {
  const lastMutation =
    mutations.length > 1 ? mutations[mutations.length - 1] : mutations[0]

  const { addedNodes, previousSibling, target } = lastMutation

  if (previousSibling) {
    unhighlight(addedNodes.item(0).previousElementSibling)
    highlight(addedNodes.item(0))
  } else {
    if (
      target.previousElementSibling &&
      target.previousElementSibling.lastElementChild
    ) {
      unhighlight(target.previousElementSibling.lastElementChild)
    }
    highlight(addedNodes.item(0))
  }
})

const options = { childList: true, subtree: true }
observer.observe(root, options)

8

Geri aramada, meydana gelen son mutasyonu alır, böylece en yeni eleman. Tüm ihtiyacımız olan bu MutationRecord içerdiği için target bizi doğrudan DOM öğesine götüren özellik. Bu DOM öğesi şunları içerir: previousSibling Odaklanmış stili ondan çıkarmak için erişmemiz gereken.

Şimdi çağrının döndürülen API’sini gözden geçirelim. new MutationObserver içimizde bize döner observer değişken:

const observer = new MutationObserver(function onMutation(mutations, observer) {...})

bu MutationObserver 3 yöntem döndürür observer kullanabilirsiniz. biz çoktan geçtik observe yöntem:

  1. disconnect()
  2. observe()
  3. takeRecords()

bu disconnect yöntem kaldırır observer mutasyonlardan daha fazla bildirim almaktan. Bu, uygulamanın kalan ömrü boyunca sessiz kalacağı anlamına gelir. observe tekrar bildirim almak için başka bir DOM öğesinde çağrılır.

bu takeRecords yöntem, mutasyonların geri kalanını alır (bir liste MutationRecord örnekler) ve bunları bir dizi olarak döndürür.

Çözüm

bu MutationObserver içeren güçlü bir api’dir. MutationEvent şimdi olan api kullanımdan kaldırıldı.

İle MutationObserver DOM ile çalışmayı ve talep üzerine meydana gelen değişikliklere tepki vermeyi kolaylaştırır. Bu, onu Google Chrome gibi tarayıcılar için web uzantıları geliştirmek için harika bir yol arkadaşı yapar.

Ve bu, bu yazının sonunu tamamlıyor! Umarım bunu değerli bulmuşsunuzdur ve gelecekte daha fazlasını ararsınız!

Bir cevap yazın

E-posta hesabınız yayımlanmayacak.