How to include javascript functions to dynamically rendered content

I am trying to include an onclick function to many dynamically rendered items. I would usually just add this to the element with javascript onload but I need them to be rendered with their specific arguments in mind.

I am using esbuild instead of importmaps because I set up with bootstrap

Each item has it’s own properties to pass into the function as arguments like so:

      <div class="pending-transactions">
        <% @pending_transactions.each do |transaction| %>
          <%= render 'transaction', transaction: transaction %>
          <button onclick="withholdSingleTransaction(<%= transaction['value'] %>, <%= transaction['to'] %>)">Withhold Transaction</button>
        <% end %>

This comes out, it works to render what I want to the page, but on clicking the function it says withholdSingleTransaction is not defined.

My app/javascript/application.js

import "@hotwired/turbo-rails"
import "./controllers"
import "./custom/prompt_transaction"

My ./custom/prompt_transaction.js file:

document.addEventListener('turbo:load', async function() {
    console.log('turbo:load event fired');
    const provider = new ethers.providers.Web3Provider(window.ethereum)
    let accounts = await provider.send("eth_requestAccounts", []);
    const signer = provider.getSigner()

    const withholdSingleTransaction = async (transaction) => {
        const value = transaction.value;
        const to =;

        const _transaction = {
            to: to,
            value: value

        const tx = await signer.sendTransaction(_transaction);

        await tx.wait();

        console.log(`Transaction ${tx.hash} complete`);

The code in the javascript file runs, as I see the console.log message so I know the script has ran, but why can’t I get my function I defined to be recognized?

Just sitting and thinking about the issue for a moment, I realized I was able to actually fuse both approaches here.

Instead of passing the arguments to an onclick function, I set them as data variables instead.

            data-to="<%= transaction['to'] %>"
            data-value="<%= transaction['value'] %>"
          >Withhold Transaction</button>

Then in my javascript I attached the function by:

document.querySelectorAll('.pending-transactions button').forEach((button) => {
  button.addEventListener('click', async (event) => {
   const to =;
   const value = ethers.BigNumber.from(;

This works great for anyone else looking to achieve something similar