I have a datepicker with tailwindcss and alpinejs and it works as expected:
this is my index.html file:
<!doctype html>
<html lang="en">
<head>
<title>Datepicker</title>
<script src="https://cdn.tailwindcss.com"></script>
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>
<style>
[x-cloak] {display: none !important;}
</style>
</head>
<body class="flex justify-center">
<div class="flex flex-col my-10 space-y-5">
<button onclick="addToFirstDiv()" class="text-white bg-red-800 p-1 rounded w-64">add to div#first</button>
<button onclick="addToSecondDiv()" class="text-white bg-red-800 p-1 rounded w-64">add to div#second</button>
<div id="first">
<!-- datepicker -->
<div x-data="datepicker()" x-init="[initDate(), initDatepicker()]" x-cloak>
<div class="w-64 rounded-lg ring-1 ring-gray-100 shadow-lg">
<div class="px-3 py-2">
<!-- header -->
<div class="flex justify-between items-center mb-2">
<div class="text-sm font-bold text-gray-800">
<span x-text="MONTH_NAMES[month]"></span>
<span x-text="year"></span>
</div>
<div class="text-gray-500 space-x-1">
<button type="button" class="transition ease-in-out duration-100 inline-flex p-1 rounded" :class="month === currentMonth && year === currentYear ? 'opacity-25' : 'hover:bg-gray-100'" :disabled="month === currentMonth && year === currentYear" @click="previousMonth()">
<svg class="h-4 w-4 inline-flex" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
</svg>
</button>
<button type="button" class="transition ease-in-out duration-100 inline-flex hover:bg-gray-100 p-1 rounded" @click="nextMonth()">
<svg class="h-4 w-4 inline-flex" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
</svg>
</button>
</div>
</div>
<div class="grid grid-cols-7 text-center -mx-1.5 text-gray-500 mb-2" style="font-size: 10px">
<!-- days of week -->
<span>M</span>
<span>T</span>
<span>W</span>
<span>T</span>
<span>F</span>
<span>S</span>
<span>S</span>
</div>
<div class="grid grid-cols-7 text-center mb-3 -mx-1.5">
<template x-for="blankDay in blankDays">
<span class="w-6 h-6 mb-1"></span>
</template>
<template x-for="(date, dateIndex) in daysOfMonth" :key="dateIndex">
<span x-text="date" @click="getDateValue(date)" class="w-6 h-6 mx-auto mb-1 flex justify-center items-center text-xs rounded-full" :class="[isPassedDay(date) ? 'opacity-25' : 'cursor-pointer transition ease-in-out duration-100 hover:bg-gray-100', isToday(date) ? 'text-red-800 font-bold' : 'text-gray-700']"></span>
</template>
</div>
</div>
</div>
</div>
</div>
<div id="second">
</div>
<script src="script.js"></script>
<script>
function addToFirstDiv() {
document.getElementById('first').innerHTML += `<span>added-to-first-div</span>`;
}
function addToSecondDiv() {
document.getElementById('second').innerHTML += `<span>added-to-second-div</span>`;
}
</script>
</div>
</body>
</html>
and this is my script.js:
const MONTH_NAMES = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
function datepicker() {
return {
timestampOfToday: null,
currentMonth: null,
currentYear: null,
month: null,
year: null,
daysOfMonth: [],
blankDays: [],
initDate() {
const today = new Date();
this.currentMonth = today.getMonth();
this.currentYear = today.getFullYear();
this.month = this.currentMonth;
this.year = this.currentYear;
this.timestampOfToday = new Date(this.year, this.month, today.getDate()) / 1000;
},
isToday(date) {
const today = new Date();
const d = new Date(this.year, this.month, date);
return today.toDateString() === d.toDateString();
},
isPassedDay(date)
{
const timestampOfDate = new Date(this.year, this.month, date) / 1000;
return timestampOfDate < this.timestampOfToday;
},
getDateValue(date) {
if (this.isPassedDay(date)) {return;}
let selectedDate = new Date(this.year, this.month, date);
selectedDate = `${selectedDate.getFullYear()}-${selectedDate.getMonth() + 1}-${selectedDate.getDate()}`;
console.log(selectedDate)
},
initDatepicker() {
let daysInMonth = new Date(this.year, this.month + 1, 0).getDate();
let firstDayOfWeek = new Date(this.year, this.month).getDay(); // find where to start calendar day of week
let blankDaysArray = [];
let daysOfMonthArray = [];
if (firstDayOfWeek === 0) {
firstDayOfWeek = 7;
}
for ( let i = 2; i <= firstDayOfWeek; i++) {
blankDaysArray.push(i);
}
for ( let i = 1; i <= daysInMonth; i++) {
daysOfMonthArray.push(i);
}
this.blankDays = blankDaysArray;
this.daysOfMonth = daysOfMonthArray;
},
previousMonth() {
if (this.month === this.currentMonth && this.year === this.currentYear) {return;}
if (this.month === 0) {
this.year--;
this.month = 11;
} else {
this.month--;
}
this.initDatepicker();
},
nextMonth() {
if (this.month === 11) {
this.year++;
this.month = 0;
} else {
this.month++;
}
this.initDatepicker();
}
}
}
my problem is when I click on first button to add an element after Alpine element, it’ll cause several errors and datepicker loses its functionality.
Why is this happening and how can I fix this?
Thank you very much for your help.
2
Answers
In your HTML, add an x-data attribute to the first div to initialize the Alpine data
in addToFirstDiv function, you’re manipulating the DOM instead of Apline data. you can add something like the following code in order to fix it:
i hope this helps
I believe the issue is with the way you’re using the
innerHTML
method. One way to fix the problem is to use theappendChild
method instead: