event-calendar-wolfactive-web-development-wolfactive-949x475
08/06/2020

Làm event calendar không dùng thư viện

Hiện nay có rất nhiều thư viện làm event calendar. Nhưng kèm theo việc sử dụng thư viện là web bạn sẽ ảnh hưởng tới tốc độ load web. Thư viện không trình bày đúng theo ý của bạn và bạn phải vào source thư viện để chỉnh sửa. Điều đó khả thi đó nhưng nếu bạn không chỉnh sửa được thư viện thì bài viết này thật sự hữu ích cho bạn. Trong bài viết này mình sẽ hướng dẫn các bạn có thể tự làm một event calendar theo đúng ý của các bạn.

Lên khung sườn event

Trước tiên mình sẽ tạo một object event trong đó kèm theo ngày xảy ra sự kiện và  cùng với title trong event. Mình có 2 object ngày trên event. Thật ra mình có hàm để tính ra object này với input đầu vào date bất kỳ. Nhưng với bài chia sẻ này mình sẽ không show hàm đó và lấy ví dụ từ một object nhất định.

dayOne ={day: 9,month:6,year: 2020,Day:  2};
var dayTwo ={day: 8,month: 7,year: 2020,Day:  3};

Sau đó mình tạo thêm một array object event cùng với title kèm theo cũng event:

var events = [{'Date': dayOne, 'Title': '<i class="fas fa-birthday-cake"></i>'},{'Date': dayTwo, 'Title': '<i class="fas fa-birthday-cake"></i>'}]

Vậy là bước đầu tạo event đã xong bây giờ chúng ta cùng đi sang bước tiếp theo. Sẽ là bước hardcore nhất :))) Nhưng không sao mình sẽ cùng các bạn giải quyết.

Thêm Javascript vào event calendar in khung lịch và event đầu tiên

