Skip to main content

จัดการหน้าเนื้อหา (Content Page)

ภาพรวม

Content Management คือโมดูลสำหรับสร้างและแก้ไข "หน้าเนื้อหา" (content page) ของแต่ละ LINE OA เนื้อหาถูกเก็บในรูปแบบ HTML ที่แก้ไขผ่าน Rich Text Editor (TipTap) และรองรับสองภาษาในหน้าเดียวกัน (ไทย/อังกฤษ) โดยเก็บเป็นชุดคำแปลชุดเดียวต่อหนึ่งรายการ

หน้าจอนี้เหมาะกับทีมการตลาดหรือทีมคอนเทนต์ที่ต้องการเผยแพร่บทความ หน้าโปรโมชัน หรือหน้าข้อมูลบริการให้ผู้ใช้เปิดผ่านลิงก์หรือ QR code จาก LINE OA โดยไม่ต้องพึ่งการพัฒนาเว็บเพิ่ม

หนึ่งหน้าเนื้อหาประกอบด้วยข้อมูลดังนี้

กลุ่มข้อมูลรายละเอียด
ข้อมูลหลักslug สำหรับประกอบ URL และสถานะของหน้า (draft / published / archived)
เนื้อหาแยกภาษาชื่อเรื่อง เนื้อหา HTML คำโปรย และชุด meta/OG tag ของแต่ละภาษา
การจัดหมวดหมวดหมู่หลักและหมวดหมู่ย่อย
การควบคุมการเข้าถึงรายการ Audience ที่อนุญาต และการตั้งรหัสผ่านสำหรับเปิดหน้า
Friend Trackแคมเปญเพิ่มเพื่อนที่ผูกไว้ พร้อมข้อความบนปุ่ม CTA
Campaign Settingsค่า UTM สำหรับติดตามแหล่งที่มา และการตั้งค่า Analytics (GA, Facebook Pixel, LINE Tag, custom script)

สถานะของหน้าเนื้อหามีสามค่า

สถานะความหมาย
ฉบับร่าง (draft)บันทึกไว้แล้วแต่ยังไม่เปิดให้ผู้ใช้ทั่วไปเข้าชม ใช้สำหรับงานที่ยังเขียนไม่เสร็จ
เผยแพร่ (published)เปิดให้เข้าชมผ่านลิงก์สาธารณะหรือ QR code ได้จริง
เก็บถาวร (archived)ปิดการเข้าชมแล้วแต่ยังเก็บข้อมูลไว้ในระบบเพื่ออ้างอิงย้อนหลัง

จุดสำคัญที่ต้องทราบ

  • หลังบันทึก ระบบหลังบ้านจะออก publicToken ให้แต่ละหน้า เพื่อใช้ประกอบลิงก์เปิดหน้าแบบสาธารณะฝั่ง client-web
  • ตารางรายการแสดงตัวนับยอดเข้าชมรวมและยอดผู้เข้าชมไม่ซ้ำของแต่ละหน้า จึงใช้เทียบผลตอบรับของแต่ละหน้าได้ทันทีโดยไม่ต้องเปิดเครื่องมือวิเคราะห์ภายนอก
  • การจำกัด Audience และการผูก Friend Track campaign ใช้ร่วมกันไม่ได้ เมื่อตั้งค่าฝั่งใดฝั่งหนึ่ง อีกฝั่งจะถูกปิดการใช้งานทันที เพราะหน้าที่จำกัดผู้ชมอยู่แล้วไม่จำเป็นต้องมีปุ่มชวนเพิ่มเพื่อน
  • การเปลี่ยนสถานะเผยแพร่ทำผ่านปุ่มบันทึกในฟอร์ม (บันทึกเป็นฉบับร่าง หรือเผยแพร่) ไม่มีปุ่มเปลี่ยนสถานะแยกในหน้ารายการ
  • คำแปลทั้งสองภาษาอยู่ในรายการเดียวกัน การแก้ไขหน้าเนื้อหาจึงเป็นการแก้ทั้งสองภาษาพร้อมกัน ไม่ได้แยกเป็นคนละรายการ

Business Flow

