JS widgets or macros to set content in HTML

Hi Folks,

If you are website or JS designer of you may be familular with injecting content into a html layout;

<script>
    // Example: injecting content later with JS
    document.getElementById('main-content').innerHTML = '<h1>Hello World!</h1><p>This is dynamic content.</p>';
</script>

As a matter of curiosity could we build a utility JS macro or widget we could call that would set the inner html ?

  • I expect we may need to disabiguate by only acting on a specific tiddler, or currentTiddler by default.
  • It would be nice to be able to inject Wiki Text and TiddlyWiki script the same way.
  • I suppose it may be nice to inject into standard tiddlywiki elements as well, such as the wiki sub title etc…

Why

I have come to ask this question while looking at facilitating the use of found or LLM generated HTML by not needing to change a HTML Layout, simply set the values within defined places within a html (Template).

  • Perhaps we can consider other dynamic “functions” in JavaScript, commonly used to build webpages and adapt it for use in TiddlyWiki.

Any thoughts?

Directly injecting content into innerHTML is as security risk an needs to be avoided.

There is a reason why the TW parser sanitises the SCRIPT element.

I would be happy if it sanitised code, I only want to be able to inject content, like wiki text, tiddlywiki script to be renderd by tiddlywiki.

  • I could use variable like <div class=centre-header><$transclude $variable=centre-header/></div> but then I have to alter every element on the layout.
  • So If I can set the content of .centre-header directly I dont need to modify the form.

eg;

<<set-ellement centre-header "title for the center of the header">>

Or the $macrocall/$transclusion method.

Here is an example of a HTML layout and css generated by an LLM in a matter of seconds.

  <div class="page">
    <div class="header-left"></div>
    <div class="header-center"></div>
    <div class="header-right"></div>

    <div class="margin-left-top"></div>
    <div class="margin-left-center"></div>
    <div class="margin-left-bottom"></div>

    <div class="margin-right-top"></div>
    <div class="margin-right-center"></div>
    <div class="margin-right-bottom"></div>

    <div class="content">
      <div class="content-header"></div>
      <div class="content-body"></div>
      <div class="content-footer"></div>
    </div>

    <div class="footer-left"></div>
    <div class="footer-center"></div>
    <div class="footer-right"></div>
  </div>

With the following CSS to align etc…


    * { box-sizing: border-box; }

    body {
      margin: 0;
      font-family: sans-serif;
    }

    .page {
      display: grid;
      grid-template-areas:
        "header-left header-center header-right"
        "margin-left-top content margin-right-top"
        "margin-left-center content margin-right-center"
        "margin-left-bottom content margin-right-bottom"
        "footer-left footer-center footer-right";
      grid-template-columns: 1fr 3fr 1fr;
      grid-template-rows: auto auto 1fr auto auto;
      min-height: 100vh;
      gap: 0.5em;
    }

    .header-left    { grid-area: header-left; background: #eee; padding: 1em; }
    .header-center  { grid-area: header-center; background: #eee; padding: 1em; }
    .header-right   { grid-area: header-right; background: #eee; padding: 1em; }

    .margin-left-top     { grid-area: margin-left-top; background: #f0f0f0; padding: 1em; }
    .margin-left-center  { grid-area: margin-left-center; background: #f8f8f8; padding: 1em; }
    .margin-left-bottom  { grid-area: margin-left-bottom; background: #f0f0f0; padding: 1em; }

    .margin-right-top     { grid-area: margin-right-top; background: #f0f0f0; padding: 1em; }
    .margin-right-center  { grid-area: margin-right-center; background: #f8f8f8; padding: 1em; }
    .margin-right-bottom  { grid-area: margin-right-bottom; background: #f0f0f0; padding: 1em; }

    .content {
      grid-area: content;
      background: #fff;
      padding: 1em;
      display: flex;
      flex-direction: column;
      gap: 1em;
    }

    .content-header,
    .content-body,
    .content-footer {
      padding: 1em;
      background: #fdfdfd;
      border: 1px solid #ccc;
      border-radius: 4px;
    }

    .footer-left    { grid-area: footer-left; background: #ddd; padding: 1em; }
    .footer-center  { grid-area: footer-center; background: #ddd; padding: 1em; }
    .footer-right   { grid-area: footer-right; background: #ddd; padding: 1em; }

    /* 🔻 Responsive Layout for Smaller Screens */
    @media (max-width: 768px) {
      .page {
        grid-template-areas:
          "header-center"
          "header-left"
          "header-right"
          "margin-left-top"
          "margin-right-top"
          "content"
          "margin-left-center"
          "margin-right-center"
          "margin-left-bottom"
          "margin-right-bottom"
          "footer-center"
          "footer-left"
          "footer-right";
        grid-template-columns: 1fr;
        grid-template-rows: auto;
      }

      .content {
        padding: 0.5em;
      }

      .content-header,
      .content-body,
      .content-footer {
        padding: 0.75em;
      }
    }

[Edited] This example should also be responsive.

We change the SCRIP element into SAFE-SCRIPT. that’s it. Sanitising the innerHTML would be the same as parsing and rendering it’s content. So there is no need to inject it in a dangerous way

Do you mean it just would not work, ie will be sanitised, if I did inject JS?

The Top of thread example uses;

 document.getElementById('main-content').innerHTML = '<h1>Hello World!</h1><p>This is dynamic content.</p>';
  • ie addresses via elementById

But we can use

  Array.from(document.getElementsByClassName("content-body")).forEach(el => {
    el.innerHTML = "<p>This content appears in all matching divs.</p>";
  });
  • ie addresses via ByClassName

So a macro to set the inner html may be best if it allows us to specify how to address the element eg ID or class or other?

[Edited]
I have used ChatGPT to write a JS Macro for me and it is working if I use HTML, but it is fragile.
<<injectcontent "header-center" " <h1>heading</h1> ">>

  • I realize it only does this after the tiddlywiki render so it only accepts html not wiki text.

$__plugins_my_macros_injectcontent.js.json (928 Bytes)

you could create a widget similar the $raw widget, adding a ‘html’ parameter. That chatgpt code is incorrect.

Do say which part, it cant all be wrong as I got part of it working.

macros are not used in that way, the javascript will run when the marco is parsed, so, for instance, this will not work

<<injectcontent xxx "<hr>">>
<div class=xxx/>

but this will

<div class=xxx/>
<<injectcontent xxx "<hr>">>

so it is better to used a widget.

Hmm, I am not totally sure what you mean. But yes the template (HTML) needs to exist before I try and populate it.

An example may be after the template a widget would be used to populate the template with the contents of fields and/or the output of various TiddlyWiki script like $list widgets.

What I mean is that macros are designed to return wikitext that will then be parsed. Macros are supposed to work like transclusions. Wikitext is parsed into the widget tree, with the content of the macro appearing inside its parent widget/tag. The macro you have breaks this idea… so if the tiddler containing it is transcluded 2 or more times

<div class=xxx/>
<<injectcontent xxx "<hr>">>

only the first <div class=xxx/> will get populated.
I

OK, I need to set up a few more examples to see if I am making any false assumptions. The way I look at it is the HTML template is included, not unlike pragma in a tiddler, then like TiddlyWiki script in the body, we address these existing locations and alter the values there in. All in the same render, the second part references the first part.