Trước nhất mình sẽ DOM tới thẻ div mà mình muốn đặt event calendar(tạo một array dayData do hàm lấy ngày trả về Thứ là số.):
var dayData=['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];var showCalendar = document.querySelector('.calendar');

Sau đó từ array dayData mình dùng JavaScript in các thứ trong tuần lên giao diện:

dayData.forEach((dayItem) => {showCalendar.innerHTML += `<div class="calendar__label calendar_item">${dayItem}</div>`;});

Sau đó mình thêm tí css :

.calendar.calendar__label.calendar_item{background:#fa587d;color:#fff}.calendar.calendar_item{display:flex;flex:0 0 14.27%;max-width:14.27%;width:100%;height:53px;border-right:1px solid #dadada;line-height:53px;text-align:center;border:1px solid #dadada;justify-content:space-evenly}.calendar.calendar_item:hover{background:#feedf1}.calendar.calendar__label.calendar_item:hover{background:#fa587d}.calendar__btn{text-align:center}.calendar__btn button{margin:0 25px;padding:8px 10px;font-size:15px;-webkit-appearance:none;-moz-appearance:none;background:#fa587d;border:2px solid #fa587d;border-radius:0;color:#fff;cursor:pointer;display:inline-block;letter-spacing:.0333em;line-height:1.25;opacity:1;text-align:center;text-decoration:none;text-transform:uppercase;transition:all .15s linear;text-transform:uppercase;display:none}

Và đây là kết quả:

event-calendar-wolfactive-javascript

Sau khi đã in được các thứ trong tuần chúng ta sẽ in khung lịch ra ngoài giao diện. Mình muốn trang lịch này trang đầu có 49 ô lịch, trang sau có 42 trang  và chỉ hiện được từ 1 tới 2 trang lịch. Mình viết đoạn script như sau: 

var countIndex=0; var countPage=1;for(var i=0; i<13; i++){dayData.forEach((dayItem,index) => {showCalendar.innerHTML += `<div class="caleandar_item week-${i} colunm" data-id=${countIndex} page-id="${countPage}" day="${index}"></div>`;countIndex++; countIndex === 49?countPage++:{}; });}

Ở đây mình truyền các attribute vào các ô lịch. Như day để mình xác định ô đó sẽ là ngày thứ mấy trong tuần. Thuộc tính page-id dùng để phân trang cho lịch. Thuộc tính data-id để giúp mình xác định vị trí của ô lịch trong calendar. Và đây là kết quả:

event-calendar-print-wolfactive-javascript

Sau đó mình sẽ in event đầu tiên vào trên khung lịch. Mình làm vậy thứ nhất để event lúc nào cũng luôn luôn nằm đâu. Đây là một trong những ưu điểm mà bạn có được khi tự viết event calendar.

var colunmArray = document.querySelectorAll('.colunm');
  events.forEach((eventItem, i) => {
    var firstWeek = document.querySelectorAll('.week-0');
    i === 0 ?
    firstWeek.forEach((first)=>{
      if(parseFloat(first.getAttribute("day")) === eventItem.Date.Day){
        eventItem.Date.day === 1 ?
        first.style.background="#4CAF50"
        :first.style.border="1px solid #fa587d";
        eventItem.Date.day === 1 ?
        first.innerHTML = `<span>${eventItem.Date.day}/${eventItem.Date.month}</span><span>${eventItem.Title}</span>`
        :first.innerHTML = `<span>${eventItem.Date.day}</span><span>${eventItem.Title}</span>`;
      }
      parseFloat(first.getAttribute("day")) === eventItem.Date.Day ?
      first.classList.add('first-day') :{};
      parseFloat(first.getAttribute("day")) === eventItem.Date.Day ?
      first.setAttribute("month", eventItem.Date.month ) :{};
      parseFloat(first.getAttribute("day")) === eventItem.Date.Day ?
      first.setAttribute("Date", eventItem.Date.day) :{};
    }):{};
    });

Trước tiên mình DOM tới array các thẻ có class là colunm mình đã tạo khi in các ô lịch ngoài giao diện. Tiếp theo mình DOM tới hàng sau của calendar có class là week-0. Tiếp tục mình đặt điều kiện để lấy event đầu tiên và in ra. Và kèm theo một số điều kiện tô màu để event đầu tiên nổi bật. Nếu ngày event là nằm ngay ô đầu tiên của calendar thì sẽ có background. Và đây là kết quả:

event-calendar-print-event-wolfactive-javascript

Dùng Javascript in ngày trên các ô lịch.

Tiếp theo chắc các bạn đang suy nghĩ rằng làm sao để biết được ngày đầu tiên ở ô là ngày ngày nào nhỉ. Không sao mình sẽ hướng dẫn các bạn. Trước tiên mình tạo một hàm cộng trừ ngày tháng cùng với một số option:

function addDate(myDate,dayplus,event) {
    var date = new Date(myDate);
    var newdate = new Date(date);
    newdate.setDate(newdate.getDate() + dayplus);
    var dd = newdate.getDate();
    var mm = newdate.getMonth()+1;
    var y =  newdate.getFullYear();
    var d =  newdate.getDay();
    event === 1 ? resDate = {
      day: dd,
      month: mm,
      year: y,
      Day: d,
    }:
    event === 5 ? resDate = mm + '-' + dd + '-' + y :resDate = dd + '-' + mm + '-' + y;
    return resDate
}

Ở đây mình sẽ cho event là 1 để kết quả trả về là một object. Tuy nhiên vẫn chưa sử dụng đâu. Đoạn code này sẽ sử dụng hàm đó nè:

var colunmIndex = document.querySelector('.first-day');
  var columnFirst = document.querySelector('#data-0');
  var beforeDayCount = parseFloat(colunmIndex.getAttribute('data-id'))-parseFloat(columnFirst.getAttribute('data-id'));
  colunmFirstCount=addDate(beginDate.value,-beforeDayCount,5);
  columnFirstData=addDate(beginDate.value,-beforeDayCount,1);
  columnFirst.innerHTML = `<span>${columnFirstData.day}/${columnFirstData.month}</span>`;
  columnFirst.style.background="#4CAF50";

Mình DOM tới ô lịch mà mình đã tạo trước đó với class là first-day. DOM tới ô dầu tiên trên khung lịch. Và tính xem từ ngày event đầu tiên với ô đầu tiên cách nhau bao nhiêu ngày. Sau đó mình dùng để cộng trừ ra ngày tháng của ô đầu tiên. Với 2 giá trị trả về là 1 biến object, một biến là string. Sau đó mình dùng biến object để in ra khung lịch. Và đây là kết quả:

event-calendar-print-event-first-wolfactive-javascript

Tiếp theo chúng ta in các ngày vào các ô còn lại còn lại của calendar. Và lúc này các bạn đau đầu suy nghĩ làm sao để in ra đây ahihi :))). Các bạn còn nhớ biến countIndex mình có đặt ở đoạn code đầu không? Lúc này chúng ta sẽ cần đến biến đó: (*)

for (var i = 1; i <countIndex; i++){
    if(i !== parseFloat(colunmIndex.getAttribute('data-id'))){
        columnData=addDate(colunmFirstCount,i,1);
        columnData.day === 1 ?colunmArray[i].innerHTML+=`<span>${columnData.day}/${columnData.month}</span>`
         :colunmArray[i].innerHTML+=`<span>${columnData.day}</span>`;
         columnData.day === 1 ?colunmArray[i].style.background="#4CAF50"
          :{};
    }
  }

Mình dùng for để tính ngày cho từng ô lịch và cho các ngày đầu tháng có background để phân biệt rõ của từng tháng.

