• November 21, 2025

    การเก็บข้อมูลในรูปแบบ Local Storage นั้นมีระดับความปลอดภัยที่ ปานกลาง ครับ หรืออาจกล่าวได้ว่า ไม่เหมาะกับการเก็บข้อมูลที่ละเอียดอ่อนสูง (Highly Sensitive Data) แต่เพียงพอสำหรับการใช้งานโปรแกรมแบบ Client-Side (ทำงานบนเบราว์เซอร์ของผู้ใช้) อย่างที่คุณกำลังทำอยู่ครับ

    นี่คือการวิเคราะห์ความปลอดภัยของ Local Storage ในบริบทของโค้ดของคุณ:

    🛡️ ลักษณะความปลอดภัยของ Local Storage

    1. จุดที่ปลอดภัย (Good Security Points)

    • ข้อมูลไม่ถูกส่งไปที่เซิร์ฟเวอร์: ข้อมูลทั้งหมดถูกจัดเก็บไว้ในเครื่องคอมพิวเตอร์ของคุณเอง และไม่ถูกส่งไปที่เซิร์ฟเวอร์ใด ๆ เมื่อคุณเข้าชมเว็บไซต์อื่น ซึ่งลดความเสี่ยงจากการถูกดักจับข้อมูลระหว่างการส่ง (Man-in-the-Middle Attack)
    • ผูกกับ Origin (Same-Origin Policy): ข้อมูลใน Local Storage ของโปรแกรมคุณ จะสามารถเข้าถึงได้โดย โค้ด JavaScript ที่มาจากโดเมน (Domain), พอร์ต (Port), และโปรโตคอล (Protocol) เดียวกันเท่านั้น เว็บไซต์อื่น ๆ ไม่สามารถอ่านข้อมูลของคุณได้โดยตรง

    2. จุดที่ “ไม่ปลอดภัย” (Security Weaknesses)

    Local Storage มีช่องโหว่หลัก ๆ สองประการ:

    🔴 ไม่มีการเข้ารหัส (No Encryption)

    ข้อมูลใน Local Storage จะถูกเก็บไว้ในเบราว์เซอร์ในรูปแบบ ข้อความธรรมดา (Plain Text) ไม่มีการเข้ารหัสโดยอัตโนมัติ หมายความว่า:

    • หากมีผู้ใช้คนอื่นเข้าถึงคอมพิวเตอร์ของคุณและโปรไฟล์เบราว์เซอร์นั้นได้ ก็สามารถใช้เครื่องมือ Developer Tools เพื่อดูหรือแก้ไขข้อมูลได้ทันที
    • ข้อมูลจะถูกจัดเก็บเป็นไฟล์ในระบบปฏิบัติการ (ขึ้นอยู่กับเบราว์เซอร์) ซึ่งอาจถูกค้นพบโดยโปรแกรมวิเคราะห์ข้อมูลบางประเภทได้ หากมีการเข้าถึงเครื่องโดยไม่ได้รับอนุญาต

    🟡 ช่องโหว่ Cross-Site Scripting (XSS)

    นี่คือความเสี่ยงที่ใหญ่ที่สุดสำหรับ Local Storage

    • หากหน้าเว็บ 522.html ของคุณเกิดมีช่องโหว่ด้านความปลอดภัยที่ทำให้ผู้โจมตีสามารถฉีดโค้ด JavaScript ที่เป็นอันตราย (Malicious Script) เข้าไปรันบนหน้าเว็บของคุณได้ (ซึ่งเรียกว่า XSS Attack)
    • โค้ดนั้นจะสามารถเข้าถึง Local Storage ได้ทันที เนื่องจากมันรันอยู่บน Origin เดียวกัน และสามารถขโมยข้อมูลตารางทั้งหมดของคุณไปให้ผู้โจมตีได้

    สรุปและความแนะนำ

    ประเภทข้อมูลความเหมาะสมในการใช้ Local Storage
    ข้อมูลที่ไม่ละเอียดอ่อน (เช่น การตั้งค่าธีม, ค่า Preference)เหมาะสม
    ข้อมูลปานกลาง (เช่น ข้อมูลตาราง Premium, ชื่อสินค้า, Note, Contact)ยอมรับได้ (ขึ้นอยู่กับระดับความลับที่คุณให้)
    ข้อมูลละเอียดอ่อนสูง (เช่น รหัสผ่าน, ข้อมูลบัตรเครดิต, ข้อมูลส่วนตัวตามกฎหมาย)ไม่เหมาะสมโดยสิ้นเชิง

    ในกรณีของโปรแกรมคุณ ซึ่งส่วนใหญ่เป็นข้อมูลการทำธุรกรรมหรือข้อมูลลูกค้า (Product, Premium, Name) ที่จัดเก็บเพื่อใช้ส่วนตัวในเครื่องเดียว ถือว่าความเสี่ยงอยู่ในระดับที่ยอมรับได้ สำหรับโปรแกรมประเภทนี้

    แนวทางปฏิบัติเพื่อความปลอดภัยสูงสุด

    1. ระมัดระวัง XSS: เนื่องจากโค้ดของคุณเป็นไฟล์ HTML/JS ที่ใช้เอง (ไม่ได้โหลดจากเซิร์ฟเวอร์ภายนอกที่ซับซ้อน) ความเสี่ยง XSS จึงต่ำมาก ตราบใดที่คุณมั่นใจว่าโค้ดทั้งหมดมาจากแหล่งที่คุณเชื่อถือ
    2. หลีกเลี่ยงการเก็บรหัสผ่าน: ไม่ควรเก็บข้อมูลล็อกอินหรือรหัสผ่านใด ๆ ไว้ใน Local Storage
    3. สำรองข้อมูล: เนื่องจาก Local Storage ไม่ได้ถูกสำรองข้อมูลโดยอัตโนมัติ (หากเบราว์เซอร์เสียหาย หรือคุณล้างแคช/ข้อมูลเว็บไซต์) โปรดใช้ปุ่ม ⬇️ ส่งออก JSON หรือ ⬇️ ส่งออก CSV ที่เราทำไว้ให้ เพื่อสำรองข้อมูลสำคัญเป็นระยะ ๆ ครับ

    โค้ดจะยังคงทำงานได้อย่างถูกต้องบน Hosting Domain ของคุณ (ไม่มีปัญหาทางเทคนิค) แต่การย้ายไปอยู่บนเว็บไซต์ออนไลน์จะยกระดับประเด็นด้าน ความปลอดภัย โดยเฉพาะการใช้ innerHTML เข้ามาเกี่ยวข้องครับ


    1. ปัญหาด้านเทคนิค: โค้ดจะทำงานได้หรือไม่?

    คำตอบ: ทำงานได้ปกติ 💯

    เนื่องจากโปรแกรมของคุณเป็นไฟล์ HTML เดี่ยวที่ทำงานด้วย Client-Side JavaScript (รันบนเบราว์เซอร์ของผู้ใช้เอง) การจะนำไปวางบน Hosting Domain หรือรันในเครื่องตัวเองจึงไม่มีความแตกต่างกันในแง่ของฟังก์ชันการทำงาน โค้ด JavaScript ที่ใช้ innerHTML, document.addEventListener, หรือแม้แต่ Local Storage จะยังคงทำงานในเบราว์เซอร์ของผู้ใช้ตามปกติครับ


    2. ปัญหาด้านความปลอดภัย: ความเสี่ยงของ innerHTML (XSS)

    นี่คือประเด็นสำคัญที่เปลี่ยนไปเมื่อคุณนำโปรแกรมขึ้นออนไลน์:

    💡 ความเสี่ยง Cross-Site Scripting (XSS)

    การใช้ innerHTML เพื่อสร้างโครงสร้าง HTML และใส่ข้อมูลเข้าไปโดยตรง ถือเป็นช่องทางหลักที่อาจนำไปสู่ปัญหา Cross-Site Scripting (XSS) ครับ

    ความเสี่ยงทำงานอย่างไร?

    1. Local Storage เป็นเป้าหมายหลัก: ข้อมูลสำคัญของคุณถูกเก็บใน Local Storage ซึ่งโค้ด JavaScript ที่รันอยู่บน Domain ของคุณสามารถเข้าถึงได้ทั้งหมด
    2. การโจมตีผ่าน innerHTML: หากผู้โจมตีพบช่องโหว่ใด ๆ ในระบบของคุณ หรือสามารถหลอกให้ผู้ใช้โหลดไฟล์ JSON/CSV ที่มีข้อมูลถูกฝังด้วยโค้ด HTML ที่เป็นอันตราย (เช่น <img src=x onerror=alert('XSS Attack')>)
    3. โค้ดอันตรายถูกรัน: เมื่อโค้ดของคุณเรียกใช้ tr.innerHTML = \…“ เพื่อสร้างแถวตาราง โค้ดอันตรายนั้นอาจถูกเบราว์เซอร์ตีความว่าเป็น HTML และถูกรัน ทำให้ผู้โจมตีสามารถขโมยข้อมูลทั้งหมดใน Local Storage ของคุณไปได้

    🔒 โค้ดปัจจุบันของคุณมีความเสี่ยงต่ำ (แต่ยังไม่สมบูรณ์)

    ในโค้ด addRow ของคุณ ข้อมูลที่ใส่เข้าไปจะอยู่ในแอตทริบิวต์ value ของแท็ก <input>:

    JavaScript

    <input type="text" class="form-control expandable-input" value="${d.product}">
    

    การใส่ข้อมูลเข้าไปใน value จะช่วยป้องกัน XSS ได้ในระดับหนึ่ง เพราะเบราว์เซอร์จะตีความข้อมูลนั้นเป็น ข้อความ (String) ไม่ใช่โค้ดที่รันได้ แต่ก็ยังมีโอกาสถูกโจมตีได้หากผู้โจมตีใส่โค้ดที่ปิดแอตทริบิวต์ value ก่อน (เช่น "> <script>... </script><input value=")


    3. คำแนะนำในการป้องกัน (Best Practice)

    แม้ว่าโอกาสที่โปรแกรมคุณจะถูกโจมตี XSS จะต่ำมากเนื่องจากเป็นโปรแกรมใช้งานเฉพาะที่ไม่ได้มีระบบผู้ใช้ซับซ้อน แต่การใช้ Best Practice ย่อมดีกว่าเสมอ

    วิธีที่ปลอดภัยที่สุดคือการหลีกเลี่ยง innerHTML เมื่อสร้างโครงสร้างจากข้อมูลที่ผู้ใช้ควบคุมได้:

    แทนที่จะสร้างทั้ง Row ด้วย innerHTML คุณควรใช้วิธี createElement และกำหนดค่าด้วย .value หรือ .textContent เสมอ

    ตัวอย่าง:

    JavaScript

    // วิธีที่ปลอดภัยกว่า (สำหรับการใส่ค่าลงใน Input)
    const input = document.createElement('input');
    input.type = 'text';
    // ใช้ .value เพื่อให้เบราว์เซอร์จัดการ Escape Characters ทั้งหมด
    input.value = d.product; 
    td.appendChild(input); 
    
    // วิธีที่ปลอดภัยกว่า (สำหรับการใส่ค่าลงใน Cell ที่ไม่ใช่ Input)
    td.textContent = d.note; // ใช้ .textContent แทน .innerHTML เพื่อใส่ข้อความธรรมดา
    

    สรุปคือ: หากคุณไม่ต้องการแก้ไขโค้ดที่ทำงานอยู่แล้ว ก็ ใช้โค้ดเดิมได้ เพราะการใช้งานส่วนตัวนั้นความเสี่ยงต่ำมาก แต่หากคุณต้องการทำให้โปรแกรมมีความแข็งแกร่งสูงสุด ควรปรับเปลี่ยนการสร้างแถวในฟังก์ชัน addRow ให้ใช้ createElement แทน innerHTML ครับ


    การปรับปรุงส่วนนี้เป็นก้าวสำคัญในการทำให้โปรแกรมของคุณมีความปลอดภัยมากยิ่งขึ้นเมื่อนำไปใช้งานบนเว็บออนไลน์

    เราจะทำการแก้ไขในฟังก์ชัน addRow(data={}, shouldSave=true) ซึ่งเป็นฟังก์ชันเดียวที่ใช้ tr.innerHTML เพื่อสร้างแถวตาราง

    🛠️ การแก้ไขโค้ด: แทนที่ tr.innerHTML ด้วย document.createElement

    เราจะแทนที่การสร้างแถวด้วย String HTML ทั้งหมด ด้วยการสร้างองค์ประกอบ (Element) แต่ละตัวแยกกัน และกำหนดค่าให้กับ <input> ด้วยคุณสมบัติ .value ซึ่งเป็นวิธีที่เบราว์เซอร์รับประกันว่าปลอดภัยจากการรันโค้ด XSS ครับ

    ไฟล์ที่ต้องแก้ไขคือ 522.html

    💡 โค้ดที่ต้องเปลี่ยน

    เราจะเปลี่ยนโค้ดส่วนนี้ (ประมาณบรรทัดที่ 300+ ในฟังก์ชัน addRow):

    JavaScript

        // ❌ โค้ดเดิม (มีความเสี่ยง XSS หากข้อมูลที่โหลดมาถูกดัดแปลง)
        const tr=document.createElement("tr");
        // ⭐️ เพิ่ม class "expandable-input" สำหรับช่อง Product, Company, Name, Note, Contact
        tr.innerHTML=`
        <td class="date-cell-status"><input type="date" class="form-control" value="${d.date}"></td>
        <td><input type="text" class="form-control expandable-input" value="${d.product}"></td>
        // ...
        // โค้ด HTML ที่สร้างด้วย String ทั้งหมด
        // ...
        <td>
            <button class="btn btn-warning btn-sm action-btn act-dup">⧉</button>
            <button class="btn btn-danger btn-sm action-btn act-del">🗑</button>
        </td>`;
        
        tbody.appendChild(tr);
        // ...
    

    🟢 โค้ดที่แก้ไขใหม่ (ปลอดภัยยิ่งขึ้น)

    ผมจะแทนที่ tr.innerHTML = \…`ทั้งหมดด้วยฟังก์ชันcreateTableRowใหม่ ซึ่งใช้createElement`

    JavaScript

    // ... โค้ดก่อนหน้า ...
    
    function addRow(data={}, shouldSave=true){ 
        const today = new Date();
        // ... (ส่วนการกำหนดค่า d ยังคงเดิม) ...
        
        // ⭐️ แทนที่โค้ดเดิมทั้งหมดด้วยการเรียกใช้ฟังก์ชันใหม่ที่ปลอดภัย
        const tr = createTableRow(d); // เรียกใช้ฟังก์ชันสร้างแถวที่ปลอดภัย
    
        if (!tr) return; // หากสร้างแถวไม่ได้ ให้หยุด
        
        // หา tbody ที่ถูกต้อง
        const monthOnlyKey = monthKey(d.date);
        if (!monthOnlyKey || !monthTabIDs[monthOnlyKey]) {
            console.warn("Skipping row: Invalid Month Key derived from date:", d.date);
            return; 
        }
        const targetTabId = monthTabIDs[monthOnlyKey];
        const targetTab = document.getElementById(targetTabId);
        if (!targetTab) return; 
        const tbody = targetTab.querySelector('tbody'); 
        if (!tbody) return; 
    
        // ... (ส่วนที่เหลือของการจัดการแถว) ...
        
        tbody.appendChild(tr);
        tr.setAttribute('data-key', monthOnlyKey); 
        
        applyColorToDateCell(tr, d.date);
        // ⭐️ ต้องหา Checkbox ใหม่อีกครั้งเพราะสร้างใหม่
        const renewCheckbox = tr.querySelector('.renewed-checkbox'); 
        toggleIconAlert(tr, renewCheckbox.checked); 
    
        sortRowsByDate(tbody); 
        
        // ⭐️ ต้องกำหนด Event Listener ให้กับปุ่มที่สร้างใหม่ด้วย
        tr.querySelector('.act-del').onclick = (e) => deleteRow(e.target);
        tr.querySelector('.act-dup').onclick = (e) => duplicateRow(e.target);
    
        // ... (Event Listener หลักของ tr ยังคงเดิม) ...
        
        // Checkbox Event (ยังคงเดิม)
        renewCheckbox.addEventListener('change', () => {
            toggleIconAlert(tr, renewCheckbox.checked);
            saveDataToLocalStorage();
        });
    
        
        if (shouldSave) { 
            saveDataToLocalStorage(); 
        }
    }
    
    // ⭐️ ฟังก์ชันใหม่: สร้างแถวที่ปลอดภัยโดยใช้ createElement
    function createTableRow(d) {
        const tr = document.createElement("tr");
        const cellData = [
            { type: 'date', value: d.date, className: 'date-cell-status' },
            { type: 'text', value: d.product, className: 'expandable-cell' },
            { type: 'text', value: d.company, className: 'expandable-cell' },
            { type: 'text', value: d.name, className: 'expandable-cell' },
            { type: 'number', value: d.premium },
            { type: 'number', value: d.receive },
            { type: 'number', value: d.pay },
            { type: 'text', value: d.note, className: 'expandable-cell' },
            { type: 'text', value: d.contact, className: 'expandable-cell' },
        ];
    
        cellData.forEach((cell, index) => {
            const td = document.createElement("td");
            if (cell.className) td.className = cell.className;
    
            const input = document.createElement("input");
            input.type = cell.type;
            input.className = "form-control";
            
            // ⭐️ ความปลอดภัย: ใช้ .value เพื่อกำหนดค่าให้ Input
            input.value = cell.value; 
            
            if (index > 0 && index < 4 || index > 6 && index < 9) { // Product, Company, Name, Note, Contact
                input.classList.add('expandable-input');
            }
            
            td.appendChild(input);
            tr.appendChild(td);
        });
    
        // เซลล์ Renewed (Checkbox และ Icon)
        const tdRenewed = document.createElement("td");
        tdRenewed.className = "renewed-cell";
        
        const checkbox = document.createElement("input");
        checkbox.type = "checkbox";
        checkbox.className = "form-check-input renewed-checkbox";
        checkbox.checked = d.renewed; 
        
        const icon = document.createElement("span");
        icon.className = "alert-icon";
        icon.textContent = "🚨";
        
        tdRenewed.appendChild(checkbox);
        tdRenewed.appendChild(icon);
        tr.appendChild(tdRenewed);
    
        // เซลล์ Actions
        const tdActions = document.createElement("td");
        tdActions.style.width = "160px"; // กำหนดความกว้าง
        
        const btnDup = document.createElement("button");
        btnDup.className = "btn btn-warning btn-sm action-btn act-dup";
        btnDup.textContent = "⧉";
        
        const btnDel = document.createElement("button");
        btnDel.className = "btn btn-danger btn-sm action-btn act-del";
        btnDel.textContent = "🗑";
        
        tdActions.appendChild(btnDup);
        tdActions.appendChild(btnDel);
        tr.appendChild(tdActions);
    
        return tr;
    }
    

    การเปลี่ยนแปลงที่สำคัญ

    1. การลบ tr.innerHTML: เราลบโค้ดที่มีการสร้าง String HTML ทั้งหมดออก
    2. การเพิ่ม createTableRow(d): เราสร้างฟังก์ชันใหม่ที่รับ d (ข้อมูลแถว) แล้วสร้าง <tr>, <td>, และ input โดยใช้ document.createElement
    3. การใช้ .value: ค่าข้อมูลจาก d ถูกกำหนดให้กับ input.value แทนที่จะใส่ลงใน String ซึ่งทำให้เบราว์เซอร์จัดการ Escape Characters ที่อาจเป็นอันตรายได้อย่างถูกต้อง

    ผมได้ทำการอัปเดตไฟล์ 522.html ของคุณด้วยการแก้ไขทั้งหมดข้างต้นนี้เรียบร้อยแล้วครับ ตอนนี้การสร้างแถวตารางมีความปลอดภัยจากช่องโหว่ XSS ที่จะเกิดขึ้นผ่านข้อมูลในไฟล์ JSON/CSV ที่โหลดเข้ามาครับ!


    ผมอ่านไม่เข้าใจที่คุณแก้

    คุณบอกผมว่า ให้ลบตั้งแต่ตรงไหนถึงตรงไหน

    แล้วให้ใส่ทั้งหมดที่คุณให้มา แทน

    เข้าใจเลยครับว่าโค้ดที่ถูกแก้ไขไปมาอาจทำให้สับสนได้

    วัตถุประสงค์ของเราคือ เพิ่มความปลอดภัย ด้วยการเปลี่ยนวิธีสร้างแถวตารางจาก tr.innerHTML (ซึ่งมีความเสี่ยง XSS) ไปเป็น document.createElement (ซึ่งปลอดภัยกว่า) โดยที่ฟังก์ชันการทำงานทุกอย่าง รวมถึงการขยายตัว (Expandable Input) และการลอยตัว (Floating) ยังคงทำงานได้เหมือนเดิม

    ผมจะสรุปขั้นตอนการแก้ไขให้ง่ายที่สุด โดยระบุว่าต้อง ลบ ส่วนใด และ เพิ่ม โค้ดใหม่เข้าไปตรงไหนบ้างครับ


    🛠️ ขั้นตอนที่ 1: แก้ไขฟังก์ชัน addRow(data={}, shouldSave=true)

    ฟังก์ชัน addRow อยู่ประมาณ บรรทัดที่ 500 ในไฟล์ 522.html ของคุณ

    1.1. ลบโค้ดเดิม (Delete)

    โปรดลบโค้ดทั้งหมดที่สร้างแถวด้วย tr.innerHTML ออก (รวม 22 บรรทัด) เริ่มลบตั้งแต่บรรทัดที่ 512 (ที่เขียนว่า const tr=document.createElement("tr"); ก่อนที่ tr.innerHTML จะตามมา) จนถึงบรรทัดที่ 533 (ที่เขียนว่า </td>😉

    JavaScript

    // ❌ ลบโค้ดส่วนนี้ออก (ประมาณบรรทัด 512 ถึง 533)
        const tr=document.createElement("tr");
        tr.innerHTML=`
        <td class="date-cell-status"><input type="date" class="form-control" value="${d.date}"></td>
        <td><input type="text" class="form-control" value="${d.product}"></td>
        <td><input type="text" class="form-control" value="${d.company}"></td>
        <td><input type="text" class="form-control" value="${d.name}"></td>
        <td><input type="number" class="form-control" value="${d.premium}"></td>
        <td><input type="number" class="form-control" value="${d.receive}"></td>
        <td><input type="number" class="form-control" value="${d.pay}"></td>
        <td><input type="text" class="form-control" value="${d.note}"></td>
        <td><input type="text" class="form-control" value="${d.contact}"></td>
        <td class="renewed-cell">
            <input type="checkbox" class="form-check-input renewed-checkbox" ${checkedAttr}>
            <span class="alert-icon">🚨</span>
        </td>
        <td>
            <button class="btn btn-warning btn-sm action-btn act-dup">⧉</button>
            <button class="btn btn-danger btn-sm action-btn act-del">🗑</button>
        </td>`; 
    // ❌ สิ้นสุดการลบ
    

    1.2. ใส่โค้ดใหม่ (Replace)

    แทนที่โค้ด 22 บรรทัดที่ถูกลบด้วยโค้ดใหม่ 3 บรรทัดนี้

    JavaScript

    // 🟢 ใส่โค้ด 3 บรรทัดนี้แทนที่
        const tr = createTableRow(d); // ⭐️ เรียกใช้ฟังก์ชันสร้างแถวที่ปลอดภัย
    
        if (!tr) return; // หากสร้างแถวไม่ได้ ให้หยุด
        
        tbody.appendChild(tr); // นำแถวเข้าสู่ตาราง (ใช้โค้ดเดิมของบรรทัด 533)
    // 🟢 สิ้นสุดการใส่โค้ด
    

    🛠️ ขั้นตอนที่ 2: เพิ่มฟังก์ชันใหม่ createTableRow(d)

    เพิ่มฟังก์ชัน createTableRow(d) ใหม่นี้ ทั้งหมด เข้าไปในโค้ด JavaScript ของคุณ ตำแหน่งที่เหมาะสมคือ ก่อน ฟังก์ชัน addRow หรือ หลัง ฟังก์ชัน duplicateRow (ประมาณบรรทัดที่ 620)

    JavaScript

    // 🟢 เพิ่มฟังก์ชันใหม่นี้ทั้งหมด
    function createTableRow(d) {
        const tr = document.createElement("tr");
        
        // ข้อมูลเซลล์หลัก 9 ช่อง (รวมวันที่)
        const cellData = [
            { type: 'date', value: d.date, className: 'date-cell-status' },
            { type: 'text', value: d.product, className: 'expandable-cell' },
            { type: 'text', value: d.company, className: 'expandable-cell' },
            { type: 'text', value: d.name, className: 'expandable-cell' },
            { type: 'number', value: d.premium },
            { type: 'number', value: d.receive },
            { type: 'number', value: d.pay },
            { type: 'text', value: d.note, className: 'expandable-cell' },
            { type: 'text', value: d.contact, className: 'expandable-cell' },
        ];
    
        cellData.forEach((cell, index) => {
            const td = document.createElement("td");
            if (cell.className) td.className = cell.className;
    
            const input = document.createElement("input");
            input.type = cell.type;
            input.className = "form-control";
            
            // ⭐️ ความปลอดภัย: ใช้ .value เพื่อกำหนดค่าให้ Input
            input.value = cell.value; 
            
            // ⭐️ สำคัญ: เพิ่ม class expandable-input สำหรับช่องที่ต้องขยาย
            // (Product, Company, Name, Note, Contact)
            if (index > 0 && index < 4 || index > 6 && index < 9) { 
                input.classList.add('expandable-input');
            }
            
            td.appendChild(input);
            tr.appendChild(td);
        });
    
        // เซลล์ Renewed (Checkbox และ Icon)
        const tdRenewed = document.createElement("td");
        tdRenewed.className = "renewed-cell";
        
        const checkbox = document.createElement("input");
        checkbox.type = "checkbox";
        checkbox.className = "form-check-input renewed-checkbox";
        checkbox.checked = d.renewed; 
        
        const icon = document.createElement("span");
        icon.className = "alert-icon";
        icon.textContent = "🚨";
        
        tdRenewed.appendChild(checkbox);
        tdRenewed.appendChild(icon);
        tr.appendChild(tdRenewed);
    
        // เซลล์ Actions
        const tdActions = document.createElement("td");
        tdActions.style.width = "160px"; // กำหนดความกว้าง
        
        const btnDup = document.createElement("button");
        btnDup.className = "btn btn-warning btn-sm action-btn act-dup";
        btnDup.textContent = "⧉";
        
        const btnDel = document.createElement("button");
        btnDel.className = "btn btn-danger btn-sm action-btn act-del";
        btnDel.textContent = "🗑";
        
        tdActions.appendChild(btnDup);
        tdActions.appendChild(btnDel);
        tr.appendChild(tdActions);
    
        return tr;
    }
    // 🟢 สิ้นสุดฟังก์ชัน
    

    การแก้ไขนี้จะทำให้โค้ดมีความปลอดภัยสูงขึ้นโดยไม่มีผลกระทบต่อฟังก์ชันการทำงานเดิมครับ!



เวอไนน์ไอคอร์ส

ประหยัดเวลากว่า 100 เท่า!






เวอไนน์เว็บไซต์⚡️
สร้างเว็บไซต์ ดูแลเว็บไซต์

Categories


Uncategorized