Disable or remove click event to avoid modal firing on empty day

Hi all

I’ve been working on a calendar CLNDR.js using moment.js and a bit of Underscore.js building an array of JSON objects to populate the calendar with events, working good so far.

One issue I’m having right now is that everyday/square on the calendar will fire my modal popup regardless if I have events or not.

Wondering where and how I can fix the code so the modal will show only on the days with the events attached?

The modal data-toggle is attached to every day, but how do I tell the click event to ignore the empty days?

Fiddle example full working calendar (click a day with an event first)

Code:

var myCal = $('#calendar').clndr({
	constraints: {
    startDate: '2016-08-06',
    endDate: '2016-12-16'
  },
  template: $('#calendar-template').html(),
  weekOffset: 1,
  events: events,
  startWithMonth: moment(),
  clickEvents: {
  	click: function (target) {
      if(target.events.length) {
        var body = '';
        
        body += '<div class="modal-dialog"><div class="modal-content">',
          body += '<div class="modal-header">',
          body += '<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">x</span></button>',
          body += '<h4 class="modal-title">Events on '+ target.date.format('MMMM Do YYYY')+'</h4></div>',
          body += '<div class="modal-body">'
        
        // loop through the events, making a <div> for each with info
        for(var i = 0; i < target.events.length; i++) {
          body += target.events[i].startTime+' - '+target.events[i].endTime+'<br>',
            body += '<div class="event">' + target.events[i].title + '</div>',
            body += target.events[i].location + '<br>';
        }
        
        body += '</div><div></div>';
        
        //console.log(target.events);
        $("#events-list-modal").html(body);
      }
    },
  }
});

I’m also wondering maybe I should display the var body on the page using underscore like the other code, looking a bit messy, what do you think?

Thanks,
Barry

I think I have found an easy option if anybody can advise thanks.

How can I remove data-toggle=“modal” from all div class=“day” that don’t have an event class?

Example

The first two instances below would have data-toggle=“modal” removed because no event class is shown

<div class="days">

<div class="day past" data-toggle="modal" data-target="#events-list-modal"></div>
<div class="day past" data-toggle="modal" data-target="#events-list-modal"></div>

<div class="day past event" data-toggle="modal" data-target="#events-list-modal"></div>
<div class="day past event" data-toggle="modal" data-target="#events-list-modal"></div>

</div>

Thanks.

Hey @computerbarry,

Rather than rely on the custom attributes to open the modal, why not trigger it via JS within your click handler?

  //console.log(target.events);
  $("#events-list-modal").html(body);
  $("#events-list-modal").modal('show');

:grinning: Don’t believe it!

So simple but so effective, make it look so easy fretburner, been at this all day ha.

Thank a lot!

Ok, so just to confirm.

Remove the modal from

<div class="<%= day.classes %>" data-toggle="modal" data-target="#events-list-modal">

And let the click handler do the work?
It seems to work like this.

Update fiddle

And a bonus question
How could I hide/disable all the past events older than today?

I think there might be some functionality within CLNDR for this, will need to check, though if you have any suggestions, cool.

Thanks again,
Barry

Yeah, that’s right.

I would just filter the events to remove any older than the current date, before initializing the calendar.

Yeah, as you’ve already got underscore loaded anyway, you might as well use it here too and pull out all the markup into a template. Then your click handler will end up looking something like this:

if(target.events.length) {
  var body = eventTpl({
    date:  target.date.format('MMMM Do YYYY'),
    events: target.events
  });

  $("#events-list-modal").html(body);
  $("#events-list-modal").modal('show');
}
1 Like

Thanks for the conformation fretburner.

I tried the below snippet just to test…

<ul>
       <% _.each(events, function(event) { %>  
       		<p><%= event.title %></p> 
       <% }); %>
</ul>

… but got the error

Uncaught ReferenceError: eventTpl is not defined

What is the correct way to turn the previous code we used shown below into underscore code?

target.events[i].title

Updated fiddle

Thanks, Barry

Hey Barry,

To turn the code into an underscore template, you need to do a few things:

  • Put the template code into a script tag with a unique ID and the type text/template:
<script id="event-template" type="text/template">
  // ...
</script>
  • Create a compiled template
var eventTpl = _.template($('#event-template').html());
  • Call the template function with your data and get back HTML
var body = eventTpl({
  date:  target.date.format('MMMM Do YYYY'),
  events: target.events
});