event-calendar-print-event-day-wolfactive-javascript

Dùng Javascript in các event còn lại và phân  trang cho event calendar

Tiếp theo để in các event còn lại mình dùng đoạn code sau: 

events.forEach((eventPrint, i) => {
    if (i !== 0){
      var indexPrint = parseFloat(colunmIndex.getAttribute('data-id'))+ distanceDate;
      for(var i; i <colunmArray.length; i++){
        parseFloat(colunmArray[i].getAttribute('data-id')) === indexPrint ?
         columnPrint = colunmArray[i] : {};
      }
      columnPrint.innerHTML+=`<span>${eventPrint.Title}<span>`;
      columnPrint.classList.add(`event-${eventPrint.event}`);
    }
    return colunmIndex;
  });

Ở đây mình đặt điều kiện loại bỏ event ban đầu và bắt đầu tim các ô có data-id bằng indexPrint. Với indexPrint được tính bằng cách cộng data-id của event đầu tiên cộng với khoản cách của event đó tới event đầu tiên. Bằng cách đơn giản các bạn viết hàm chuyển object dayTwo về day và trừ nhau ra. Mình có viết hàm này rồi. Nhưng mình nghĩ bài viết này khá dài nên sẽ không để vào. Nhưng mình hướng dẫn các bạn cách thức tính như sau:

var distanceDate = (events[1].day + events[1].month*30) - (events[0].day + events[0].month*31)

Tuy nhiên để ổn hơn các bạn có thể viết hàm để có thể dùng trong mọi trường hợp. Và đây là kết quả:

event-calendar-print-event-day-2-wolfactive-javascript

Và tiếp theo mình sẽ phân trang và viết các điều kiện kèm theo như nếu sự kiện thứ hai xuất hiện trong 49 ô lịch đầu tiên thì sẽ không có trang thứ 2 và chỉ có 49 ô đầu tiên xuất hiện. Tuy nhiên đoạn code trên cần thêm một tí

for (var i = 1; i <countIndex; i++){
    if(i !== parseFloat(colunmIndex.getAttribute('data-id'))){
        columnData=addDate(colunmFirstCount,i,1);
        columnData.day === 1 ?colunmArray[i].innerHTML+=`<span>${columnData.day}/${columnData.month}</span>`
         :colunmArray[i].innerHTML+=`<span>${columnData.day}</span>`;
         columnData.day === 1 ?colunmArray[i].style.background="#4CAF50"
          :{};
    }
    // Phần cần thêm vào 
if(parseFloat(colunmArray[i].getAttribute('page-id')) !== 1){ colunmArray[i].style.display = "none"; calendarNext.style.display = "inline-block"; }
// Phần cần thêm vào }

Các bạn thêm đoạn code sau dê thêm nút nhấn chuyển trang cho lịch:

calendarNext ? calendarNext.onclick = function(){
    for (var i = 0; i <countIndex; i++){
      if(parseFloat(colunmArray[i].getAttribute('page-id')) !== 1){
        colunmArray[i].style.display = "flex";
        calendarPrev.style.display = "inline-block";
        calendarNext.style.display = "none";
      }
      if(parseFloat(colunmArray[i].getAttribute('page-id')) === 1){
        colunmArray[i].style.display = "none";
      }
    }
  }:{};
  calendarPrev ? calendarPrev.onclick = function(){
    for (var i = 0; i <countIndex; i++){
      if(parseFloat(colunmArray[i].getAttribute('page-id')) !== 1){
        colunmArray[i].style.display = "none";
        calendarPrev.style.display = "none";
        calendarNext.style.display = "inline-block";
      }
      if(parseFloat(colunmArray[i].getAttribute('page-id')) === 1){
        colunmArray[i].style.display = "flex";
      }
    }
  }:{};
  var eventEnd = document.querySelector('.event-end');
  if(parseFloat(eventEnd.getAttribute("data-id")) < 50) {
    for (var i = 1; i <countIndex; i++){
      if(parseFloat(colunmArray[i].getAttribute('data-id')) > 49){
        colunmArray[i].style.display = "none";
        calendarNext.style.display = "none";
      }
    }
  }
}

Đây là kết quả: 

event-calendar-wolfactive-javascript

Khi sự kiện  nằm trong 49 ô đầu tiên

event-calendar-print-event-day-next-wolfactive-javascript 

event-calendar-print-event-day-prev-wolfactive-javascript

Tổng kết

Vậy là mình đã hướng dẫn xong các bạn cách làm event calendar không cần dùng thư viện. Nếu các bạn có thắc mắc hay có cách nào hay hơn hoặc có góp ý nào thì comment ở dưới nhé để team tụi mình có thể các thiện nhiều hơn cũng như support các bạn 😀 😀 

Vui lòng chọn size trước khi đặt hàng (*)