Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

fylzero's avatar
Level 67

addEventListener not working? Using exact example from Cashier.

I am using the direct example for setting up Laravel Cashier using Laravel v6.8.0 and Cashier v10.5.1

For some reason addEventListener on the Update Payment Method button refuses to fire. I tried using the ES6 function example, I changed it to ES5 just to see if that was the problem... still nothing.

Any help is much appreciated. I'm pretty stuck on this one.

Blade File:

<input id="card-holder-name" type="text" placeholder="Cardholder Name">

<!-- Stripe Elements Placeholder -->
<div id="card-element"></div>

<button id="card-button" data-secret="{{ $intent->client_secret }}">
    Update Payment Method
</button>

JS File:

const stripe = Stripe(process.env.MIX_STRIPE_KEY);

const elements = stripe.elements();
const cardElement = elements.create('card');

cardElement.mount('#card-element');

const cardHolderName = document.getElementById('card-holder-name');
const cardButton = document.getElementById('card-button');
const clientSecret = cardButton.dataset.secret;

// THIS IS SHOWING THE BUTTON, SO IT IS NOT A LOAD READY ISSUE.
console.log('Card Button: ', cardButton);

// WHEN I CLICK THE BUTTON, NOTHING HAPPENS IN CONSOLE?!
cardButton.addEventListener('click', function() {
    console.log('Meep');
});
0 likes
10 replies
fylzero's avatar
Level 67

@andreich1980 No errors, stripe library definitely loaded. It shows the payment fields.

I literally cleared everything off the page except...

 <button id="merp">Merp</button>
const merpButton = document.getElementById('merp');

merpButton.addEventListener('click', function() {
    alert('Hey')
});

This does not work.

BUT it works in a codepen...

https://codepen.io/fylzero/pen/yLygWow

What the heck?

1 like
fylzero's avatar
fylzero
OP
Best Answer
Level 67

@andreich1980 I figured it out. By default now Laravel has a defer tag in the script for app layout. I used that instead of a custom template so my JS wasn't loading in the right order.

1 like
mikylucky's avatar

@fylzero I'm facing the same issue, but I cannot get your solution.

Can you please provide a more detailed explanation?

Where did you put the Javascript code?

fylzero's avatar
Level 67

@mikylucky You basically just need to make sure your app.js file loads before this code is executed so the Stripe/Cashier js loads first. By default Laravel adds a defer to the Javascript tag in the layout view.

<script src="{{ asset('js/app.js') }}" defer></script>

Basically what defer is doing is the same as placing the script tag before the closing body tag, which is a common practice but also requires using a DOM ready check like this...

$(function () {
    // Javascript code to run after DOM is loaded here
});

I think basically all I did was rip out the defer tag and it worked.

<script src="{{ asset('js/app.js') }}"></script>

This probably happened because I was using a script tag and yielding it into the blade template. I can't remember exactly. I was doing that to avoid having this code load everywhere.

It probably wouldn't hurt anything to leave defer as is and just add this to your compiled Javascript either. I'm assuming it just wouldn't add the listener if it couldn't find the #card-element on the page. Both approaches feel muddy. I think I opted for the first approach as js is cached once it is loaded anyways, so I didn't mind the tiny slow down there. I also felt it was better not to load the Stripe code on all pages.

1 like
mikylucky's avatar

@fylzero thanks for the explanation and the quick reply!

Reading your post, I came out with a maybe smoother solution, that applies only if your frontend uses Vue.

Instead of deferring the app.js, I created a Vue component and included Stripe script in the mounted() hook. Doing this, the code will be surely executed after the relevante DOM part is loaded.

Thank for the support!

1 like
fylzero's avatar
Level 67

@mikylucky Doing this in Vue makes this a non-issue for sure. That said, if you are using Vue the Javascript you'll be loading will be that much heavier on first load. So using defer on the tag can give you a bit of a first paint speed boost. Using defer also keeps you from having to add ready checks for other js to run. It is the exact same thing as prepending the closing body tag with your script tag. Same effect. Glad you got it situated.

1 like
cportal31's avatar

Hi, another solution, put the code in $( document ).ready(function()

<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script>
    $( document ).ready(function() {
    const form = document.getElementById( 'registration-form' );
    const stripe = Stripe('{{ config('services.stripe.key') }}');
    const elements = stripe.elements();'''
fylzero's avatar
Level 67

@cportal31 That is a pretty outdated practice tbh. You should avoid doing that and just place your script tags below your DOM. I'm also not a fan of the defer tag because it causes a page flicker issue in Bootstrap.

1 like

Please or to participate in this conversation.