Fire on submit instead of checkbox change

Hi all

I have a filtered item list that shows when I select a checkbox. I’d like the list only to show when I click the submit button.

How can I change the functionality of:

$(".chkbox").change(function () ...

…onto my submit button so it only changes on submit?

<input type="submit" class="hit">

I did try below but nothing happened?

$(".hit").click(function () ...

Working fiddle example

I’d also like to keep the functionality of the close button so the list will close and update automatically.

Thanks,
Barry

You could do something like this:

$(".hit").click(function () {
  $list.empty();
  $('.chkbox').each(function() {
    var a = $('label[for="' + this.id + '"]').text();
    if (this.checked) {
      $list.append('<li><a href="#">' + a + '</a><button class="closebutton" value="' + a + '">X</button></li>');
    }
  })
});

Every time the button is clicked, it empties the UL and then loops over the checkboxes, adding back a LI for each one that is checked.

1 Like

Thats what I was missing, works great!
Saved the day again for me thanks fretburner :smiley:


I have now extended this snippet of code, a couple of issue if you get chance to take a look.

First
For some reason, if we start at the top of the checkbox list and select, example, Sports and submit, it removes all Sports items, it should keep Sports items and remove the items not selected.
But if I start at the bottom and select Lectures first and click submit, it works, keeping the Lectures items and removing the others. As it should.

Why does it act differently depending on which end we start in the list?

Second
When I close a filter tag, the checkboxes deselect, works good, but how do we also update the list. So when a filter is closed, update the list.

What am I missing?

New fiddle with all code

Cheers, Barry

Are you about today @fretburner any suggestions on the remaining issues mentioned in my last post, appreciated thanks.

Barry

Hey Barry,

I had a look at your fiddle yesterday - the code is a little bit busy, so I thought I’d have a go at refactoring it to get it working and to make it a little easier to maintain/work with.

Here’s my fiddle: http://jsfiddle.net/3pefkgm8/1/

I’ll briefly step through what the code is doing:

$(".hit").click(updateEventList);

I’ve moved the click handler code to a separate function (I’ll explain why in a minute):

function updateEventList() {
  var cats = getSelectedCategories();
  renderSelectedCategories(cats);
  showEvents(cats);
}

This function basically gets the selected categories (as an array) from the checkboxes, and passes them to renderSelectedCategories(), which adds the category name and close button to the UL, and showEvents(), which is responsible for updating the events that get displayed.

function getSelectedCategories() {
  return $('.checkit:checked').map(function(checkbox) {
    return this.id;
  }).get();
}

This function gets all the checked checkboxes, and then loops over them with map().

If you’re not familiar with map, it allows you to loop over a collection and return a value for each item - those values are then returned as a new collection. Here I’m using it to get an array of checkbox IDs from the array of checkbox DOM elements.

It’s necessary to call get() on the result to transform it from a jQuery collection back to a regular JS array.

function renderSelectedCategories(categories) {
  $list.empty();
  categories.forEach(function(cat) {
    $list.append(
      '<li><a href="#">' + 
      cat + '</a><button class="closebutton" value="' + 
      cat + '">X</button></li>'
    );
  });
}

This function is similar to the code you had before, but it’s no longer necessary to check for checked values, as we’re already passing in an array of the categories we want to be displayed.

function showEvents(categories) {
  $('.event-spot').hide();
  categories.forEach(function(cat){
    $('.'+cat).show();
  });
}

This is pretty straightforward - it just hides all of the events, then loops over the selected categories, calling show() on each one.

Finally, we just need to modify the close button handler slightly:

$list.on('click', '.closebutton', function () {
  var b = this.value;
  $("#filtered-tags div:contains('" + b + "')").remove();
  $('label:contains("' + b + '")').find('.checkit').prop('checked', false);
  updateEventList();
});

The important thing here is that I’ve added a call to updateEventList() (hence why it’s a separate function) to trigger re-rendering when a category is removed.


On a separate note, is there any reason why you’re having users click a submit button and close buttons to update the selected categories? You could just have the whole thing react to checkbox changes, which I think would give you a cleaner interface.

I’ve made a quick fiddle to show you what I mean: http://jsfiddle.net/n6vLc29t/1/

Doing it that way allows you to remove about half the code.

2 Likes

Thanks a lot fretburner!
Cheers for quick getting back.

I’m just trying to digest this while working on an updated fiddle to explain why I’m using submit onclick event and not the change event. I know, it would be much easier, I did have a change event working very well, it is the click that has caused all the pain ha :neutral_face:

One small issue I can see while I’m updating…
All the events should show when we have no check boxes or filters selected, currently, once we finish using the form and have nothing selected, it stays blank. It should default to show all.

I think simple show(); somewhere into the code if else ?

Post back shortly with update myself, cheers,
Barry

Hi fretburner

I have been trying to find the old fiddle to explain better. Numerous versions using different classes and markers everywhere :upside_down:

On a separate note, is there any reason why you’re having users click a submit button and close buttons to update the selected categories?

In short, regarding the click event and the reason I didn’t post this was because I didn’t want to bombard you/anybody with lots of code. All the functionality we have been working on, is for an instance of the calendar we worked on last week :slight_smile:

If you check - fiddle example
You’ll see we know have a filter category modal popup, which will be the main functionality on this cal and only want the calendar to update once submitted. This filter will stay applied across the full calendar until changed, until submit is clicked again.

Note, this fiddle is old copy and not current, using just as an example to show you.

And the filters will be showing just below the calendar.

So far, what you suggest and have explained in great detail, which I appreciate greatly, is working well!
Just the issue with show() all when nothing is selected?

I’ll have access to my updated files tomorrow, hopefully I can update and bring closure :smile:


I then need to update the list of events based on cat and markers showing, though I’ll leave that for another day, things becoming very busy now, as you say.

Cheers, Barry

In that case, I’d add a check at the beginning of showEvents() - if categories is empty, show all events, else run the rest of the function.

Cool :sunglasses:

Updated function, seems to be working good - fiiddle update

function showEvents(categories) {
  	$('.event-spot').hide();
    categories.forEach(function(cat){
    	$('.'+cat).show();
    });
    if($(categories).length===0){
        $('.event-spot').show();
    }
  }

Speak soon, and thanks,
Barry

Things have become a little confused now the working snippet from yesterday is added inside the moment calendar.

And apologies if going over old ground here, I know we built lots of code on a previous calendar.

Ok, if we select the filter and select sports, the filter works and the close button defaults back to all events as discussed.

What I don’t understand is where do we place the functions from yesterday so everything works and syncs across all the months?

Inside moments clickEvents:

clickEvents: {
  	click: function (target) {
..
clickEvents: {
    onMonthChange: function(month) {

Maybe outside?

Once we start moving beyond the current month the filters no longer work.

I know this is a pain @fretburner, any guidance thanks.

The fiddle is not the greatest though should explain things - fiddle update

Update
I’ve update the fiddle, we needed to add the class name on to showEvents function $('.'+'event-marker--clndr-cat-'+cat).show() so the markers shown.

Barry

Hey Barry,

The problem with the filters resetting every time you change the month is because you’ve included the HTML for the filter modal inside the calendar template. When you move between months, the modal is being re-rendered, losing the user’s selections. The solution to this is just to move the modal markup outside of the template and into index.html.

The next thing you want to do is make sure that the correct events are shown/hidden when you change between months. The calendar library provides an onMonthChange() callback, which we can use to call updateEventList() - for this to work, you need to remove the $(function() { ... }); that’s wrapping the second half of the code.

Here’s an updated fiddle: http://jsfiddle.net/twtaoxm4/

1 Like

Good catch, didn’t realise, mad busy trying to change the javascript :upside_down:

I added onMonthCahnge, though I was adding all the code from updateEventList, much easier now, once you explain thanks :sunglasses:

Ok, nearly there :grin:

I’ve been working on this most of the day after your reply, things are coming together, though as with any CMS, lots of libraries and other JS slightly conflicting with the visual, not much to worry about.

So far, so good!
Working very well fretburner up to now after latest updated, thank you!


One final issue, which I was saving till last, probably the most challenging is when we apply the filters. How do we pass this onto the days click event so only the events from the selected categories are shown.

Example
Fire up the latest fiddle, select the filters and click sport.

You’ll see we now have two events showing, one on the 6th and one on the 15th looks good, but now click the 15th day and you’ll see we have a collection of events from all the categories, we should/only want to show the Sport event as this is what we have selected from the filter.

So how/best way to filter the day so only the filtered categories events are shown when clicked/unclicked?

Hope this makes sense,
Cheers,
Barry

You could alter the calendar’s click handler to filter the collection of that day’s events to match the categories that have been selected:

if(target.events.length) {
  var categories = getSelectedCategories();
  var events;
            
  if (categories.length > 0) {
    events = target.events.filter(function(event) {
      return categories.indexOf(event.cat.id) > -1;
    });
  } else {
    events = target.events;
  }

You can re-use the getSelectedCategories() function from before to get an array of selected categories. If the array is empty then you just want to use the unfiltered collection.

Don’t forget to change the references in the template from target.events[i].whatever to events[i].whatever.

Here’s the updated fiddle: http://jsfiddle.net/s1f2fcmt/

1 Like

:grinning: :sunny:

I don’t know weather to cry or jump with joy ha
Been working at this for so long, thanks a lot! Saved me a lot of headaches.
Appreciate the fiddle examples.

Hopefully things will run smoothly once I update my existing code, looking, and working great.

Thanks for explanation it will become more clear once I update the code.

You can re-use the getSelectedCategories() function from before to get an array of selected categories.

Sounds good, likewise for re-using the updateEventList() function. So in hindsight, it was a masterclass to go with the new functions we built, makes things that little easier now, more organised :sunglasses:

Ok, hopefully that is the final post of this thread :neutral_face:

Lots of useful information here, a couple of new methods to understand, map() and indexOf() amongst other things.

Thanks for your time fretburner!

Speak soon,
Barry

Sorry about the continued support here @fretburner
Had a few issues adding this into the CMS, everything is working 90% :grin:

One question based on the snippet below, mainly this section

return categories.indexOf(event.cat.id) > -1;

if(target.events.length) {
  var categories = getSelectedCategories();
  var events;
            
  if (categories.length &gt; 0) {
    events = target.events.filter(function(event) {
      return categories.indexOf(event.cat.id) &gt; -1;
    });
  } else {
    events = target.events;
  }

The problem is I don’t have json data anymore and the data is coming from node variables, ultimately, the cat.id is no longer in use?

this
cat: {id: 'Lectures', name:'category 4'}

is now this
cat: {'Lectures'}

How do I change return categories.indexOf(event.cat.id) > -1; to reflect the new code?

Thanks, Barry

Do you mean that cat now just contains the category as a string? The curly brackets in the example above don’t really make sense.

If it’s just a string, then you can do this:

return categories.indexOf(event.cat) > -1;

I was just literally writing an update :slight_smile:

Yes I’ve realised…

It should be without the brackets
cat: 'Lectures' [quote=“fretburner, post:17, topic:241539”]
If it’s just a string, then you can do this:

return categories.indexOf(event.cat) > -1;
[/quote]

Thats fixed it locally, until I try again tomorrow on another version, this CMS causing lots of debugging. Works great locally.

Couple of questions if you have a minute fretburner:

  1. When we select a filter, even though the makers are hidden, if we click a day which has had markers, and click the day the date still pops up in a modal window without the events. How do we fix this so the modal want fire. Exmaple, filter sport, then click day 28 - fiddle example
  2. Just wondering the snippet below, is this what checks and unchecks the input checkbox in the filter modal on close button click? Reason being, no matter what I do on another version, the checkboxes don’t uncheck… just need to pinpoint the code so I can try and force this somehow.

$('label:contains("' + b + '")').find('.checkit').prop('checked', false);

Barry

Hey Barry,

Apologies for the late reply - last week was a bit hectic.

In the the calendar’s click handler, after you’ve filtered the day’s events to remove any from unselected categeries, you can check if events is empty and return from the function early, before the modal is displayed:

// existing code
if (categories.length > 0) {
  events = target.events.filter(function(event) {
    return categories.indexOf(event.cat) > -1;
  });
} else {
  events = target.events;
}

// No events left to display? Return and don't display the modal
if (events.length === 0) return;

Yep, when you remove a category from below the calendar, that code will uncheck the relevant checkbox in the modal. You could tweak that a bit to use a simpler and more efficient selector though, as b is also the id of the checkbox:

$('#' + b).prop('checked', false);

It’s impossible to say why it’s not working on a different version of the code without seeing it. There must be something that’s different from the code in the fiddle.

I feel the pain :sunglasses:
Been in a similar situation, endless code and debugging :upside_down:

I always like to quote a guy who once said to me

A programmer’s life is one of the hardest

The above works great thanks.

Cool, nice to shorten the code thanks for example I’ll put this in place. Though this is one area where I’ve had to add extra code to get the functionality I need, as mentioned below.

There must be something that’s different from the code in the fiddle.

Yes fiddle works perfect.

I’ve come across a global.js file about 1000+ lines of code in the CMS which has been conflicting with the calendar code we have built. Still working through, finding bugs and trying to find out what’s causing the problems.

I found a snippet mid-week which was .checkbox .parent.parent something along those lines which was causing some unnecessary and random events when working with the filters. I removed it and it worked, so I knew this was the problem.

Overall, everything is now good.

Just one issue I’m still having as outlined in #16 hopefully I can find the code/culprit and fix it. After changing cat.id to cat worked in the fiddle and local code but again, something is conflicting in the CMS and this is not working, still seeing all events even when filtered.

Well, keep at it and lets hope we both have a better week next week :sunny:

If I coming unstuck I’ll post back with some code, hopefully I can fix this last little issue.

Thanks again fretburner!

Barry

Hi @fretburner

Still have issues with #16, though still working on this.
What I have found is a small issue, if you can shed some light on this, would be great thanks.


In the HTML we have a underscore snippet _.each which displays the markers (as shown below).
If we have more than 4 markers then show ellipses.

The only problem is, after using the filter and resetting, this seems to be ignored resulting in all markers showing for that day.

<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 %> <%= index < 4 ? 'show-marker' : '' %>">
        <div class="event-spot__inner"></div>
      </div>
  <% }); %>
  <% if (_.size(day.events) > 5) { %>
        <div class="event-spot event-spot--ellipses show-marker"><div class="event-spot__inner">...</div></div>
  <% } %>
</div>

I think a snippet of Javascript is now needed, maybe another function, or another way?

My attempt of a function to fix the markers, no sucess:

function updateDots(){
  $.each($('.event-spots'), function( index, value ) {
    var $el = $(this);
    if ($el.find('.event-marker.show-marker').length > 5) {
      $el.find('.event-marker').slice(4).removeClass('.show-marker').addClass('.event-spot--ellipses');
    }
  });
}

Which I added to:

$list.on('click', '.closebutton', function () {
    var b = this.value;
    $("#filtered-tags div:contains('" + b + "')").remove();
    $('#' + b).prop('checked', false);
    updateEventList();
    updateDots();
  });

If we work by selecting Sport again from the filter, then switch the filter off, you’ll see the problem.

updated fiddle

Barry