Lazyload dataview/dvjs codeblock

I’ve built a dvjs view and managed to add a lazy loading feature to it.

As this can be useful for all compute-intensive dvjs views (and dql queries), I thought I’d extract its logic into a single file for easier use. Here it is:

const viewObserver = new IntersectionObserver((entries) => {
  entries.map((entry) => {
    if (entry.isIntersecting) {
      viewObserver.unobserve(entry.target);
      if (typeof input === 'string') {
        dv.execute(input)
      } else {
        input()
      }
    }
  });
});
viewObserver.observe(dv.container)

To use it you’ll need to save this in a .js file somewhere in your vault. You can name it the way you want (mine is saved at this path inside my vault _js/lazyload.js). Then to “convert” your codeblock and make it lazy loaded, you’ll need to do 2 things:

  1. Put all the code from your codeblock inside a function (don’t forget to make it async if needed)
  2. Call the view to lazyload your function

So for example, you should go from this:

```dataviewjs
const amountOfImages = 3;
const attachmentFolderPath = app.vault.getConfig("attachmentFolderPath");
const imageFiles = app.vault
	.getFiles()
	.filter(file => {
		return file.extension !== 'md'
			&& file.parent.path.startsWith(attachmentFolderPath)
	})

dv.list(dv.array(imageFiles)
	.sort(() => 0.5 - Math.random())
	.limit(amountOfImages)
	.map(file => dv.func.embed(dv.fileLink(file.path))))
```

P.S 1: The original code from this codeblock was taken from here: obsidian_dataview_example_vault/20 Dataview Queries/Display random images from a path.md at main · s-blu/obsidian_dataview_example_vault · GitHub (fantastic resource btw)

P.S. 2: Don’t try using it on iPad, it crashes the app (for unkown reasons)

to this:

```dataviewjs
function f() {
	const amountOfImages = 3;
	const attachmentFolderPath = app.vault.getConfig("attachmentFolderPath");
	const imageFiles = app.vault
		.getFiles()
		.filter(file => {
			return file.extension !== 'md'
				&& file.parent.path.startsWith(attachmentFolderPath)
		})
	
	dv.list(dv.array(imageFiles)
		.sort(() => 0.5 - Math.random())
		.limit(amountOfImages)
		.map(file => dv.func.embed(dv.fileLink(file.path))))
}
await dv.view("_js/lazyload", f)
```

From now on, the code within your function will not be triggered until the code block is visible in your window, and yes, it works with closed callouts!

Dataview DQL support

This solution works with dataviewjs out of the box. Now If you want to do the same with your dataview queries, you’d need to do the following:

  1. Verify that you you have Enable JavaScript Queries enabled in dataview’s settings
  2. Change dataview to dataviewjs at the top of your code block
  3. Instead of encapsulating your code in a function, you will want to convert your entire DQL code into a string and store it in a variable:

from this:

```dataview
LIST
FROM ""
WHERE file.size = 0
```

to this:

```dataviewjs
const dql = `LIST
FROM ""
WHERE file.size = 0`

await dv.view("_js/lazyload", dql)
```

N.B. Obsidian already does a lazy loading of the file content, so if you have a long file and your code block is at the very bottom, it wouldn’t have been loaded into the DOM in the first place.

7 Likes

In some cases, using this caused my queries to execute, return successfully, and then vanish mysteriously.

Specifying the view area seemed to fix this. In case anyone else experiences disappearing tables, here’s my tweaked js:

// lazy-load-dql.js
const rootNode     = dv.el("div", "");
const rootView     = rootNode.closest(".view-content");
const viewObserver = new IntersectionObserver(
    (entries, io) => {
        entries.map((entry) => {
            if (entry.isIntersecting) {
                dv.execute(input);
                io.disconnect();
            }
        });
    },
    {
        root:       rootView,
        rootMargin: "50px",
    },
);
viewObserver.observe(rootNode);