1

I have rendered multiple calendars on a single page and I need to use the next / prev buttons (as well as date select) in order to see different date ranges. I have connected external next / prev buttons, however, they only effect the last calendar on the page.

My JS to make the calendar elements:

document.addEventListener('DOMContentLoaded', function() {
    for (let index = 1; index <= array_length; index++) {
        calendar_str = 'calendar_' + String(index);
        eval('var ' + calendar_str + ' = document.getElementById(\'' + calendar_str + '\');');
        var calendar = new FullCalendar.Calendar(eval('calendar_' + String(index)), {

        ... code here to setup calendar ...

          });
          calendar.render();
          calendar.setOption('contentHeight', 200);

          document.getElementById('prev-btn-ext').addEventListener('click', function() {
            calendar.prev(); // call method
          });

          document.getElementById('next-btn-ext').addEventListener('click', function() {
            calendar.next(); // call method
          });

    }
});

My problem is that when the page is rendered, I can see all of the calendars and I can drag and drop events between them, however, when I click the next and prev buttons only the very last calendar changes. I need all of the calendars to change synchronously based off of the single next / prev button.

As you can see, my calendar instances are named 'calendar_1', 'calendar_2', etc. but the EventListener for the next and previous buttons only looks at the "calendar" variable. I am wondering if my problem has to do with that, maybe I need to specify an argument for calendar.next() or calendar.prev() for each calendar instance somehow?

ADyson
  • 57,178
  • 14
  • 51
  • 63

1 Answers1

0

This is a scope issue.

var calendar = redefines the calendar variable each time your loop runs. And since you've defined it with var, its scope is global to the whole function body, and extends inside all the callbacks assigned to the click events within the function. This means even the callbacks defined in previous iterations of the loop will use the most recent definition of calendar when they execute.

Whereas if you write let calendar = instead, this will limit the scope of the variable to the immediate enclosing block (the loop in this case), and mean the callbacks use the version defined within that iteration of the loop when they run, and your code will work as intended. This is one of the reasons let was added to JavaScript.

Demo: https://codepen.io/ADyson82/pen/OJMbjQz

This is a useful reference article: What's the difference between using "let" and "var"?


But overall, instead of using eval (which can be a security issue, as well as creating hard-to-read code) to generate unique variable names, it would be a lot cleaner just to store all your calendar references in an array. So here's an alternative approach:

document.addEventListener("DOMContentLoaded", function () {
  let calendars = []; //calendars array
  //auto-detect the number of calendars on the page
  var calendarElements = document.querySelectorAll(".cal");

  calendarElements.forEach(function (cal) {
    let calendar = new FullCalendar.Calendar(cal, { plugins: ["dayGrid"] });
    calendar.render();
    calendar.setOption("contentHeight", 200);
    calendars.push(calendar); //add to array of calendars
  });

  document
    .getElementById("prev-btn-ext")
    .addEventListener("click", function () {
      calendars.forEach(function (cal) {
        cal.prev();
      });
    });

  document
    .getElementById("next-btn-ext")
    .addEventListener("click", function () {
      calendars.forEach(function (cal) {
        cal.next();
      });
    });
});

The HTML would just need a small change to add the "cal" class to the calendar elements, e.g.:

<button type="button" id="prev-btn-ext">Prev</button>
<button type="button" id="next-btn-ext">Next</button>
<div id='calendar_1' class="cal"></div>
<div id='calendar_2' class="cal"></div>
<div id='calendar_3' class="cal"></div>

Demo: https://codepen.io/ADyson82/pen/zYrodyG

ADyson
  • 57,178
  • 14
  • 51
  • 63
  • 1
    Thanks! This was exactly what I was looking for. I am fairly new to JavaScript (if you couldn't tell by my code lol) so this helps a ton, especially the let vs. var information. This totally fixed my problem and taught me some new JS syntax too! – stackoverflowing321 Jun 17 '20 at 15:55