Here’s a forked version of your previous fiddle, updated to use an underscore template for the modal contents.

Perfect :sunglasses:
Much appreciated!

Thanks for detailed explanation and fork looks so easy now ha.

I didn’t realise we needed multiple templates and stuff, gives me scope for moving forward with extra templates now I understand. Much cleaner now separating our code and data :sunny:

I’m also working on some extra functionality for this calendar, as you can see the different colors, this represents different categories which I’ll be filtering… though another thread once up and running if I need help.

Update
I just realised the filter :grinning:

events = events.filter(event => !moment(event.date).isBefore(today));

And slightly off topic fretburner…

I’ve been using underscore past couple of weeks just wondering if this is something you use yourself? What do you think of underscore as a templating framework? What do you use?

Reason I ask - I read an article today talking about underscore is dead with the arrival of ES6
What your view on this? And do you use Babel (religiously)?

Does ES6 Mean The End Of Underscore / Lodash?
https://derickbailey.com/2016/09/12/does-es6-mean-the-end-of-underscore-lodash/

Thanks again!
Barry

Yeah I’ve been using lodash on a project lately. Whether I’d use it for templating would depend: if there’s a limited amount of markup to be generated and I’m already including underscore then sure, but in other situations I’ve used something like mustache/handlebars (I prefer the syntax).

Regarding Derick Bailey’s article, I think he has a point in that you don’t want to be including a whole lib like underscore or lodash if all you’re going to use are some basic array methods like map and filter. Where those libraries shine is with more heavy duty data manipulation, because it’s really easy to chain together a bunch of simple functions to do some very complex things.

We actually published an article on the JS channel not so long back that you might find interesting: 10 Lodash Features You Can Replace with ES6

As for ES6/Babel, I’m starting to get into that more. I have a project at the moment where I’m forcing myself to use ES6 syntax/features where possible so I can become fully conversant with them.

1 Like

Yes I did look at handlebars, so many different libraries :confused:
It usually boils down to what the person is most comfortable with, as you mention yourself, handlebars, or best fit for purpose.

Thanks for the link, I had a browse through good write up, advanced stuff!

I’m still a JS novice so a little over my head, though nice to see what else can be done, still learning about things like currying and using operations etc.

That’s another reason I asked about ES6, might dive straight into ES6 than coding in older syntax, something to think about. Though maybe best get the fundamentals before jumping the gun.

I look forward to when I can do this :grin:

As for ES6/Babel, I’m starting to get into that more. I have a project at the moment where I’m forcing myself to use ES6 syntax/features where possible so I can become fully conversant with them.

:grinning: Nice to hear!

And again, thanks for sharing the knowledge.

And one last question which I came across today, if you get a chance for a quick look fretburner :sunglasses:

As things stand, on clicking a day in the calendar, the modal pops up and displays all the events for that day works good as you know.

But now, I need to create another instance of this calendar, but this time only show the modal for each event and not all events. Basically, only allow each event to be clicked and not the full day.

I was trying today though couldn’t figure out how to remove _each and get the details for each event?

I updated your fiddle visually to explain.

This time I need something like below when a button is clicked.


<h4>Event Title</h4>
<p>startTime - endTime</p>
<p>location</p>

I need to change the below so we don’t loop through all the events but only show the details for each event singularly.

<script id="event-template" type="text/template">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">x</span>
        </button>
        <h4 class="modal-title">Events on <%= date %></h4>
      </div>
      <% _.each(events, function(event) { %>
        <%= event.startTime %> - <%= event.endTime %><br>
        <div class="event"><%= event.title %></div>
        <%= event.location %><br>
      <% }); %>
    </div>
  </div>
</script>

And we’ll need to move data-target="#events-list-modal" so this triggers each event now and not the full day.

