Now.js Framework Documentation

Now.js Framework Documentation

CodeEditor

TH 09 Nov 2026 04:40

CodeEditor

ภาพรวม

CodeEditor เปลี่ยน <textarea> ให้เป็นตัวเขียนโค้ด มีสีตามไวยากรณ์ เลขบรรทัด และปุ่มลัดที่นักเขียนโค้ดคุ้นเคย
อยู่ใน core bundle จึงใช้ได้ทุกโปรเจ็คโดยไม่ต้องโหลดไฟล์เพิ่ม

textarea ยังเป็นพื้นที่พิมพ์จริง การพิมพ์ เคอร์เซอร์ การเลือกข้อความ undo การพิมพ์ผ่าน input method (รวมภาษาไทย)
และการส่งฟอร์มเป็นของเบราว์เซอร์ทั้งหมด ส่วน <pre> ที่วาดอยู่ด้านหลังแสดงข้อความเดียวกันเป็นสี และไม่มีการอ่านค่าใดกลับจากชั้นนั้น
ค่าของ textarea จึงเป็นโค้ดตรงตามที่พิมพ์เสมอ

ใช้เมื่อ:

  • ฟอร์มที่แก้โค้ดหรือข้อความมีโครงสร้าง: CSS, template HTML, ค่าตั้ง JSON, SQL, PHP, JavaScript
  • หน้าที่แสดงโค้ดซึ่งผู้อ่านอาจแก้หรือคัดลอก

ใช้ได้สองแบบ:

เมื่อไร
<textarea data-element="code"> (CodeElementFactory) ในฟอร์ม: validation, required, FormError และ data-attr ทำงานเหมือนช่องอื่น
new CodeEditor(textarea, options) จากสคริปต์ นอกฟอร์ม (เช่น playground)
<form data-form="theme">
  <textarea name="css" data-element="code" data-language="css" data-max-height="60vh" required></textarea>
</form>
const editor = new CodeEditor(document.querySelector('#source'), {language: 'javascript', height: 400});
editor.getValue();

ตัวเลือก

ส่งผ่าน constructor หรือเป็น attribute data-* บน textarea (data-line-numbers, data-tab-size, ...)
ค่าที่ส่งผ่าน constructor ชนะ attribute

ตัวเลือก Attribute ค่าเริ่มต้น คำอธิบาย
language data-language 'text' ดู ภาษา
lineNumbers data-line-numbers true แสดงเลขบรรทัด
tabSize data-tab-size 2 จำนวนคอลัมน์ต่อระดับการย่อหน้า (1–8)
indentWithTabs data-indent-with-tabs false ย่อหน้าด้วยอักขระ tab
autoCloseBrackets data-auto-close-brackets true พิมพ์ ) ] } และเครื่องหมายคำพูดปิดให้พร้อมตัวเปิด
autoIndent data-auto-indent true Enter แล้วคงระดับย่อหน้า และลึกขึ้นหนึ่งระดับหลังตัวเปิด
minLines data-min-lines 3 (5 ในฟอร์ม) จำนวนบรรทัดที่เห็นอย่างน้อย
height data-height null ความสูงคงที่ (px หรือหน่วย CSS ใดก็ได้) เลื่อนภายใน
maxHeight data-max-height null สูงตามเนื้อหาจนถึงค่านี้
theme data-theme null 'light' หรือ 'dark' ถ้าไม่ระบุจะตาม data-theme ของหน้า
onChange — null function(value, editor) ทุกครั้งที่โค้ดเปลี่ยน

ถ้าไม่ระบุ height หรือ maxHeight ตัวเขียนจะสูงตามเนื้อหา และหน้าเว็บเป็นตัวเลื่อน

ปุ่มลัด

