DataViewJS copy to clipboard on click (and some weird interactions)

Making DataviewJS cells copy to clickboard when clicked

I want to use my vault to help me fill out forms (both on desktop and mobile) so I set out to create this script that turns cells clickable and when clicked copies something (in my case the content of the cell) to the clipboard.

Plugins I use

I use DataviewJS for formatted output and Buttons for creating clickable cells. (In my vault I also use CustoJS to load this functionality in scripts)

The code:

const Buttons = app.plugins.plugins["buttons"]
const el = this.container

function createButton(name, content = name){
    return Buttons.createButton({
      app,
      el: el,
      args: {
        name: name,
        class: "button-cell"
      },
      clickOverride: {
        params: [],
        click: (x) => navigator.clipboard.writeText(content)
      }
    })
  }


dv.table(["Test"], [[createButton("Foo", "Bar")]])

And it looks like:

image

When you click on the cell “Foo” it copies bar to your clipboard.

The weird part

I wanted to use DataviewJS’s markdownTable function (for looks) and something I did not expect pop out:

Code:

const Buttons = app.plugins.plugins["buttons"]
const el = this.container

function createButton(name, content = name){
    return Buttons.createButton({
      app,
      el: el,
      args: {
        name: name,
        class: "button-cell"
      },
      clickOverride: {
        params: [],
        click: (x) => navigator.clipboard.writeText(content)
      }
    })
  }


dv.paragraph( dv.markdownTable(["Test"], [[createButton("Foo", "Bar")]]))

The unexpected result

image

The button is rendered once more on top of the block.

Problem

After some testing I figured out that the button is renderd 2 separate times:
1: When dv.markdownTable creates the “result” it will output, this time using the parent block as
the container
2: When the dv.paragraph renders the output.

After some code diving:

I took a look at the code of the plugins and the published obsidian api, and I’m fairly certain that it happens due to Obsidian’s HTMLElement (It’s an interface defined in Typescript) and when the createEl function is called (It’s defined in the Node Typescript interface) it both inserts the result to the given HTMLElem and returns it as plain text html code.
I took a look at the Plugins code but nothing worked (both plugins have settings options but nothing I found solved the above problem), so I suspect Obsidians built in HTMLElement can’t return only html code without rendering it too.

The solution - at least my fix

I made a new element that does not render and “created” the buttons in it.

Just replace line 2 with this:

const el = this.container.createEl("template")

This basically makes a <template> block inside the parent block and templates by default don’t render, but returns the HTMLElement that can be printed out. I was worried when reloading the script it might pollute the dom but it overwrites with every relod.

If I missed something (like a setting in Obsidian API or the Buttons plugin that would turn off rendering new elements) or you have a better way of doing this, let me know.

Almost forgot the CSS

.button-cell{
	width: 100%;
	border-radius: 0;
	padding: var(--size-2-2) var(--size-4-2);
}

td:has(.button-cell){
	padding: 0;
}

Thanks for reading,

Cheers

1 Like

Hey, I’m wondering if this is possible with the successor of the buttons plugin: meta-bind?