<div class="days">
    <% _.each(days, function(day) { %>
        <div class="<%= day.classes %>" data-target="#events-list-modal">
          <div class="day-num"><%= day.day %></div>
          
          <div class="event-markers event-spots">
          <% _.each(day.events, function(event, index) { %>
          	<div class="event-marker event-spot <%= event.title %> event-marker--clndr-cat-<%= event.cat ? event.cat.id : '' %> <%= index < 4 ? 'show-marker' : '' %>">
          		<div class="event-spot__inner"></div>
...

Thanks Barry

Hi Barry,

That’s a bit trickier, because it doesn’t look as though the calendar plugin provides a way to identify which event was clicked. The click handler you set only seems to receive a collection of events for the selected day.

The best way so far that I could think of to get around it is to listen for click events on the event divs themselves.

To do that, first you’ll need to amend the calendar template to add a data attribute to each event div with its index in the events array:

<div class="event-markers event-spots">
  <% _.each(day.events, function(event, index) { %>
    <div data-event-id="<%= index %>" class="event-marker event-spot <%= event.title %> event-marker--clndr-cat-<%= event.cat ? event.cat.id : '' %> <%= index < 4 ? 'show-marker' : '' %>">
      <div class="event-spot__inner"></div>
    </div>
  <% }); %>
  ...

and then, after initializing the calendar plugin, add an event listener to handle any click events on divs with the event-marker class:

$('#calendar').on('click', '.event-marker', function(event) {
  var id = $(this).data('eventId');
  var event = events[id];
  // Now you can render your modal content and display it
})
1 Like

Your a god send fretburner :slight_smile:

Great timing.
Been working on this for past 3-4 hours… close to dismantling my computer ha

That’s a bit trickier, because it doesn’t look as though the calendar plugin provides a way to identify which event was clicked. The click handler you set only seems to receive a collection of events for the selected day.

This is what was causing the main problem. The only feature out of the box is clicking the day itself and showing all events. As you’ve realised, you can’t click the events separately.

Tried everything… I’ll give this a shot and let you know how things go!

Cheers, Barry

The id’s are been added correctly.

But all events are still showing inside the modal.
Not sure if I have placed the click function in the right place?

Update fiddle for quick viewing

Did you mean like this?

click: function (target) {
      if(target.events.length) {
         var body = eventTpl({
           date:  target.date.format('MMMM Do YYYY'),
           events: target.events
         });
         //console.log(target.events);
         $("#events-list-modal").html(body);
         $('#calendar').on('click', '.event-marker', function(event) {
  	        var id = $(this).data('eventId');
  		var event = events[id];
         $("#events-list-modal").modal('show');
      })
      }
    },

Thanks, Barry

Hey Barry,

You need to attach the handler after the calendar has been created (not inside of the existing click handler).

You also need to adjust your event template slightly to render a single event, rather than multiple ones.

Have a look at the fiddle I’ve updated here

1 Like

This is perfect!

Made my night fretburner :smiley: :sunglasses:

I’ll be changing some of the classes to fit the new instance as styling is slight different, this won’t be a problem now we have everything in working order.

And incase your wondering.
The first instance we worked on is a mini calendar and this, second instance will be a bigger version with more focus on each event.

I’ll draw this thread to a close, been a busy few nights and sure you’ve had enough of calendars for one week :slight_smile:

As mentioned previous, some more features to work on which I’ll create a new thread if I need any assistance though some good examples here.

Thanks again!
Been a great help.

Barry

One small issue fretburner

Each day’s events id always starts from 0

<div data-event-id="<%= index %>"

So when clicked, the events link to the same event for each day.

Is this an easy fix?

Thanks,
Barry

Doh! Yes indeed… the index relates to the selected day’s array of events, not the array of all events.

(Edit: Just to clarify, that was me making a silly mistake. What can I say… it was late and I was tired :blush:)

So, what we can do is get the event we want by title, so we’d need a function to handle that:

function getEventByTitle(title) {
  var event = events.filter(function(ev) {
    return ev.title === title
  });
  return (event.length > 0) ? event[0] : null;
}

What we’re doing here is just filtering the array to return the events with a matching title (which is hopefully unique). Filter returns an array, so we’ll check if it’s not empty and return the first element, else return null.

Next we need to change the event click handler to call this new function:

$('#calendar').on('click', '.event-marker', function(event) {
  var title = $(this).data('eventTitle');
  var event = getEventByTitle(title);
  
  if (event) {
    var body = eventTpl({
      event: event
    });

    $("#events-list-modal").html(body);
    $("#events-list-modal").modal('show');
  }
});

If we get back null from getEventByTitle() then we don’t do anything. We should always get something back, but it’s a good idea to check anyway.

Lastly we need to tweak the calendar template, to set a data attribute with the event’s title instead of the ID:

<% _.each(day.events, function(event, index) { %>
    <div data-event-title="<%= event.title %>" ...

That should then display the selected event correctly.

Right on!

:grinning: that makes two of us fretburner… It’s shows that we’re human ha

And thanks for quick response looking good.

Will get back if any issues,

Barry

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.