0

I'm trying to build a micro JS library to emulate InDesign text threading on the web using this constructor pattern boilerplate. The idea is the user feeds the function DOM selectors in a desired order, e.g.

var thread=new TextThread(['#container1','#container2']);
thread.build();

And from there the library splits the text contents of the first container into spans for each word. When a word is out of view, the library will move it to the following container in the order.

Here's where IntersectionObserver comes in: I need to assign an observer to each container fed to the constructor, and then have it observe the spans inside it. When a span goes out of view, it fires the observer callback, which should loop through all the observers with the .takeRecords() method to check which spans are out of view in each container. I've gotten the observers to fire their callbacks with no problem, but the issue is referencing all the observers within the callback.

What I've tried is storing an array variable in the self-executing function from the boilerplate, and then when the constructor is built, it pushes the observers to that array.

var observers=[];

var Constructor = function (selectors) {
    /*placeholder for code that selects and assigns the containers*/

    containers.forEach((item, i) =>{
        var options={
            root: item,
            rootMargin: '0px',
            threshold: 1.0
        }
        var newObserver=new IntersectionObserver(callback,options);
        observers.push(newObserver);
    })
};

Then when the text is split into spans by word

words.forEach((word,w) =>{
observers[ current container index ].observe(word); });

In the callback, entries for the observer that fired the callback are visible. However, if I try to reference the other observers using my observers array variable, takeRecords() returns an empty array. Here's the callback I'm testing:

function callback(entries){
    //the entries for the observer firing the callback are returning correctly
    console.log(entries)
    //the forEach below doesn't work though. It returns an empty array for each observer.
observers.forEach((item, i) => {
    console.log(item.takeRecords())
});
}
Nico
  • 129
  • 11
  • Please post the code of the `callback`. Also, I don't see why you would need to reference the other observers? – Bergi Jul 20 '20 at 16:10
  • @Bergi I've now added the callback code. I want to reference the other observers because I'd like to be able to see which spans have gone out of view in *each* of the containers, not just the one that fired the callback, since the actions I take in the callback have the potential to shift more elements out of view. True, that would trigger another callback, but I'm seeing if I can handle the reflow all at once so I have complete control over the timing. – Nico Jul 20 '20 at 16:20
  • I think you're referencing the other observers just fine. It returns an empty array because there are no more intersection records yet. You should first take into account the entries [passed to the callback](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/IntersectionObserver#Syntax), not call the [`takeRecords` method](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/takeRecords) – Bergi Jul 20 '20 at 16:24
  • @Bergi The thing that makes me doubt that it's returning correctly is that takeRecords returns empty *even for the observer that fired the callback*, while logging the entries (see edited callback) correctly returns the array of elements with changed status – Nico Jul 20 '20 at 16:29
  • It's working fine, [as documented](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/takeRecords): "*`takeRecords()` returns an array of `IntersectionObserverEntry` objects, one for each targeted element which has experienced an **intersection change since the last time the intersections were checked**, either explicitly through a call to this method or **implicitly by an automatic call** to the observer's callback. Note: If you use the callback to monitor these changes, you don't need to call this method.*" – Bergi Jul 20 '20 at 16:32

2 Answers2

1

In the callback, entries for the observer that fired the callback are visible. However, if I try to reference the other observers using my observers array variable, takeRecords() returns an empty array - even for the observer that fired the callback!

It's working fine, as documented:

takeRecords() returns an array of IntersectionObserverEntry objects, one for each targeted element which has experienced an intersection change since the last time the intersections were checked, either explicitly through a call to this method or implicitly by an automatic call to the observer's callback.

Note: If you use the callback to monitor these changes, you don't need to call this method.

(Emphasis mine)

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
0

you declared the observers as a private variable for the class. You either need to make it public, or static. I am not familiar with your design but I would suggest to make it static. From an architectural point of view making those static makes more sense for me

tstoev
  • 1,415
  • 11
  • 12
  • No, they should be instance-specific, not static. – Bergi Jul 20 '20 at 16:10
  • if you are running multiple threads on the page, sure. – tstoev Jul 20 '20 at 16:16
  • Yes, of course, otherwise `TextThread` wouldn't be a class that can be instantiated multiple times. – Bergi Jul 20 '20 at 16:18
  • I wouldn't say that having a class requires you to do(or is required if you do) multiple instances of it. I often use a single instance to encapsulate code. A UIController for example that handles the operations on the DOM can be a singleton class that ensures that each manipulation of the DOM is being accounted for and or is not spread across the entire code-base. But then again it really depends on what you need to do. – tstoev Jul 20 '20 at 16:35
  • "*I often use a single instance to encapsulate code.*" - then you probably shouldn't use a `class` in javascript. A plain object would suffice for a singleton. – Bergi Jul 20 '20 at 16:36
  • sure, but since I moved from C++/C# I feel more comfortable using classes. Kind of feels right :) – tstoev Jul 20 '20 at 16:39
  • I can only argue about JavaScript/TypeScript, not about C# code written in a weird syntax :-) – Bergi Jul 20 '20 at 16:40
  • After 15+ years on the rakes, I tend to make the code do what I need it to do, and not bother too much with the syntax the next language introduces. I open the hood when I need to squeeze the miliseconds out of the box, but then it is mostly about optimizations here and there, the bulk of the code is either written poorly(regardless of language) or makes a weird call here and there to clog the pipes. There are indeed fancy things with new languages, but man...I am way beyond the shiny stuff – tstoev Jul 20 '20 at 16:47