Roots Discourse

Can't access JS functions from a Blade Template

I don’t get it.
I have created an archive.js file that enqueues when visiting an archive. The js file is correctly loaded since it shows the message I have console.logged but I’m not able to access from my archive.blade.php the js function I have declared.

Some code:

  1. From the blade template
<span onClick="ajaxCall()">...</span>
  1. From archive.js
function ajaxCall() {
    console.log('clicked');
}
  1. The click output
Uncaught ReferenceError: ajaxCall is not defined
    at HTMLSpanElement.onclick ((index):94)

Also:

  • nothing changes by moving the function to a router-called js file
  • binding the click event to the function does work, but I need to pass some arguments from the template

I guess I might have to grant access to the function in some way…? I’m goin’ crazy

I think you have a scope problem. Have you examined the processed code that is output inside archive.js? You’ll find that it’s not the same as your original source file, in particular because ajaxCall() is no longer in the global scope after it has been processed by Webpack.

My JS skills still need some modernising so I can’t give the best advice but as far as I understand it, it’s rarely a good idea to have your functions and variables in the global scope (due to possible conflicts with other scripts using the global space). Webpack helps by bundling everything separately…

This probably isn’t a good practice but if you want to make your ajaxCall() function available globally, you can add a line like this to the end of your archive.js:

window.ajaxCall = ajaxCall;

I’m sure there is a much better way but I’m still figuring that part out…

2 Likes

Hey again @Tom-m - my instinct would be to put the code in a JS route file, bind it to the click event, and make the data you need from the template available in data attributes on the element.

I’ll just use common.js and an arbitrary element for illustration:

fictional.blade.php

...
<button id="jsTrigger" data-my-attribute="My value">Click Me</button>
...

common.js

export default {
  init() {
    // JavaScript to be fired on all pages

    // vanilla JS
    document.getElementById('jsTrigger').addEventListener('click', function() {
      console.log(this.dataset.myAttribute);
    });
    
    // jQuery
    $('#jsTrigger').click(function() {
      console.log($(this).data('myAttribute'));
    });
  },
  finalize() {
    // JavaScript to be fired on all pages, after page specific JS is fired
  },
};

But @Stephen is likely right that your current problem is a scope issue.

4 Likes

Yeah, somehow I did not think to put the data in a data attribute :sweat: I guess I’ll go that direction, I would not be confortable using globals.
Thanks guys, you’ll likely have to save my life a few more times these days, since I’m developing my first Sage9 website and I get stuck quite often :’)

1 Like

this solve my problem,