ปุ่ม ผล
Tab / Shift+Tab ย่อหน้า / ถอยย่อหน้า ถ้าเลือกหลายบรรทัดจะทำทุกบรรทัด
Enter ขึ้นบรรทัดใหม่โดยคงย่อหน้า หลัง { [ ( (หรือแท็กเปิด HTML) ลึกขึ้นหนึ่งระดับ และตัวปิดย้ายไปบรรทัดของตัวเอง
`( [ { " ' `` พิมพ์พร้อมตัวปิด ถ้าเลือกข้อความไว้จะครอบข้อความนั้น
ตัวปิดที่อยู่ติดเคอร์เซอร์ ข้ามไปแทนการพิมพ์ซ้ำ
Backspace ในคู่วงเล็บว่างลบทั้งคู่ ในช่องว่างหน้าบรรทัดลบทีละหนึ่งระดับย่อหน้า
Ctrl+/ (⌘/) ใส่หรือเอา comment ออกจากบรรทัดที่เลือก
Ctrl+Z / Ctrl+Shift+Z undo / redo ของเบราว์เซอร์ รวมการแก้ที่ตัวเขียนทำเอง
Esc แล้ว Tab / Shift+Tab ออกจากตัวเขียนไปช่องถัดไป / ช่องก่อนหน้าด้วยคีย์บอร์ด (Tab อย่างเดียวคือย่อหน้า)
คลิกเลขบรรทัด เลือกทั้งบรรทัด

Esc ปล่อยให้ออกได้แค่ Tab ครั้งถัดไปทันที ถ้าระหว่างนั้นผู้ใช้กดปุ่มอื่น พิมพ์หรือวางข้อความ (รวมข้อความจาก input method)
คลิกในโค้ดหรือที่เลขบรรทัด ออกจากตัวเขียนแล้วกลับเข้ามา หรือสคริปต์ย้ายเคอร์เซอร์ Tab จะกลับไปย่อหน้าตามปกติ
ส่วนปุ่ม modifier ที่กดเปล่า ๆ (Shift ของ Shift+Tab) ไม่นับ

Ctrl+Enter ไม่ถูกใช้ หน้าเว็บจึงผูกเองได้ (playground ใช้รันโค้ด)

ตัวเขียนไม่ห้าม Esc ส่งต่อไปถึง document หน้าต่างที่ปิดด้วย Esc (Modal, dialog) จึงปิดตามปกติ
ถ้าไม่อยากให้หน้าต่างที่มีโค้ดค้างแก้ปิดเพราะ Esc ให้หน้าเว็บกันเองที่ชั้นที่อยู่ใต้ document

API

new CodeEditor(textarea, options?)

ผูกตัวเขียนเข้ากับ textarea (element หรือ selector) ถ้า textarea มีตัวเขียนอยู่แล้วจะได้ตัวเดิมกลับมา
textarea ถูกย้ายเข้าไปใน element ของตัวเขียน (editor.root) ถ้า textarea ยังไม่อยู่ในหน้า ผู้เรียกต้องใส่ editor.root เข้าหน้าเอง

เมธอดของ instance

เมธอด คำอธิบาย
getValue() โค้ดปัจจุบัน (ค่าของ textarea)
setValue(value, options?) แทนโค้ดทั้งหมด ค่าเริ่มต้นเหมือนกำหนด textarea.value: undo ไม่ได้ ไม่มี event input · {history: true} ทำให้เป็นการแก้ที่ undo ได้ (focus ตัวเขียนและส่ง input)
getLanguage() / setLanguage(name) อ่านหรือเปลี่ยนภาษาแล้วระบายสีใหม่
insertText(text) แทนข้อความที่เลือกเหมือนพิมพ์เอง (undo ได้)
indent(outdent?) ย่อหน้าหรือถอยย่อหน้าบรรทัดที่เลือก
toggleComment() ใส่หรือเอา comment ออกจากบรรทัดที่เลือก
focus() ย้าย focus เข้าตัวเขียน
refresh() วัดและวาดใหม่ (หลังเปลี่ยนฟอนต์หรือขนาด)
destroy() คืน textarea กลับสภาพเดิม

การกำหนด textarea.value จากสคริปต์ใดก็ตามจะระบายสีใหม่เช่นกัน และติดตาม form.reset() ด้วย

Property

editor.textarea, editor.root (element .code-editor), editor.language, editor.options

Static

สมาชิก คำอธิบาย
CodeEditor.get(textarea) ตัวเขียนที่ผูกกับ textarea หรือ null
CodeEditor.highlight(code, language) HTML ที่ escape แล้ว มี token เป็น <span class="ce-{type}">
CodeEditor.tokenize(code, language) [{type, text}] นำ text มาต่อกันได้โค้ดเดิมทุกตัวอักษร
CodeEditor.registerLanguage(name, definition, aliases?) เพิ่มภาษา
CodeEditor.resolveLanguage(name) ชื่อภาษาที่ลงทะเบียนของชื่อหรือชื่อแฝง ('text' ถ้าไม่รู้จัก)
CodeEditor.defaults ค่าเริ่มต้นของตัวเลือก

เหตุการณ์

event input ของ textarea เกิดทุกครั้งที่โค้ดเปลี่ยนจากการพิมพ์หรือจากปุ่มลัดของตัวเขียน (ไม่เกิดกับ setValue() ที่ไม่ใส่ history)

ภาษา

ชื่อ ชื่อแฝง Comment
javascript js, mjs, cjs, jsx, ts, typescript //
json — —
html htm, xml, svg, xhtml, vue <!-- --> (เนื้อหา <script> และ <style> ระบายสีเป็น JavaScript และ CSS ส่วน <?php ?> เป็น PHP)
css scss, less /* */
php — //
sql mysql --
text plain, txt, plaintext — (ไม่มีสี ไม่ปิดวงเล็บให้)

เพิ่มภาษา

CodeEditor.registerLanguage('ini', {
  lineComment: ';',
  rules: [
    {type: null, match: /\s+/y, skip: true},
    {type: 'comment', match: /[;#][^\n]*/y, skip: true},
    {type: 'selector', match: /\[[^\]\n]*\]/y},
    {type: 'property', match: /[\w.-]+(?=\s*=)/y},
    {type: 'string', match: /"[^"\n]*"?/y}
  ]
});

กฎถูกลองตามลำดับทุกตำแหน่ง กฎแรกที่ตรงชนะ RegExp ทุกตัวต้องเป็นแบบ sticky (y) ข้อความที่ไม่ตรงกฎใดจะไม่มีสี
กฎหนึ่งข้อใส่ when(previousToken, state), onMatch(text, state), type แบบฟังก์ชัน หรือ scan(text, pos, state)
ที่คืน {tokens, end} สำหรับโครงสร้างที่ RegExp เดียวทำไม่ได้ และภาษาหนึ่งให้ state() สำหรับสถานะระหว่างสแกนได้
(ภาษา CSS ใช้แยก selector ออกจาก declaration)

ชนิด token ที่ภาษาในตัวใช้ แต่ละชนิดมีตัวแปรสี --ce-{type}: keyword, string, comment, number, constant,
function, property, builtin, operator, punctuation, regex, variable, tag, attr-name, attr-value,
selector, entity, meta

การจัดรูปแบบ

สไตล์อยู่ใน Now/css/code-editor.css ปรับด้วยตัวแปรบน .code-editor

.code-editor {
  --code-editor-font-size: 14px;
  --code-editor-line-height: 22px;   /* px: สองชั้นต้องตรงกัน */
  --code-editor-bg: #fff;
  --ce-keyword: #8b5cf6;
}

textarea และชั้นที่ระบายสีต้องใช้ฟอนต์ ขนาด ความสูงบรรทัด ระยะขอบ และ tab size เดียวกัน ไม่งั้นเคอร์เซอร์จะเลื่อนออกจากตัวอักษร
เปลี่ยนค่าเหล่านี้ผ่านตัวแปร --code-editor-* เท่านั้น บนจอสัมผัสฟอนต์จะขยายเป็น 16px เพื่อไม่ให้มือถือซูมเข้าช่อง

ประสิทธิภาพ

  • ไฟล์ไม่เกิน 300 บรรทัดวาดทั้งไฟล์ ไฟล์ที่ยาวกว่าวาดเฉพาะบรรทัดที่เห็น (และส่วนเผื่อ) การพิมพ์จึงยังเร็วในไฟล์หลายพันบรรทัด
    (ประมาณ 25 ms ต่อการกดในไฟล์โค้ดแน่น 1,500 บรรทัด)
  • หลังการแก้ แยก token ใหม่เฉพาะส่วนที่เปลี่ยน
  • เกิน 300,000 ตัวอักษรจะแสดงโค้ดโดยไม่มีสี

ข้อควรระวัง

⚠️ 1. ในฟอร์มให้ใช้ data-element="code"

new CodeEditor() แบบตรง ๆ จะนำ textarea ออกจาก ElementManager (data-enhance="false") validation ของช่องจึงไม่ทำงาน
ฟอร์มควรใช้ CodeElementFactory

⚠️ 2. setValue() undo ไม่ได้โดยค่าเริ่มต้น

การโหลดเนื้อหาจากสคริปต์ไม่ใช่สิ่งที่ผู้ใช้ทำ สำหรับการกระทำของผู้ใช้ เช่นโหลดตัวอย่างหรือล้างโค้ด ให้ส่ง {history: true}
เพื่อให้ Ctrl+Z เอาโค้ดเดิมกลับมาได้

⚠️ 3. บรรทัดไม่ตัดขึ้นบรรทัดใหม่

บรรทัดยาวเลื่อนไปด้านข้าง เพราะถ้าตัดบรรทัด เลขบรรทัดและตำแหน่งเคอร์เซอร์จะขึ้นกับความกว้าง

เอกสารที่เกี่ยวข้อง