# How do we fire scripts after topic HTML is rendered in DOM?

**URL:** https://meta.discourse.org/t/how-do-we-fire-scripts-after-topic-html-is-rendered-in-dom/114701
**Category:** Development
**Created:** [April 10, 2019, 9:54pm UTC](https://meta.discourse.org/t/how-do-we-fire-scripts-after-topic-html-is-rendered-in-dom/114701 "2019-04-10T21:54:48Z")
**Posts on this page:** 1
**Showing post:** 3

<div class="post-metadata">

### Author: ![Johani](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/johani/32/176920_2.png) [@Johani](https://meta.discourse.org/u/Johani)
#### Post date: [April 12, 2019, 1:12pm UTC](https://meta.discourse.org/t/how-do-we-fire-scripts-after-topic-html-is-rendered-in-dom/114701/3 "2019-04-12T13:12:11Z")

</div>

Like @tshenry pointed out, what you’re looking for is `decorateCooked`

For basic changes, something like this works:

```plaintext
api.decorateCooked($elem => $elem.css("background", "yellow"));

```

If you need something more complicated, you have two choices, you can either use this syntax:

```plaintext
api.decorateCooked($elem => {
  // 1
  console.log("foo");
  // 2
  $elem.css("background", "yellow");
  // 3
  console.log("bar");
});

```

or create your own jQuery plugins like so:

```plaintext
$.fn.doSomething = function() {
  // 1
  console.log("foo");
  // 2
  $(this).css("background", "yellow");
  // 3
  console.log("bar");
  // you have to add this at the end for chainability
  return this;
};

api.decorateCooked($elem => $elem.doSomething());

```

Do note that `decorateCooked` runs every time

1- a post is loaded (yes, that means once for every post in the stream).  
2- the composer preview is updated - this can be very expensive if you don’t watch out.

What that means is that you have to be mindful and only fire scripts on the posts you want to target.

So, if you don’t want the changes to happen in the composer you would add the `onlyStream` option. You would do that with something like this:

```plaintext
$.fn.doSomething = function() {

  // do your work
  
  return this;
};

api.decorateCooked($elem => $elem.doSomething(), { onlyStream: true });

```

This also ensures that your script ignores other places where the post might appear like the user post-stream.

And if you only want to target posts that contain specific elements you can use something like this

```plaintext
$.fn.doSomething = function() {
  const targetElement = $(this).children("[data-theme-test]").length;
  if (!targetElement) return;

  // do your work
  
  return this;
};

api.decorateCooked($elem => $elem.doSomething(), { onlyStream: true });

```

and that would ensure that your script only fires on the posts that contain the element you want to target and also ignore the composer preview.

If you have external scripts you want to load in order to do some work, you can use `loadScript` and you can do that with something like this:

```plaintext
const loadScript = require("discourse/lib/load-script").default;

$.fn.doSomething = function() {
  const targetElement = $(this).children("[data-theme-test]").length;
  if (!targetElement) return;

  loadScript(
    "//cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.1.1/iframeResizer.min.js"
  ).then(() => {
    // do your work
  });

  return this;
};

api.decorateCooked($elem => $elem.doSomething(), { onlyStream: true });

```

Do note that if that’s the case, you will also need to watch out for [CSP issues](https://meta.discourse.org/t/mitigate-xss-attacks-with-content-security-policy/104243)

Finally, if you need to transverse the dom, you might need a little bit of Ember. By default, everything in `cooked` div should be available for jQuery. However, say you want to target one of the parents of cooked, well you can use something like this:

```plaintext
const loadScript = require("discourse/lib/load-script").default;

$.fn.doSomething = function() {
  const targetElement = $(this).children("[data-theme-test]").length;
  if (!targetElement) return;

  Ember.run.scheduleOnce("afterRender", () => {
    loadScript(
      "//cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.1.1/iframeResizer.min.js"
    ).then(() => {
      // do your work
    });
  });

  return this;
};

api.decorateCooked($elem => $elem.doSomething(), { onlyStream: true });

```

and that will allow you to fire a script after everything renders and after you load an external script while ignoring posts you don’t want to target and ignoring the composer preview.

Let me know if you need any clarification.

---

_[View the full topic](https://meta.discourse.org/t/how-do-we-fire-scripts-after-topic-html-is-rendered-in-dom/114701)._
