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.

1 Like

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.

1 Like

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.

like

this

(mentioned previously @: Bbcode parser "plugin"/widget (or..whatever) - #7 by wiki_user )

afaik
this is the simplest example of just that sort of thing iv found

…it might be interesting to test
if/how the .replaceWith(
input-data could be maped from some source ??..
other than an existing html page element…

1 Like

I have just managed to build an action widget to set or toggle html attributes and you can do it with any html tag or attribute. The primary use case is the open attribute on the details widget.

I will see if I can use a similar mechanisum to satisfy my original request

Have you tried to write a widget? Widget can manage its dom. If you let a widget wrapped some wikitext, then JS code in that widget can freely inject or modify HTML content inside of it.

Yes I have recently come to realise that. I am working on a widget (OK I admit with ChatGPT) but its a long shot.

On the other hand I have something working well here How to toggle/add/remove select box attribute(s) existance with checkbox - #11 by TW_Tones which is quite similar.

  • It looks like solving a long standing problem with using the HTML Details widget, the open attribute. But may have numerouse other uses.

The solution I was looking for at the top of this topic was to inject rendered content into a internet sourced layout. I now belive it is technicaly possible. But it would be another way to craft structured content in a tiddler whilst keeping the structure in html. Allowing us to source layouts from templates and LLM’s rarther than needing to do the fiddly html.

An example my be one of the more advanced layouts like flex box and others and using tiddlywiki script to populate it. Keeping it usable by users accross a range of skill levels.

  • I am currently trying to get a widget to analyse the html layout and extract the css selectors we can use to then inject the TiddlyWiki rendered content into. The idea being to make this quick and easy to use.

As I wrote, writing to .innerHTML is forbidden. It’s a SECURITY RISK. It’s used by examples because it’s easy to demonstrate something. But that’s crap. It should not be there!

You could use something as follows in a TW environment. Open your dev console in TW and type this. After you created a tiddler named: asdf with some text eg:

title: asdf

! header

some text

<script>alert('Hacked!')</script>

Code for the console.

var text = $tw.wiki.getTiddlerText("asdf");
var rendered = $tw.wiki.renderText("text/html","text/vnd-tiddlywiki",text);

console.log(rendered);

Then have a closer look, how to create a proper TW widget at: Javascript Widget Tutorial

I will let you look at this before I publish and you can tell me if it’s breaking the rules but this is using a widget. I take note of your concern but ask you keep in mind it may not be the risk you imagine.

I will look at the reference you gave me and check what I am doing against it.