Global events across all templates?

I want to make a global event that works site-wide. I tried the below but it failed.

/main.coffee:

UI.body.events
  'click img': (e) ->
    alert 'Does this work?'

What should I try instead?

You can try:

Template.body.events
  'click img' : (e) ->
    alert 'Does this work?'

Doesn’t work, unfortunately.

I just did a test app to see if this would work. So, with Meteor 1.0.4+, this worked:

test.html

<head>
  <title>testbody</title>
</head>

<body>
  <h1>Welcome to Meteor!</h1>

  {{> hello}}
  <h3>Test</h3>
  {{> test}}
</body>

<template name="hello">
  <button>Click Me</button>
  <p>You've pressed the button {{counter}} times.</p>
  <img src="https://gp3.googleusercontent.com/-KYMyBb8ozhw/AAAAAAAAAAI/AAAAAAAAAC0/9lYHiETrYD0/s48-c-k-no/photo.jpg" />
</template>
<template name="test">
  <img src="https://gp3.googleusercontent.com/-KYMyBb8ozhw/AAAAAAAAAAI/AAAAAAAAAC0/9lYHiETrYD0/s48-c-k-no/photo.jpg" />
</template>

test.js

if (Meteor.isClient) {
  Template.body.events({
    'click img' : function(e) {
      alert('test');
    }
  });
}

So, my guess is that your main template needs to be wrapped in a body in order for the “site-wide” events to work.

1 Like

That is a known issue (https://github.com/meteor/meteor/issues/2024).
You can use this package to fix it: https://atmospherejs.com/gwendall/body-events

2 Likes

I may be wrong, but in Meteor I don’t think you’re supposed to include the <body> tag.

Yes you can, if you create the example app there’s a body tag in it. Any body tags declared with be concatenated together. Order determined by load order of the files:

file 1.html

<body>
    <header>I'm a header</header>
</body>

file 2.html

<body>
    <footer>I'm a footer</footer>
</body>

Would result in:

<body>
    <header>I'm a header</header>
    <footer>I'm a footer</footer>
</body>

From docs.meteor.com:

HTML files in a Meteor application are treated quite a bit differently from a server-side framework. Meteor scans all the HTML files in your directory for three top-level elements: , , and . The head and body sections are separately concatenated into a single head and body, which are transmitted to the client on initial page load.

One way to get around this is to use a layout template. Since this template wraps everything else. If you use Iron router this is easy.

In your router.js file

Router.configure({
  layoutTemplate: 'MasterLayout'
});

MasterLayout.html file

<template name="MasterLayout">
  {{> yield}}
</template>

Then in the master-layout.js file

Template.MasterLayout.events({
  'click #logout': function () {
    console.log("logging out");
    AccountsTemplates.logout();
  }
});
2 Likes

This doesn’t provide the same convenience and functionality as Template.body if you are using multiple layouts with Iron Router.

Another option for global events is to define them at the window or document.body level, with Meteor.startup() on the client.

Where is this atm? Meteor documentation http://docs.meteor.com/api/templates.html#Template-body has listed template.body.events() as a native function, but it does not seem to work for me, even I have binded layout template elements inside the <body></body>. I have many layers of templates, but the root template / layout template has body tags

@mz103 Do you suggest that I global event functions should be binded inside the Meteor.startup() => $( document ).ready() in plain jQuery?

Ok, body.events started to work when I installed gwendall/meteor-body-events -package. However I wonder why Meteor documentation has this listed as a native function when it’s not working without gwendalls package

as documented Meteor Docs

Event maps on Template.body don’t apply to elements added to the body via Blaze.render, jQuery, or the DOM API, or to the body element itself.

I think, you maybe using something that renders the elements triggering the event!