8
\$\begingroup\$

I have been working on a calendar widget for the past hour or so. I wonder if it could be compacted any more than it already is (ignoring whitespace).

$(document).ready(function() {
  var d = new Date();
  var cd = d.getDate();
  var cm = d.getMonth() + 1;
  var cy = d.getFullYear();
  var M = cm;
  var Y = cy;

  var days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
  var months = ["January", "Febuary", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];

  monthDays = function(m, y) {
    var d = new Date(y, m, 0).getDate();
    return d;
  };
  firstDay = function(m, y) {
    var d = new Date(y, m - 1, 1).getDay();
    d = (d === 0) ? 7 : d
    return d;
  };
  currentDay = function(d, m, y) {
    var d = new Date(y, m - 1, d).getDay();
    d = (d === 0) ? 7 : d
    return d;
  };

  function calendar(M, Y) {
    M = M || cm;
    Y = Y || cy;
    //calendar dates
    var date = cd + '/' + ('0' + M).substr(-2) + '/' + ('000' + Y).substr(-4);
    var totalDays = monthDays(M, Y);
    var lastMonth = monthDays(M - 1, Y);
    var firstday = firstDay(M, Y);
    var actualDay = currentDay(cd, M, Y);
    //counters
    var dateCount = 1;
    var dayCount = 0;
    //prev padding
    for (var i = 1; i < firstday; i++) {
      var oldDate = lastMonth - ((firstday - 1) - i);
      if (dayCount == 0)
        $('div.widget div.body table').append('<tr>');
      $('div.widget div.body table tr:last').append('<td class="prev"><p>' + oldDate + '</p></td>');
      dayCount++;
      if (dayCount == 7) {
        $('div.widget div.body table tr:last').after('</tr>');
        dayCount = 0;
      };
    };
    //calendar
    for (var i = 1; i <= totalDays; i++) {
      if (dayCount == 0)
        $('div.widget div.body table').append('<tr>');
      $('div.widget div.body table tr:last').append('<td data-date="' + i + '" data-day="' + dayCount + '"><p>' + i + '</p></td>');
      dayCount++;
      if (dayCount == 7) {
        $('div.widget div.body table tr:last').after('</tr>');
        dayCount = 0;
      };
    };
    //next padding
    if (dayCount != 0) {
      var nextMonth = 0;
      for (var i = dayCount; i < 7; i++) {
        nextMonth++;
        $('div.widget div.body table tr:last').append('<td class="prev"><p>' + nextMonth + '</p></td>');
        if (i == 7)
          $('div.widget div.body table tr:last').after('</tr>');
      };
    };
    $('div.widget div.head p.day').html(days[actualDay - 1]);
    $('div.widget div.head h1.date').html(cd);
    $('div.widget div.head p.month').html(months[M - 1] + ' / ' + Y);
  };
  calendar();
  //calendar date clicker
  $(document).on('click', 'div.widget div.body table td:not(.prev, .next)', function() {
    var trg = $(this);
    var date = $(this).data("date");
    var day = $(this).data("day");

    $('div.head p.day').html(days[day]);
    $('div.head h1.date').html(date);
  });
  //previous month clicker
  $(document).on('click', 'div.head i.trg-prev', function() {
    $('div.widget div.body table tr.head').nextAll().remove();
    M = M - 1;
    if (M == 0) {
      M = 12;
      Y = Y - 1;
    };
    calendar(M, Y);
    $('div.widget div.head p.month').html(months[M - 1] + ' / ' + Y);
  });
  //next month clicker
  $(document).on('click', 'div.head i.trg-next', function() {
    $('div.widget div.body table tr.head').nextAll().remove();
    M = M + 1;
    if (M == 13) {
      M = 1;
      Y = Y + 1;
    };
    calendar(M, Y);
    $('div.widget div.head p.month').html(months[M - 1] + ' / ' + Y);
  });
});
@import url(http://fonts.googleapis.com/css?family=Open+Sans:400,300,600,700,800);
 html,
body {
  background-color: #F0F0F0;
  text-align: center;
}
* {
  margin: 0;
  padding: 0;
  font-size: 12px;
  font-weight: 400;
  font-family: 'Open Sans', sans-serif;
  text-decoration: none;
}
div.widget {
  height: auto;
  overflow: auto;
  display: inline-block;
  position: relative;
  margin: auto auto;
  margin-top: 100px;
  border-radius: 2px;
  box-shadow: 0 2px 10px #AAA;
}
div.widget div.head {
  display: block;
  position: relative;
  padding: 10px;
  background-color: #3498DB;
  background: -webkit-linear-gradient(#3498DB, #2980B9);
  background: -moz-linear-gradient(#3498DB, #2980B9);
  background: -o-linear-gradient(#3498DB, #2980B9);
  background: linear-gradient(#3498DB, #2980B9);
  border-top-left-radius: 2px;
  border-top-right-radius: 2px;
  text-align: center;
}
div.widget div.head p,
div.widget div.head h1 {
  display: block;
  color: #FFF;
  cursor: default;
}
div.widget div.head p {
  font-size: 10px;
}
div.widget div.head h1 {
  font-size: 48px;
}
div.widget div.head p.day {
  margin-top: 25px;
}
div.widget div.head p.month {
  margin-bottom: 25px;
}
div.widget div.head i.trg {
  height: 12px;
  position: absolute;
  margin: auto auto;
  top: 0;
  bottom: 0;
  color: #FFF;
  cursor: pointer;
  -webkit-transition: all ease-in-out 0.15s;
  -moz-transition: all ease-in-out 0.15s;
  -o-transition: all ease-in-out 0.15s;
  transition: all ease-in-out 0.15s;
}
div.widget div.head i.trg.trg-prev {
  left: 10px;
}
div.widget div.head i.trg.trg-prev:hover {
  left: 9px;
}
div.widget div.head i.trg.trg-next {
  right: 10px;
}
div.widget div.head i.trg.trg-next:hover {
  right: 9px;
}
div.widget div.body {
  display: block;
  position: relative;
  background-color: #FFF;
  border-bottom-left-radius: 2px;
  border-bottom-right-radius: 2px;
  text-align: center;
}
div.widget div.body table {
  margin: auto auto;
  border-collapse: collapse;
  border-spacing: 0;
  cursor: pointer;
}
div.widget div.body td {
  padding: 10px;
  -webkit-transition: background-color ease-in-out 0.15s;
  -moz-transition: background-color ease-in-out 0.15s;
  -o-transition: background-color ease-in-out 0.15s;
  transition: background-color ease-in-out 0.15s;
}
div.widget div.body td:hover {
  background-color: #F0F0F0;
  cursor: pointer;
}
div.widget div.body td.prev {
  background-color: #FFF;
  color: #AAA;
  cursor: default;
}
div.widget div.body tr.head td {
  background-color: #FFF;
  cursor: default;
}
div.widget div.body tr.head td > * {
  color: #3498DB;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
<div class="widget">
  <div class="head">
    <p class="day"></p>
    <h1 class="date"></h1>
    <p class="month"></p>
    <i class="trg trg-prev fa fa-fw fa-caret-left"></i>
    <i class="trg trg-next fa fa-fw fa-caret-right"></i>
  </div>
  <div class="body">
    <table>
      <tbody>
        <tr class="head">
          <td>
            <p>M</p>
          </td>
          <td>
            <p>T</p>
          </td>
          <td>
            <p>W</p>
          </td>
          <td>
            <p>T</p>
          </td>
          <td>
            <p>F</p>
          </td>
          <td>
            <p>S</p>
          </td>
          <td>
            <p>S</p>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</div>

\$\endgroup\$
  • \$\begingroup\$ The bottom question appears to be off-topic as we cannot assist with adding new implementation. We can still fulfill the compaction request, though. \$\endgroup\$ – Jamal Aug 19 '15 at 7:49
  • \$\begingroup\$ @Jamal, No Worries... it's just that the icons used are part of the calendar to control the date and would be useful when demoing. \$\endgroup\$ – 099 Aug 19 '15 at 7:50
3
\$\begingroup\$

What exactly do you mean with "compacted"? Compactness isn't really something you should go for software development, since usually it doesn't have any advantages (performance or otherwise). You should instead be focused on functionality and readability of the source code.

This leads directly to the first point of critic: The variable names. What are cm vs M and cy vs Y? Use long(er), expressive variable names. Also variables shouldn't start with capital letters in JavaScript unless they are classes.

You can always uses a minifier for production code, if you want to improve load times.


monthDays, firstDay and currentDay definitions are lacking the var keyword putting them into the global namespace.

All three could have better names, e.g. monthDays => daysInMonth, firstDay => weekdayOfFirstDay, currentDay => weekday. (Generally you should use the phrases "weekday" and "day of month" in function and variable names. "Day" and "date" alone are too ambiguous.)

Also the functions firstDay and currentDay are almost identical.


The parameters of the function calendar shadow the variables M and Y.

('0' + M).substr(-2) should be extracted into a function, e.g.

function padWithZeros(value, length) {
  var zeros = Array(length).join('0'); // Repeats '0' length-1 times.
  return (zeros + value).substr(-length);
}

dateCount isn't used.

Don't repeat jQuery selectors. Run $('div.widget div.body table') once and store it in a variable.

Instead of searching for $('div.widget div.body table tr:last') each time, store a reference to the table row when you create it:

// before loop
var calenderTable = $('div.widget div.body table');
var currentRow;

// inside loop
  if (dayCount == 0)
    currentRow = $('<tr>').appendTo(calenderTable);
  currentRow.append('<td class="prev"><p>' + oldDate + '</p></td>');

$('div.widget div.body table tr:last').after('</tr>'); does absolutely nothing. ...append("<tr>") already appended the "complete" element. Keep in mind you are manipulating the DOM here, not generating HTML source code. As a reminder you should use ...append("<tr></tr>") instead.

The use of paragraphs in the table cells (also later in the HTML) seems unnecessary.

All three loops are virtually identical and I believe the use of the variable currentDay is unnecessary. The weekday should be calculable from the loop index. (EDIT: added missing unnecessary.)


In the first click handler you define trg but don't use it.


widget is a far too generic class name, and I don't understand what trg stands for.


The use of HTML elements seems a bit random. Why is the day of the month a level 1 header and the other parts paragraphs?

Consider using thead and th elements in the table. That way you can also get rid of the duplicate class name head.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.