มีบั๊กในโปรแกรมล่าสุด ทำให้ v77.html มีปัญหา ผมทดสอบพบว่า ถ้าอัพโหลด json ด้วย v65.html (เวอชั่นก่อน) จะสามารถใช้ v77.html ได้ปกติ
จากปัญหาที่คุณพบในโปรแกรมเวอชั่น v77.html ที่ทำให้ Local Storage บวมและระบบค้างเมื่ออัปโหลดไฟล์ JSON โดยตรง สาเหตุเกิดจาก “ตรรกะการบันทึกข้อมูลแบบแยกปีที่ผิดพลาด” ครับ
สาเหตุของบั๊ก (Root Cause)
ใน v77.html มีการเพิ่มฟังก์ชัน Year Filter (กรองปี) ซึ่งทำให้โค้ดส่วน saveDataToLocalStorage ถูกแก้ไขให้ “ฉลาดขึ้น” (แต่ผิดพลาด) ดังนี้ครับ:
- การดึงข้อมูลจากหน้าจอ (
getTableData): ฟังก์ชันนี้จะดึงข้อมูล ทุกแถว ที่ปรากฏใน Tab เดือนมกราคม-ธันวาคม บนหน้าจอ ไม่ว่าแถวนั้นจะเป็นของปีไหนก็ตาม - ปัญหาตอนอัปโหลด JSON: เมื่อคุณอัปโหลดไฟล์ JSON ใน
v77.htmlโค้ดจะวนลูปเรียกaddRow(d)เพื่อสร้างแถวใส่ลงใน Tab ต่างๆ ทันที (ซึ่งอาจมีข้อมูลหลายปี เช่น 2024 และ 2025) - ตรรกะการบันทึกที่ผิดใน
saveDataToLocalStorage:- โปรแกรมจะดึงข้อมูลปีที่เลือกอยู่ (เช่น 2025)
- จากนั้นไปดึงข้อมูลเก่าจาก Local Storage มา แล้ว “กรองเอาปีอื่นๆ ออกเก็บไว้” (เช่น เก็บปี 2024 ใน Storage ไว้)
- สุดท้ายเอา “ข้อมูลปีอื่นๆ จาก Storage” มาบวกกับ “ข้อมูลทั้งหมดบนหน้าจอ”
- ผลที่ได้: ถ้าในไฟล์ JSON มีข้อมูลปี 2024 และหน้าจอก็โชว์ปี 2024 อยู่… ข้อมูลปี 2024 จะถูกบันทึก ซ้ำสองรอบ (รอบแรกจาก Storage เดิม รอบสองจากหน้าจอที่เพิ่งอัปโหลดมา)
- หากมีการบันทึกซ้ำ ข้อมูลจะเพิ่มขึ้นเป็นเท่าตัว (Exponential Growth) เช่น 1,000 -> 2,000 -> 4,000 แถว จน Local Storage เต็มและเบราว์เซอร์ค้าง
วิธีแก้ไข
คุณต้องแก้ไข 2 จุดในไฟล์ v77.html เพื่อให้การอัปโหลดและการบันทึกทำงานสัมพันธ์กันครับ
จุดที่ 1: แก้ไขฟังก์ชัน saveDataToLocalStorage
ให้เปลี่ยนจากการกรองแค่ “ปีเดียว” เป็นการกรอง “ทุกปีที่มีอยู่บนหน้าจอ” เพื่อป้องกันข้อมูลซ้ำซ้อน
หาโค้ดเดิม (ประมาณบรรทัด 1100-1120):
JavaScript
// 3. กรองเอาข้อมูล "ปีอื่นๆ" เก็บไว้ (ห้ามลบ)
let otherYearsData = allData.filter(d => d.date.substring(0, 4) !== currentYearFilter);
// 4. รวมข้อมูลใหม่ (ปีที่ดูอยู่) + ข้อมูลเดิม (ปีอื่นๆ)
const finalData = otherYearsData.concat(currentYearViewData);
เปลี่ยนเป็นโค้ดนี้:
JavaScript
// 3. หาว่าบนหน้าจอมีข้อมูลของปีอะไรบ้าง
const yearsInDom = [...new Set(currentYearViewData.map(d => d.date.substring(0, 4)))];
// 4. กรองข้อมูลใน Storage: เอาเฉพาะปีที่ "ไม่อยู่" บนหน้าจอเก็บไว้
let otherYearsData = allData.filter(d => !yearsInDom.includes(d.date.substring(0, 4)));
// 5. รวมข้อมูล (ข้อมูลปีอื่นๆ ใน Storage + ข้อมูลทุกปีบนหน้าจอ)
const finalData = otherYearsData.concat(currentYearViewData);
จุดที่ 2: แก้ไขฟังก์ชัน loadFromJSONFile
แทนที่จะสั่ง addRow ลงหน้าจอโดยตรง (ซึ่งทำให้หน้าจอปนเปหลายปี) ให้เปลี่ยนเป็นการอัปเดตข้อมูลเข้าตัวแปรหลักแล้วสั่ง Render ใหม่ตามปีที่เลือกอยู่
หาโค้ดเดิม (ในฟังก์ชัน loadFromJSONFile):
JavaScript
data.forEach(d => {
if (d && typeof d === 'object' && d.date) {
addRow(d); // บรรทัดที่เป็นปัญหา
loadedCount++;
// ...
}
});
เปลี่ยนเป็น:
JavaScript
// 1. อัปเดตข้อมูลเข้า dataList (ตัวแปร Global)
dataList = data;
// 2. บันทึกลง Local Storage ทันทีเพื่อความปลอดภัย
localStorage.setItem(STORAGE_KEY, JSON.stringify(dataList));
// 3. สั่งวาดตารางใหม่ โดยใช้ปีที่เลือกอยู่ปัจจุบัน (หรือปีจากข้อมูลใหม่)
const yearToRender = dataList.length > 0 ? dataList[0].date.substring(0, 4) : new Date().getFullYear().toString();
document.getElementById('yearFilterInput').value = yearToRender;
renderDataByYear(yearToRender);
alert(`✅ โหลดข้อมูล ${dataList.length} แถว และบันทึกเรียบร้อยแล้ว`);
สรุป
ปัญหาใน v77.html คือระบบพยายาม “รวมร่าง” ข้อมูลเก่าในเครื่องกับข้อมูลใหม่บนหน้าจอแบบผิดวิธี การแก้จุดที่ 1 จะช่วยให้ระบบฉลาดพอที่จะไม่เซฟข้อมูลซ้ำ และจุดที่ 2 จะช่วยให้หน้าจอแสดงผลเฉพาะปีที่เลือก ทำให้เครื่องไม่ทำงานหนักเกินไปครับ
ทดสอบแล้ว โดยอัพโหลด json ปรากฏว่า โหลดไม่ได้
สาเหตุที่คุณพบปัญหา “โหลด JSON ไม่ได้” หลังจากลองแก้ตามคำแนะนำก่อนหน้า หรือพบว่า “Local Storage พัง/ช้า” ในเวอร์ชัน v77.html เกิดจากความผิดพลาดของระบบ “การรวมข้อมูล (Data Merging)” และการใช้ฟังก์ชันที่ชื่อไม่ตรงกันครับ
ใน v77.html มีการใช้ฟังก์ชันชื่อ renderDataToTable (ไม่ใช่ renderDataByYear) และมีระบบ dataList ที่ทำงานซ้อนทับกับข้อมูลบนหน้าจอ ทำให้เวลาอัปโหลดไฟล์ที่มีข้อมูลหลายปี ข้อมูลจะเกิดการ “บันทึกซ้ำซ้อนเป็นทวีคูณ (Double Entry)” จนหน่วยความจำเต็มครับ
เพื่อให้โหลดไฟล์ได้ปกติและแก้ปัญหาเครื่องค้าง ให้คุณเปลี่ยนโค้ดใน v77.html ตามจุดดังนี้ครับ:
จุดที่ 1: แก้ไขฟังก์ชัน loadFromJSONFile
ฟังก์ชันนี้เดิมจะเรียก addRow วนลูป ซึ่งทำให้หน้าจอกระตุกและเสี่ยงต่อการเซฟซับซ้อน ให้ใช้โค้ดชุดนี้แทนที่ของเดิมทั้งหมดครับ:
JavaScript
function loadFromJSONFile(file) {
const reader = new FileReader();
reader.onload = (e) => {
try {
const data = JSON.parse(e.target.result);
if (!Array.isArray(data)) {
alert("รูปแบบไฟล์ JSON ไม่ถูกต้อง");
return;
}
// 1. อัปเดตตัวแปรหลัก dataList ด้วยข้อมูลใหม่ทั้งหมดจากไฟล์
dataList = data.filter(d => d && typeof d === 'object' && d.date);
// 2. บันทึกลง Local Storage ทันที (ล้างของเก่าทับด้วยของใหม่จากไฟล์)
localStorage.setItem(STORAGE_KEY, JSON.stringify(dataList));
// 3. อัปเดตหน้าจอ: ดึงปีล่าสุดจากข้อมูลมาแสดงผล
const latestYear = dataList.length > 0
? dataList[0].date.substring(0, 4)
: new Date().getFullYear().toString();
document.getElementById('yearFilterInput').value = latestYear;
// ใช้ฟังก์ชัน renderDataToTable (ชื่อที่ถูกต้องใน v77)
renderDataToTable(dataList, latestYear);
alert(`✅ โหลดข้อมูล ${dataList.length} แถวเรียบร้อยแล้ว!`);
if (dataList.length > 0) {
document.getElementById('btnSwitchTab').removeAttribute('disabled');
}
} catch (err) {
console.error("JSON Parse Error:", err);
alert("❌ ไม่สามารถโหลดไฟล์ได้: ไฟล์อาจเสียหายหรือรูปแบบไม่ถูกต้อง");
}
};
reader.readAsText(file, 'UTF-8');
}
**
จุดที่ 2: แก้ไขฟังก์ชัน saveDataToLocalStorage
จุดนี้คือ “หัวใจของบั๊ก” ที่ทำให้ Local Storage เต็ม เพราะมันเอาข้อมูลจากหน้าจอไปบวกซ้ำกับข้อมูลเก่าในเครื่อง ให้แก้ตรรกะการกรองใหม่ดังนี้ครับ:
หาฟังก์ชัน saveDataToLocalStorage แล้วเปลี่ยนโค้ดข้างในเป็นแบบนี้:
JavaScript
function saveDataToLocalStorage() {
try {
// 1. ดึงข้อมูลจากหน้าจอตอนนี้ (ซึ่งเป็นข้อมูลของปีที่เลือกอยู่)
const currentYearViewData = getTableData();
const currentYearFilter = document.getElementById('yearFilterInput').value;
// 2. ดึงข้อมูลทั้งหมดจากเครื่องมาเพื่อเตรียมกรอง
let allStoredData = [];
const stored = localStorage.getItem(STORAGE_KEY);
if (stored) {
allStoredData = JSON.parse(stored);
}
// 3. สำคัญ: กรองข้อมูลในเครื่อง "ทิ้งเฉพาะปีที่กำลังแก้อยู่" เพื่อเอาข้อมูลปีอื่นไว้
let otherYearsData = allStoredData.filter(d => d.date.substring(0, 4) !== currentYearFilter);
// 4. รวมร่าง: ข้อมูลปีอื่นๆ (จากเครื่อง) + ข้อมูลปีปัจจุบัน (จากหน้าจอที่แก้ล่าสุด)
dataList = otherYearsData.concat(currentYearViewData);
// 5. บันทึกกลับลงไป
localStorage.setItem(STORAGE_KEY, JSON.stringify(dataList));
updateAllCharts();
checkStorageQuota(); // เช็คแถบสถานะการใช้พื้นที่
} catch (e) {
console.error("Error saving data:", e);
}
}
**
ทำไมวิธีนี้ถึงแก้ปัญหาได้?
- แก้ปัญหาโหลดไม่ได้: เพราะการใช้
dataList = dataตรงๆ จะเสถียรกว่าการสั่งaddRowทีละแถวผ่านลูป ซึ่งมักจะค้างหากไฟล์มีข้อมูลเกิน 500 แถวครับ - แก้ปัญหา Storage พัง: โค้ดใหม่จะตรวจสอบ
currentYearFilterอย่างเข้มงวด ทำให้เมื่อคุณเซฟ ข้อมูลปี 2024 จะไม่ไปทับซ้อนหรือเบิ้ลกับปี 2025 ในฐานข้อมูล - ความเร็ว: การเรียก
renderDataToTableเพียงครั้งเดียวหลังจากโหลดเสร็จ จะเร็วกว่าการปล่อยให้เบราว์เซอร์วาดตารางใหม่ทุกครั้งที่เจอข้อมูล 1 แถวในไฟล์ JSON ครับ
คำแนะนำ: หลังจากแก้โค้ดแล้ว ให้กดปุ่ม “🗑️ DEL Local Storage” ในโปรแกรมหนึ่งครั้งเพื่อล้างขยะเก่าที่ค้างอยู่ แล้วค่อยอัปโหลด JSON ใหม่อีกครั้งครับ