Obsidian Publish API: createDiv doesn't create div in time

I’m trying to add comments on my OP site.

Since I didn’t find any other way, I use “comments” text on a separate line and then track it in my MarkdownPostProcessor:

publish.registerMarkdownPostProcessor((el, ctx) => {
  if (el.innerHTML === '<p>comments</p>') {
    // So I found the comments block
    // clear <p>comments</p>
    el.innerHTML = ''
    // create container for comments
    const element = el.createDiv({ attr: { id: 'comments' } })
    // Check to see if it's available
    console.log('element:', document.getElementById('comments'))
   /// But it's not: `undefined`
  }
})

So as you see, createDiv doesn’t create the container right away. Which makes it unpredictable. What do I do? setTimeout? How long?

Is there a robust way to get it created properly?

I also tried using the callback:

el.createDiv({ attr: { id: 'comments' } }, (element) => {
  console.log('element:', document.getElementById('comments'))
})

but the outcome is the same.

So what is the current way?

Other questions:

  • Why do you expose createDiv in the first place? Can’t we just we normal DOM-methods to work with it?
  • There is ctx.createChild() method. Should I use it instead? If yes, then how?

I just test with plugin api registerMarkdownCodeBlockProcessor, hope there won’t be too many differences.

For checking the availability of the newly created element, I directly logged it, as well as setting its innerText, both gave the expected result, while the method getElementById didn’t

publish.registerMarkdownPostProcessor((el, ctx) => {
  if (el.innerHTML === '<p>comments</p>') {
    el.innerHTML = ''
    const element = el.createDiv({ attr: { id: 'comments' } })
    element.innerText = "Hello world!" // the text appears
    console.log(element) // gives the correct element
    console.log('element:', document.getElementById('comments')) // return `null`
  }
})

So I wonder if it’s a sync/async issue, and you may try this

publish.registerMarkdownPostProcessor(async (el, ctx) => {
  if (el.innerHTML === '<p>comments</p>') {
    el.innerHTML = ''
    await el.createDiv({ attr: { id: 'comments' } })
    console.log('element:', document.getElementById('comments'))
  }
})

Edit: Regarding the direct / createChild() way, I think either is possible, and ctx.addChild() provides you with a better lifecycle management.

  1. You can add a comment by %% ... %% syntax. See the help page
  2. createDiv is not async.

It’s just a utility function that makes DOM handling easier. You don’t need to use it if you don’t like it. The implementation can be found in the dev tools > Source > enhance.js.

So I have a question: what is the publish object? Where in the docs can I find information about it?

publish is a global object, that is used to integrate with Obsidian Publish (it’s when you publish your notes on their server).

Here is (a part of) Publish API:

Under comments, I meant real user comments that go under an article. But using %%%% seems to be a better idea, thanks!

No, %%%% doesn’t make sense as it’s not being rendered and context doesn’t contain anything useful.

I see, thank you for letting me know about publish! I will explore it.

I’m sorry I misunderstood. I thought you wanted to add “comments” in a programming sense.