+ 2

Как отследить изменение количества элементов в HTML коллекции (document.getElementsByClassName) без таймера?

У меня есть живая HTML коллекция элементов с классами x-grid-cell-inner. Живая то есть если какой-то элемент теряет этот класс он исчезнет из коллекции автоматически. Мне в виду производительности нужно отслеживать количество элементов без таймера. Что-то типа mutationObserver.

17th May 2021, 8:03 PM
Влад Цислевский
Влад Цислевский - avatar
19 Respuestas
+ 6
Влад Цислевский I've created this code for you to review in case it helps. https://code.sololearn.com/WW9n05GZJ2ma/?ref=app
18th May 2021, 2:40 AM
David Carroll
David Carroll - avatar
+ 5
[Part 3 or 3] Влад Цислевский Something to consider with Option B is when refreshing the list, you will need to assign the new value to the original variable or to a property contained within some parent object. In my demo code, the parent for htmlCol is window. Ideally, I would have used something like: const cells = { nodes: document.querySelectorAll(".foo") } or... a custom MutableNodeList class that wraps all the logic needed to do the refresh. Consider the following function: const refreshNodes = (nodeList, selector) => { nodeList = document.querySelectorAll(selector) } refreshNodes(nodes, ".foo") Here... updating nodeList only changes the object reference for this variable, not the original variable outside the function. Alternatively... const refreshNodes = (parent, , propName, selector) => { parent[propName] = document.querySelectorAll(selector) } refreshNodes(cells, "nodes", ".foo") The property update will persist after the function call.
18th May 2021, 10:31 PM
David Carroll
David Carroll - avatar
+ 4
Влад Цислевский Why not use a MutationObserver like you suggested in your question? Are you looking for an alternative to MutationObserver or are you asking how to use the MutationObserver?
17th May 2021, 11:58 PM
David Carroll
David Carroll - avatar
+ 3
[Part 2 of 3] Влад Цислевский Two options I can think of are: -------- Option A: Convert NodeList to Array -------- Unless there's a reason you have to use NodeList, I recommend doing something like: let cells = [...document.querySelectorAll(".x-grid-cell-inner")] This will convert the NodeList to Array, which can be manipulated. Then, use MutationObserver to trigger some process that will remove or add nodes to the cells array. -------- Option B: Refresh the NodeList -------- If you absolutely must work with NodeList, you could use MutationObserver to trigger refreshing the collection when a node is added or removed. -------- Updated Code: -------- I updated my original code to hack something quickly together. The code is a bit messy, but, the proof of concept is tested against two global variables: htmlArr //implements Option A htmlCol //implements Option B Hope this helps. https://code.sololearn.com/WW9n05GZJ2ma/?ref=app
18th May 2021, 10:06 PM
David Carroll
David Carroll - avatar
+ 3
Vasiliy Indeed... React is definitely preferred here as it implements an abstraction layer with a virtual DOM plus synthetic events sitting between React Components and the DOM API. Combined with one way data flow via dispatched actions for updating a store, which in turn, flows immutable state changes back to the view, makes way for an architecture that eliminates so many complexities. Whew... that was a mouthful. 😂🤣
19th May 2021, 12:35 AM
David Carroll
David Carroll - avatar
+ 3
Влад Цислевский Based on these numbers, I would definitely rethink using the DOM as a source of truth for the data model. While it may be convenient, it's incredibly slow and only gets slower as it gets bigger in size. My experimenting up to this point has only been purely for exploring options. I would never use the DOM this way. Without being able to see the code, I'm not really going to be able to provide feedback that will be helpful. I wish you the best of luck. Sorry I wasn't able to figure things out with only limited information to work with.
19th May 2021, 1:44 PM
David Carroll
David Carroll - avatar
+ 2
[Part 1 of 3] Влад Цислевский Ah... I see. I misunderstood your reference to "live HTML Collection." I originally thought you were referring to Live vs Static NodeLists. https://developer.mozilla.org/en-US/docs/Web/API/NodeList#live_vs._static_nodelists Node.childNodes returns a "Live" NodeList - which is updated when the DOM is updated. On the other hand, Node.querySelectorAll() returns a "Static" NodeList. Static here means that changes in the DOM will not be reflected in the collection. The problem with NodeList is it's not an array. The NodeList interface locks down the underlying data such that items can not be inserted, removed, or replaced. So, there's no option to force modifying the NodeList using a sync. All said, I'll present some options to consider next. ******** NOTE: ******** Feel free to respond in Russian. I'll use Google Translate so it's easier for you. 😉
18th May 2021, 9:48 PM
David Carroll
David Carroll - avatar
+ 2
David Carroll As I understand it, Vlad needs to implement this without manipulation, changes should occur on the fly. I think this is easiest to implement with React. Oh, yes, you did it, I did not immediately see it. 👏👏👏👏👏👍☺️
18th May 2021, 11:04 PM
Solo
Solo - avatar
+ 2
Влад Цислевский Here's an updated version where I simplified the proof of concept using a custom wrapper class called LiveNodes. It really implements Option B where I refresh the static NodeList when DOM mutation occurs. https://code.sololearn.com/WDNpZn7RYwmy/?ref=app NOTE: This version doesn't work in Firefox because I'm using private hash properties.
19th May 2021, 7:41 AM
David Carroll
David Carroll - avatar
+ 2
Vasiliy Indeed... node.children reflects a Live NodeList. 👌 However, this won't be viable for this use case. The reason is nodes with class "x-grid-cell-inner" may be nested within other nodes; spread throughout the DOM; or mixed with other nodes that don't include this class name. Moreover, node.children.length won't actually change simply because the class attributes change. If the child node exists, it will be counted. On the other hand, node.children would work if the following conditions were both acceptable: 1.) The items in the NodeList are all sibling nodes that share the same parent node. 2.) No other nodes exist as siblings that should be filtered out.
19th May 2021, 6:43 PM
David Carroll
David Carroll - avatar
+ 1
если вам нужно только количество, то зачем вам MutationObserver, достаточно document.querySelectorAll(".x-grid-cell-inner").lenght
17th May 2021, 11:20 PM
Solo
Solo - avatar
+ 1
Влад Цислевский How frequently is this CSS name toggled in the DOM? How frequently is the length value checked? Is the setInterval checking innerText on all nodes only to verify if the static NodeList needs to be updated? If so, why would it check innerText instead of the class attribute? Why does the filtered NodeList need to be loaded separately and updated in real time? This could just be refreshed in real time as the count is needed. Do you have to use the static NodeList returned from the querySelectorAll() method or can you store those results in a different structure? What specifically is the static NodeList used for? Are the nodes only being read or are they being modified as well? The answers to these questions might open up some options to improve your solution.
19th May 2021, 6:58 PM
David Carroll
David Carroll - avatar
+ 1
David Carroll I agree, that's why I gave a simple example with html, since I could not explain in words as well as you. But if these conditions are met, then you can track any number of tags with any number of classes, or am I wrong?
19th May 2021, 7:10 PM
Solo
Solo - avatar
+ 1
Vasiliy Yeah... sounds right to me. 👌 Incidentally, if you review my earlier post with the label: [Part 3 of 3] I'm essentially describing a similar situation where refreshing the list will only work properly when the refreshed NodeList object is a child to a parent container. In this case, the parent would either be the global window object or the cells {...} object. As long as we have access to the parent node, all the child references will be preserved across multiple parent instances. It's basic computer science 101 stuff. 😉
19th May 2021, 10:06 PM
David Carroll
David Carroll - avatar
0
если вам нужно только количество, то зачем вам MutationObserver, достаточно document.querySelectorAll(".x-grid-cell-inner").lenght Мне нужно чтоб браузер при изменении количества элементов запускал функцию. Длинну я и так могу взять и querySelector тут не подходит потому что он возвращяет мертвую коллекцию, которая не обновляется автоматически. Мне нужно чтоб браузер сам реагировал на изменение коллекции и запускал функцию когда нужно без всяких таймеров.
17th May 2021, 11:38 PM
Влад Цислевский
Влад Цислевский - avatar
0
I not use mutation observer because this is not full working how i want. I create modification for EXT-JS web aplication. For easer my work.
18th May 2021, 5:16 AM
Влад Цислевский
Влад Цислевский - avatar
0
mutation observer is not work with HTML elements collection! I need call my function for edit elements in this collectin when this collection changed.
18th May 2021, 5:35 AM
Влад Цислевский
Влад Цислевский - avatar
0
Коллекция у меня и так обновляется. Живая коллекция это очень хорошая штука. Дело в том что сейчас у меня по таймеру setInterval(func,500) обрабатывается содержимое elements[x].innerText в цикле. А это замедляет работу. Элементов много. В самом веб-приложении есть вкладки и я работаю со всем содержимым. Элементов может быть от 650 до 10000. Мне нужно чтоб проверка запускалась только когда коллекция именно getElementsByClassName("x-grid-cell-inner") меняется. Учитывается стиль элементов и количество элементов в коллекции.
19th May 2021, 9:31 AM
Влад Цислевский
Влад Цислевский - avatar