การดูรายการหน้าเนื้อหา

  1. เปิดหน้ารายการที่ /content-management ระบบดึงข้อมูลแบบแบ่งหน้าพร้อมเงื่อนไขค้นหาและตัวกรองสถานะ (ทั้งหมด / เผยแพร่แล้ว / ฉบับร่าง / เก็บถาวร)
  2. ระบบอ่านข้อมูล LINE OA ปัจจุบันเพื่อนำ LIFF ID และรหัส hash ของ OA มาประกอบเป็น URL สาธารณะสำหรับปุ่มคัดลอกลิงก์และปุ่ม QR code
  3. ปุ่มคัดลอกลิงก์เลือกรูปแบบ URL ตามการตั้งค่าของหน้า หากหน้านั้นจำกัด Audience และ LINE OA มี LIFF ID ระบบจะสร้างลิงก์ในรูปแบบ LIFF (https://liff.line.me/<liffId>/<lineOaHash>/content/<slug>) หากไม่ได้จำกัด Audience จะใช้ลิงก์เว็บปกติ (<BASE_APP_URL>/<lineOaHash>/content/<slug>) กรณีที่หน้ายังไม่มี slug ระบบจะใช้ publicToken แทน
  4. ปุ่ม QR code เปิดกล่องข้อความแสดง QR ของ URL เดียวกัน พร้อมให้ดาวน์โหลดเป็นไฟล์ PNG
  5. การลบต้องยืนยันผ่านกล่องข้อความก่อน เมื่อสำเร็จระบบจะดึงข้อมูลตารางใหม่อัตโนมัติ

การสร้างหน้าเนื้อหา

  1. กดปุ่มสร้างเพื่อเข้าสู่หน้าฟอร์มหลายภาษา (/content-management/create-multilang) หน้าจอแบ่งเป็นสองคอลัมน์ ซ้ายเป็นส่วนแก้ไข ขวาเป็นตัวอย่างแบบเรียลไทม์
  2. แผงตัวอย่างปรับขนาดจอจำลองได้สามแบบ (เดสก์ท็อปเต็มความกว้าง แท็บเล็ต 768px และมือถือ 375px) และสลับภาษาที่แสดงตามแท็บภาษาที่กำลังแก้ไขอยู่
  3. แท็บ Content — กรอกชื่อเรื่องแยกตามภาษา ระบบสร้าง slug ให้อัตโนมัติจากชื่อเรื่อง (แก้ไขเองได้) จากนั้นเขียนเนื้อหาด้วย Rich Text Editor และเปิดส่วน SEO เพื่อกรอก meta title, meta description, คำโปรย รวมถึง OG title, OG description และรูป OG
  4. เลือกหมวดหมู่จากรายการหมวดหมู่หลัก เมื่อเลือกแล้วระบบจะโหลดหมวดหมู่ย่อยแบบมีลำดับชั้นของหมวดนั้นมาให้เลือกต่อ
  5. แท็บ Access Control — เลือก Audience ได้หลายรายการเพื่อจำกัดผู้เข้าชม และ/หรือเปิดการป้องกันด้วยรหัสผ่านพร้อมกำหนดรหัส
  6. แท็บ Friends Track — เลือกแคมเปญจากรายการแคมเปญที่กำลังใช้งานอยู่ และตั้งข้อความบนปุ่ม CTA พร้อมดูตัวอย่างปุ่มสไตล์ LINE
  7. แท็บ Campaign Settings — กรอกค่า UTM โดย utm source และ utm medium เลือกจากค่าที่เตรียมไว้หรือพิมพ์เองได้ ส่วน campaign, term และ content เป็นช่องกรอกอิสระ จากนั้นตั้งค่า Analytics ได้แก่ การเปิดใช้ Google Analytics พร้อมรายการ event, event ของ Facebook Pixel, event ของ LINE Tag และสคริปต์เพิ่มเติมที่จะแทรกในส่วนหัวของหน้า ระบบแสดงตัวอย่าง URL สุดท้ายที่จะได้แยกตามภาษา
  8. กดบันทึกเป็นฉบับร่างหรือเผยแพร่ ระบบตรวจสอบว่ามีอย่างน้อยหนึ่งภาษาที่กรอกชื่อเรื่องแล้วและมี slug จากนั้นจึงสร้างรายการและพากลับสู่หน้ารายการ

การแก้ไขหน้าเนื้อหา

  1. เข้าหน้าแก้ไขที่ /content-management/edit พร้อมระบุรหัสหน้าเนื้อหาใน query string
  2. ระบบโหลดข้อมูลเดิมและแปลงกลับเป็นโครงสร้างฟอร์มทั้งหมด ทั้งคำแปลสองภาษา หมวดหมู่ Audience Friend Track การป้องกันด้วยรหัสผ่าน UTM และ Analytics
  3. บันทึกการแก้ไขได้เช่นเดียวกับตอนสร้าง โดยสถานะจะถูกส่งไปเปลี่ยนเฉพาะเมื่อผู้ใช้กดปุ่มที่ระบุสถานะชัดเจน หากกดบันทึกเฉย ๆ สถานะเดิมของหน้าจะไม่ถูกแตะต้อง
  4. หน้าที่เผยแพร่แล้วสามารถแก้ไขเนื้อหาแล้วบันทึกทับได้ทันที ผู้ใช้ที่เปิดลิงก์เดิมจะเห็นเนื้อหาใหม่โดยที่ลิงก์และ QR code ไม่เปลี่ยน

การเผยแพร่และการติดตามผล

  1. เมื่อหน้าเนื้อหาอยู่ในสถานะเผยแพร่ ให้คัดลอกลิงก์หรือดาวน์โหลด QR code จากหน้ารายการไปใช้ในช่องทางที่ต้องการ เช่น Rich Menu ข้อความบรอดแคสต์ หรือสื่อออฟไลน์
  2. หากตั้งค่า UTM ไว้ ลิงก์ที่ระบบสร้างจะแนบพารามิเตอร์ติดตามให้อัตโนมัติ ทำให้แยกแหล่งที่มาของผู้เข้าชมได้ในเครื่องมือวิเคราะห์
  3. เมื่อผู้ใช้เปิดหน้า สคริปต์ Analytics ที่ตั้งค่าไว้จะถูกฝังในหน้าและเริ่มเก็บข้อมูลตาม event ที่กำหนด ขณะเดียวกันระบบก็บันทึกยอดเข้าชมของหน้าเพื่อนำมาแสดงในตารางรายการ
  4. หากหน้าเปิดการป้องกันด้วยรหัสผ่าน ผู้ใช้ต้องกรอกรหัสให้ถูกต้องก่อนจึงจะเห็นเนื้อหา และหากจำกัด Audience ไว้ ระบบจะตรวจสิทธิ์ผู้ใช้จากบริบทของ LINE OA ก่อนแสดงผล

หน้าจอและองค์ประกอบหลัก

หน้ารายการ (/content-management)

  • ส่วนกรองข้อมูล — ช่องค้นหาและตัวเลือกสถานะ พร้อมการแบ่งหน้า
  • ตารางรายการ — แสดงชื่อเรื่อง slug หมวดหมู่ สถานะ ยอดเข้าชม และวันที่ปรับปรุงล่าสุด
  • ปุ่มคัดลอกลิงก์และ QR code — สร้าง URL สาธารณะตามรูปแบบที่เหมาะกับการตั้งค่าของหน้า และแสดง QR ให้ดาวน์โหลดได้
  • ปุ่มลบ — ยืนยันก่อนลบด้วย Popconfirm

รูปแบบ URL สาธารณะที่ระบบสร้างให้มีสองแบบ

เงื่อนไขของหน้ารูปแบบลิงก์
จำกัด Audience และ LINE OA มี LIFF IDhttps://liff.line.me/<liffId>/<lineOaHash>/content/<slug>
ไม่จำกัด Audience หรือไม่มี LIFF ID<BASE_APP_URL>/<lineOaHash>/content/<slug>

ทั้งสองรูปแบบจะใช้ publicToken แทนตำแหน่งของ slug เมื่อหน้านั้นยังไม่ได้กำหนด slug ไว้

ไฟล์อ้างอิงหลัก: src/app/content-management/page.tsx

หน้าสร้าง (/content-management/create-multilang) และหน้าแก้ไข (/content-management/edit)

  • ฟอร์มหลายภาษา (src/components/content-management/MultiLanguageForm.tsx) — แท็บภาษาไทย/อังกฤษ ครอบคลุมชื่อเรื่อง slug เนื้อหา และส่วน SEO แบบพับเก็บได้
  • Rich Text Editor (src/components/content-management/TiptapEditorSimple.tsx) — เครื่องมือเขียนเนื้อหาหลักของโมดูล ครอบคลุมความสามารถต่อไปนี้
    • จัดรูปแบบข้อความพื้นฐาน เช่น หัวข้อ ตัวหนา ตัวเอียง รายการลำดับ และ blockquote
    • แทรกลิงก์ จัดวางตำแหน่งข้อความ ไฮไลต์ข้อความ กำหนดระยะห่างบรรทัด และกำหนดสีพื้นหลังข้อความ
    • อัปโหลดรูปเข้าเนื้อหาโดยตรง รองรับทั้งการเลือกไฟล์และการลากไฟล์มาวางในพื้นที่แก้ไข
    • ปรับขนาดรูปที่แทรกแล้วได้จากในเนื้อหา โดยผลลัพธ์ที่บันทึกคือ HTML ที่นำไปแสดงบนหน้าสาธารณะได้ทันที
  • ตัวแสดงผลเนื้อหา (src/components/content-management/TiptapViewer.tsx) — แสดง HTML แบบอ่านอย่างเดียว ใช้ทั้งในแผงตัวอย่างและหน้าสาธารณะ
  • ตัวเลือกหมวดหมู่ (src/components/content-management/CategoryCascader.tsx) — Cascader ที่โหลดหมวดหมู่หลักก่อน แล้วจึงโหลดหมวดหมู่ย่อยแบบลำดับชั้นเมื่อผู้ใช้ขยายรายการ รองรับลึกสุด 4 ระดับ
  • แผงตัวอย่างเรียลไทม์ — แสดงผลหน้าเนื้อหาตามขนาดจอที่เลือกและตามภาษาที่กำลังแก้ไข ช่วยตรวจการจัดวางบนมือถือก่อนเผยแพร่จริง

แท็บทั้งสี่ในคอลัมน์ซ้ายแบ่งหน้าที่กันดังนี้

แท็บใช้ตั้งค่าอะไร
Contentชื่อเรื่อง slug เนื้อหา และข้อมูล SEO ของแต่ละภาษา รวมถึงการเลือกหมวดหมู่
Access Controlรายการ Audience ที่เข้าชมได้ และการป้องกันด้วยรหัสผ่าน
Friends Trackแคมเปญเพิ่มเพื่อนและข้อความบนปุ่ม CTA
Campaign Settingsค่า UTM และการตั้งค่า Analytics พร้อมตัวอย่าง URL ที่จะได้

บริการฝั่ง API

รวมอยู่ที่ src/services/content-page.service.ts ภายใต้ path หลัก content-pages

ความสามารถEndpoint
ดึงรายการหน้าเนื้อหาGET /content-pages
ดึงข้อมูลรายหน้าGET /content-pages/{id}
สร้างหน้าเนื้อหาPOST /content-pages
แก้ไขหน้าเนื้อหาPATCH /content-pages/{id}
ลบหน้าเนื้อหาDELETE /content-pages/{id}
อัปโหลดรูปเข้าเนื้อหาPOST /content-pages/upload-image

นอกจากนี้ service ยังเตรียมคำสั่งเสริมไว้รองรับการใช้งานในอนาคต ได้แก่ การเผยแพร่/ยกเลิกเผยแพร่/เก็บถาวร การทำสำเนาหน้า การปรับ UTM และ Analytics แยกจากการบันทึกหลัก การสร้าง publicToken ใหม่ รวมถึงการดึงเนื้อหาสาธารณะและบันทึกยอดเข้าชม โดยหน้าจอปัจจุบันยังไม่ได้เรียกใช้คำสั่งกลุ่มนี้

จุดเชื่อมต่อกับฟีเจอร์อื่น

  • สิทธิ์การใช้งาน — เมนูแม่ควบคุมด้วยสิทธิ์โมดูล content-management และเมนูย่อยใช้สิทธิ์ content-page ทั้งสองถูกปลดล็อกจากโมดูล line-oa ฝั่งหลังบ้าน
  • Content Category / Subcategory — เป็นแหล่งข้อมูลของตัวเลือกหมวดหมู่และหมวดหมู่ย่อยในฟอร์ม
  • Audience — ใช้กำหนดกลุ่มผู้ที่เปิดหน้าเนื้อหาได้ และเป็นตัวกำหนดว่าลิงก์สาธารณะจะออกมาในรูปแบบ LIFF หรือเว็บปกติ
  • Friend Track — เป็นแหล่งของแคมเปญที่นำมาผูกกับปุ่ม CTA เพิ่มเพื่อนบนหน้าเนื้อหา
  • LINE OA Management — ให้ค่า LIFF ID และรหัส hash ของ OA ที่ใช้ประกอบลิงก์สาธารณะและ QR code
  • Content Links — นำหน้าเนื้อหามารวมเป็นรายการลิงก์ตามเงื่อนไขที่กำหนด
  • Menu Builder — มีตัวเลือกหน้าเนื้อหาที่เผยแพร่แล้วให้ผูกกับเมนู โดยอ้างอิงผ่าน token ของหน้า
  • หน้าสาธารณะฝั่ง client-web — เป็นปลายทางจริงที่ผู้ใช้เปิดจากลิงก์หรือ QR code และเป็นจุดที่สคริปต์ Analytics ทำงาน
  • โครงสร้างพื้นฐานร่วม — ใช้ HTTP client กลางของ CMS ที่แนบ token ให้อัตโนมัติและออกจากระบบเมื่อ token หมดอายุ รวมถึงระบบ breadcrumb เมนูด้านข้าง และชุดคำแปลหน้าจอเช่นเดียวกับโมดูลอื่น

รายละเอียดฝั่ง Backend (CMS API)

โค้ดฝั่ง backend อยู่ที่ internal/modules/contentpage/ โมดูลนี้แบ่งเส้นทางออกเป็นสองกลุ่มชัดเจน คือ กลุ่มผู้ดูแล ที่ต้องล็อกอิน และ กลุ่มสาธารณะ ที่ผู้อ่านทั่วไปเรียกได้โดยไม่ต้องล็อกอิน

สิทธิ์ที่ต้องมี

  • route ฝั่งผู้ดูแลต้องผ่านการยืนยันตัวตนกลาง (JWT) เท่านั้น ไม่ได้ครอบด้วย module gate และ policy ที่ประกาศไว้อ้างถึงโมดูล line-oa ซึ่งยังเป็นเพียงข้อมูลกำกับที่ยังไม่บังคับใช้
  • ผลคือการควบคุมสิทธิ์รายเมนูของหน้านี้เกิดขึ้นที่ฝั่งหน้าจอเป็นหลัก ผู้ใช้ที่ล็อกอินแล้วยังเรียก endpoint จัดการเนื้อหาได้โดยตรง
  • route สาธารณะไม่มี guard ใด ๆ เลย ตามเจตนา เพราะเป็นเส้นทางที่ผู้อ่านทั่วไปต้องเข้าถึงได้

สิ่งที่ backend สร้างให้และวิธีป้องกันหน้า

  • ตอนสร้างหน้าใหม่ backend เป็นผู้ สร้าง public token ด้วยตัวสร้างค่าสุ่มเชิงเข้ารหัสขนาด 32 ไบต์ ค่านี้จึงเดาไม่ได้ และเป็นสิ่งที่ทำให้ลิงก์สาธารณะปลอดภัยพอที่จะแจกออกไปได้
  • หน้าใหม่ถูกตั้งสถานะเป็นฉบับร่างเสมอ ไม่ได้เผยแพร่ทันทีที่บันทึก
  • รหัสผ่านของหน้าถูกเข้ารหัสด้วย bcrypt ก่อนบันทึก ไม่ได้เก็บเป็นข้อความธรรมดา และการเปิดหน้าที่ล็อกไว้จะเทียบรหัสผ่านด้วยกลไกเดียวกัน
  • endpoint อัปโหลดรูปเข้าเนื้อหาตรวจชนิดของไฟล์จากส่วนหัวของคำขอก่อนรับไฟล์

สิ่งที่บันทึกและผลข้างเคียง (Side Effect)

  • ข้อมูลกระจายอยู่หลายตาราง: content_page (ตัวหน้า), content_page_translation (คำแปลแยกภาษา), content_page_view และ content_page_analytics (ข้อมูลการเข้าชม) และ content_page_utm (ค่าติดตามแหล่งที่มา) โดยอ้างอิงกับ content_category และ content_subcategory
  • การบันทึกยอดเข้าชมเกิดผ่าน endpoint สาธารณะแยกต่างหาก ที่หน้าเว็บฝั่งผู้อ่านเป็นผู้เรียก ไม่ใช่ผลข้างเคียงของการเปิดหน้า จึงเพิ่มทั้งตัวนับและเก็บเหตุการณ์ไว้สำหรับสรุปสถิติ
  • รูปประกอบเนื้อหาถูกเก็บบน object storage ส่วนตัวเนื้อหาที่บันทึกคือ HTML ที่นำไปแสดงได้ทันที
  • โมดูลนี้ไม่ได้ส่งงานเข้าคิวและไม่ได้เขียน cache การแก้เนื้อหาจึงมีผลกับหน้าสาธารณะทันที

ความสามารถฝั่ง backend ที่หน้าจอยังไม่ได้เรียกใช้

ฝั่ง API รองรับการทำงานมากกว่าที่หน้าจอปัจจุบันใช้อยู่ ได้แก่

  • POST /api/content-pages/:id/publish และ POST /api/content-pages/:id/archive สำหรับเปลี่ยนสถานะแยกจากการบันทึกเนื้อหา
  • POST /api/content-pages/:id/duplicate สำหรับคัดลอกหน้าทั้งชุดรวมรูปประกอบ
  • GET /api/content-pages/:id/analytics สำหรับดูสถิติการเข้าชมเชิงลึกรายหน้า ซึ่งละเอียดกว่าตัวนับที่แสดงในตารางรายการ

พฤติกรรมของเส้นทางสาธารณะ

  • GET /api/public/content-pages/:token เป็นเส้นทางที่ผู้อ่านใช้เปิดหน้า โดยหน้าที่ล็อกรหัสต้องส่งรหัสผ่านมาด้วย
  • กรณีที่เปิดไม่ได้ทั้งหมดถูกยุบเป็นข้อผิดพลาดเดียวกันว่า "ไม่พบเนื้อหา" ไม่ว่าจะเป็น token ผิด หน้ายังไม่เผยแพร่ หรือรหัสผ่านไม่ถูกต้อง ซึ่งเป็นพฤติกรรมที่ดีต่อความปลอดภัย เพราะผู้ที่สุ่ม token ไม่สามารถแยกได้ว่ามีหน้านั้นอยู่จริงหรือไม่
  • ข้อสังเกตด้านความปลอดภัย — รหัสผ่านของหน้าถูกส่งมาทาง query string ซึ่งมีโอกาสไปปรากฏใน log ของเซิร์ฟเวอร์ พร็อกซี และประวัติเบราว์เซอร์ จึงไม่ควรใช้รหัสผ่านที่ผู้ใช้ใช้ซ้ำกับที่อื่น
  • ระบบยังมีเส้นทางสาธารณะอีกชุดหนึ่งที่ทำงานคล้ายกันในโมดูล public API ซึ่งรองรับการเปิดผ่าน LIFF เพิ่มเติม การเปลี่ยนพฤติกรรมของหน้าสาธารณะจึงต้องพิจารณาทั้งสองเส้นทาง

Edge Case และข้อสังเกตที่ควรรู้

  • ตาราง content_page ใช้คอลัมน์ประทับเวลาลบแบบธรรมดา ไม่ใช่กลไก soft delete อัตโนมัติของ ORM ทุก query อ่านข้อมูลต้องเขียนเงื่อนไข "ยังไม่ถูกลบ" เอง หากลืม หน้าที่ลบไปแล้วจะโผล่กลับมาในผลลัพธ์ รวมถึงในเส้นทางสาธารณะด้วย
  • คำแปลสองภาษาเก็บแยกอยู่ในตารางคำแปล ไม่ได้อยู่ในแถวเดียวกับตัวหน้า การเพิ่มภาษาที่สามในอนาคตจึงไม่ต้องเปลี่ยนโครงสร้างหลัก
  • เพราะ public token ไม่เปลี่ยนเมื่อแก้เนื้อหา ลิงก์และ QR code ที่แจกไปแล้วจะยังใช้ได้และชี้ไปที่เนื้อหาเวอร์ชันล่าสุดเสมอ
  • ยอดเข้าชมที่เห็นในตารางขึ้นกับว่าหน้าฝั่งผู้อ่านเรียก endpoint บันทึกการเข้าชมสำเร็จหรือไม่ ตัวเลขนี้จึงอาจต่ำกว่าความเป็นจริงถ้าผู้ใช้ปิดหน้าเร็วหรือถูกบล็อกโดยเบราว์เซอร์