ความแตกต่างระหว่าง jQuery "คลิก", ​​"ผูก", "สด", "ผู้รับมอบสิทธิ์", "ทริกเกอร์" และ "on" ฟังก์ชั่น (พร้อมตัวอย่าง)?


139

ฉันได้อ่านเอกสารของแต่ละฟังก์ชั่น jQuery official websiteแล้ว แต่ไม่มีรายการเปรียบเทียบดังกล่าวระหว่างฟังก์ชั่นด้านล่าง:

$().click(fn)
$().bind('click',fn)
$().live('click',fn)
$().delegate(selector, 'click', fn)
$().trigger('click') // UPDATED
$().on('click', selector ,fn); // more UPDATED

โปรดหลีกเลี่ยงลิงก์อ้างอิงใด ๆ

ฟังก์ชั่นทั้งหมดข้างต้นทำงานอย่างไรและควรเลือกใช้ในสถานการณ์ใด

หมายเหตุ:หากมีฟังก์ชั่นอื่น ๆ ที่มีฟังก์ชั่นหรือกลไกเดียวกันกรุณาอธิบายให้ละเอียด

ปรับปรุง

ฉันเคยเห็น$.triggerฟังก์ชั่น มันทำงานคล้ายกับฟังก์ชั่นด้านบนหรือไม่

ปรับปรุงเพิ่มเติม

ตอนนี้.onเพิ่มในv1.7และฉันคิดว่าอันนี้ครอบคลุมความต้องการฟังก์ชั่นทั้งหมดข้างต้นด้วยกัน


3
@ ฉันชอบ PHP บางครั้งผู้คนลงคะแนนถ้าพวกเขาไม่รู้สึกว่ามันเป็นคำถามที่ไม่สามารถตอบด้วยตนเองหรือดูเหมือนจะเป็นคำถามที่ต้องทำการบ้าน ไม่ต้องกังวลคนอื่นจะลงคะแนนถ้าพวกเขาพบว่ามีประโยชน์
typeoneerror

@ Typeoneerror - ขอบคุณสำหรับการสนับสนุนฉันได้อ่านคู่มือแล้วและเมื่อฉันไม่เข้าใจความแตกต่างที่ชัดเจนจากนั้นฉันโพสต์ที่นี่
diEcho

3
@ ฉันชอบ PHP - แก้ไขคำถามสำหรับการล้างข้อมูลเล็กน้อย ... นี่มักจะถูกถาม แต่ไม่ค่อยมีวิธีนี้มันสามารถเป็นทรัพยากรที่มีค่าของ Google ได้อย่างง่ายดาย ฉันเห็นด้วยกับรายการของ wiki-ish เช่นนี้จะมีประโยชน์มากฉันเห็นความสับสนรอบนี้โดยเฉพาะกับ.live()และ.delegate()เกือบทุกวัน +1 สำหรับคำถามที่ถูกต้องสมบูรณ์
Nick Craver

@Nick Craver ขอบคุณสำหรับการแก้ไขจริง ๆ แล้วฉันไม่ดีในภาษาอังกฤษ (lol) มีผู้ใด maunal / SOอ้างอิงว่าทำอย่างไรเราจะโพสต์คำถามบน ot มันเพิ่งมาจากประสบการณ์
diEcho

@ ฉันชอบ PHP - เป็นทั้งสองอย่างมีคำถามที่ดีมากมายสำหรับที่นี่: meta.stackexchange.com/questions/7931สำหรับการใช้ถ้อยคำ / ความคิดเห็นคุณเพิ่งเริ่มเรียนรู้สิ่งที่เหมาะสมและเป็นวิธีที่ดีที่สุดในการ ถ่ายทอดความคิดของคุณรหัส> คำอธิบายใด ๆ บางครั้งโดยใช้คำหลักที่ถูกต้องติดแท็กอย่างเหมาะสมยิ่งคุณมาที่นี่นานเท่าไรฉันคิดว่าฉันเห็นผู้โพสต์มากมายปรับปรุงอยู่ตลอดเวลา สำหรับการแก้ไขของคุณ.trigger()เพียงแค่เรียกใช้ตัวจัดการเหตุการณ์ ... ฉันจะเพิ่มคำอธิบายลงในคำตอบของฉันด้านล่าง
Nick Craver

คำตอบ:


162

ก่อนที่คุณจะอ่านให้ดึงรายการเหตุการณ์นี้ขึ้นมาในอีกหน้าหนึ่ง API นั้นมีประโยชน์อย่างมากและสิ่งที่ฉันกำลังพูดถึงด้านล่างทั้งหมดนั้นเชื่อมโยงโดยตรงจากหน้านี้

อย่างแรก.click(function)คือทางลัดสำหรับ.bind('click', function)พวกมันที่เทียบเท่ากัน ใช้พวกมันเมื่อรวมตัวจัดการกับองค์ประกอบโดยตรงเช่นนี้

$(document).click(function() {
  alert("You clicked somewhere in the page, it bubbled to document");
});

หากองค์ประกอบนี้ถูกแทนที่หรือถูกโยนออกไปตัวจัดการนี้จะไม่อยู่ที่นั่นอีกต่อไป องค์ประกอบที่ไม่ได้อยู่ที่นั่นเมื่อมีการเรียกใช้รหัสนี้เพื่อแนบตัวจัดการ (เช่นตัวเลือกที่พบแล้ว) จะไม่ได้รับตัวจัดการ

.live()และ.delegate()มีความเกี่ยวข้องในทำนองเดียวกัน.delegate()ใช้งานจริง.live()ภายในพวกเขาทั้งสองฟังเหตุการณ์ที่เกิดฟอง สิ่งนี้ใช้ได้กับองค์ประกอบใหม่และเก่ามันทำให้เหตุการณ์เหมือนกัน คุณใช้สิ่งเหล่านี้เมื่อองค์ประกอบของคุณอาจมีการเปลี่ยนแปลงเช่นการเพิ่มแถวรายการในรายการ ฯลฯ หากคุณไม่มีบรรพบุรุษ / บรรพบุรุษทั่วไปที่จะอยู่ในหน้าและไม่ถูกแทนที่ในทุกจุดใช้.live()ดังนี้:

$(".clickAlert").live('click', function() {
  alert("A click happened");
});

หากคุณมีองค์ประกอบพาเรนต์ที่ไม่ได้ถูกแทนที่ (ดังนั้นตัวจัดการเหตุการณ์จะไม่ลาก่อน) คุณควรจัดการกับองค์ประกอบ.delegate()ดังนี้:

$("#commonParent").delegate('.clickAlert', 'click', function() {
  alert("A click happened, it was captured at #commonParent and this alert ran");
});

สิ่งนี้ใช้งานได้เกือบเหมือนกัน.live()แต่เหตุการณ์นั้นเกิดฟองสบู่น้อยกว่าครั้งก่อนที่จะถูกจับและตัวจัดการดำเนินการ การใช้งานร่วมกันของทั้งสองอย่างนี้คือการที่คลาสของคุณเปลี่ยนแปลงองค์ประกอบไม่ตรงกับตัวเลือกที่คุณใช้ในตอนแรก ... ด้วยวิธีการเหล่านี้ตัวเลือกจะได้รับการประเมินในเวลาที่เกิดเหตุการณ์หากตรงกับตัวจัดการจะทำงาน .. . ดังนั้นองค์ประกอบไม่ตรงกับตัวเลือกที่สำคัญอีกต่อไปมันจะไม่ทำงานอีกต่อไป .click()อย่างไรก็ตามด้วยตัวจัดการเหตุการณ์ที่ถูกผูกไว้บนองค์ประกอบ DOM ความจริงที่ว่ามันไม่ตรงกับตัวเลือกใด ๆ ที่ใช้ในการค้นหาว่ามันไม่เกี่ยวข้อง ... เหตุการณ์ถูกผูกไว้และมันจะคงอยู่จนกว่าองค์ประกอบนั้นจะหายไปหรือตัวจัดการ .unbind()จะถูกลบออกผ่านทาง

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


การเรียกใช้ - สำหรับคำถามที่อัปเดต

มี 2 ฟังก์ชั่นจัดการเหตุการณ์วิกฤติหลักที่มีอยู่ให้พวกเขาตกอยู่ภายใต้เดียวกัน "สิ่งที่แนบมาจัดการเหตุการณ์" ในหมวดหมู่ของ APIเหล่านี้เป็นและ .trigger() มีทางลัดในตัวสำหรับเหตุการณ์ทั่วไปตัวอย่างเช่น.triggerHandler().trigger('eventName')

$().click(fn); //binds an event handler to the click event
$().click();   //fires all click event handlers for this element, in order bound

คุณสามารถดูรายชื่อรวมทั้งทางลัดเหล่านี้ที่นี่

สำหรับความแตกต่าง.trigger()ทริกเกอร์ตัวจัดการเหตุการณ์ (แต่ไม่ใช่การกระทำเริ่มต้นเกือบตลอดเวลาเช่นวางเคอร์เซอร์ที่จุดที่ถูกต้องในคลิก<textarea>) มันทำให้ตัวจัดการเหตุการณ์เกิดขึ้นในลำดับที่ถูกผูกไว้ (ตามที่เป็นเหตุการณ์ดั้งเดิม), ไฟการกระทำเหตุการณ์พื้นเมืองและฟองขึ้น DOM

.triggerHandler()โดยทั่วไปแล้วจะใช้เพื่อจุดประสงค์ที่แตกต่างกันที่นี่คุณเพียงแค่พยายามยิงตัวจัดการที่ถูกผูกไว้มันไม่ทำให้เกิดเหตุการณ์แบบดั้งเดิมที่จะยิงเช่นส่งแบบฟอร์ม มันไม่ทำให้ฟอง DOM และไม่สามารถโยงได้ (จะส่งคืนสิ่งที่ตัวจัดการเหตุการณ์ที่ถูกผูกไว้ล่าสุดสำหรับเหตุการณ์นั้นส่งคืน) ตัวอย่างเช่นถ้าคุณต้องการที่จะทริกเกอร์focusเหตุการณ์ แต่ไม่ได้มุ่งเน้นวัตถุจริงๆคุณเพียงแค่ต้องการให้รหัสที่คุณผูกไว้กับ.focus(fn)การทำงานนี้จะทำเช่นนั้นในขณะที่.trigger()จะทำเช่นนั้นเช่นเดียวกับเน้นองค์ประกอบและฟองขึ้น

นี่คือตัวอย่างโลกแห่งความจริง:

$("form").submit(); //actually calling `.trigger('submit');`

นี้จะทำงานใด ๆ ที่ส่งรถยกตัวอย่างเช่นjQuery ตรวจสอบปลั๊กอิน<form>แล้วพยายามที่จะส่ง แต่ถ้าคุณเพียงต้องการที่จะตรวจสอบเนื่องจากมันติดยาเสพติดผ่านทางsubmitจัดการเหตุการณ์ แต่ไม่ได้ส่ง<form>หลังจากนั้นคุณสามารถใช้.triggerHandler('submit')เช่นนี้

$("form").triggerHandler('submit');

ปลั๊กอินป้องกันตัวจัดการจากการส่งแบบฟอร์มโดยการระเบิดถ้าการตรวจสอบการตรวจสอบไม่ผ่าน แต่ด้วยวิธีนี้เราไม่สนใจสิ่งที่มันทำ ไม่ว่าจะเป็นการยกเลิกหรือไม่เราไม่ได้พยายามส่งแบบฟอร์มเราเพียงต้องการทริกเกอร์แบบฟอร์มเพื่อตรวจสอบความถูกต้องอีกครั้งและไม่ทำอะไรเลย ( ข้อจำกัดความรับผิดชอบ:นี่เป็นตัวอย่างที่ไม่จำเป็นเนื่องจากมี.validate()วิธีการในปลั๊กอิน แต่เป็นตัวอย่างที่ดีของเจตนา)


ถี่ถ้วนมาก การแก้ไขเล็ก ๆ น้อย ๆ หนึ่งอย่าง: triggerไม่ได้เริ่มเหตุการณ์พื้นเมือง โดยดั้งเดิมฉันหมายถึงเหตุการณ์จำลองด้วยfireEvent(IE) หรือdispatchEvent(w3c)
Crescent Fresh

@Crescent - อัปเดตให้มีความคลุมเครือน้อยลงฉันหมายถึงว่ามันเป็นการกระทำที่เกิดจากเหตุการณ์ดั้งเดิมเช่นการส่งแบบฟอร์มการเชื่อมโยงต่อไปนี้เป็นต้นหวังว่าการอัปเดตจะชัดเจน :)
Nick Craver

@ ไม่อ่านสิ่งที่ฉันอ่านดูเหมือนว่าสิ่งที่คุณทำlive()จะแตกต่างจากที่คุณให้ไว้ที่นี่: stackoverflow.com/questions/3981762/… 'น้ำหนัก' ยังเป็นปัญหาเมื่อใช้live()หรือไม่
Yahel

@ yahelc - ใช่แน่นอน ... คำตอบนี้เป็นการเปรียบเทียบตัวเลือกของตัวจัดการเหตุการณ์และวัตถุประสงค์เฉพาะของพวกเขาอย่างเคร่งครัด ต้นทุนน้ำหนักเทียบกับจำนวนการผูกเริ่มต้นเทียบกับหากคุณเพิ่มอะไรแบบไดนามิกกับตัวเลือกเริ่มต้นยังคงเป็นปัญหาที่ถูกต้อง หากคุณกำลังโครงสร้างหน้าไม่ว่าขนาดใหญ่ที่คุณคุณไม่ได้ว่าหลายเหตุการณ์แล้วคุณกำลังดี ... ความกังวลน้ำหนักเกินเป็นสัดส่วนโดยตรงกับขนาด / ความลึกของโครงสร้างของคุณและจำนวนของเหตุการณ์ที่เกิดขึ้น (จาก ประเภทการ.live()โทรของคุณรับฟังเช่นclick) ที่กำลังถูกสร้างขึ้นเนื่องจากจะเรียกใช้ตัวเลือกสำหรับแต่ละรายการ
Nick Craver

1
ใน jQuery 1.7, live () ถูกคัดค้าน $ (document) .on ()
ซิงโคร

28

สองคนแรกเทียบเท่ากัน

// The following two statements do the same thing:
$("blah").click( function() { alert( "Click!" ); } );
$("blah").bind( "click", function() { alert( "Click!" ); } ); 

อย่างไรก็ตามตัวที่สองสามารถใช้เพื่อเชื่อมโยงกับเหตุการณ์มากกว่าหนึ่งรายการในเวลาเดียวกันโดยการระบุชื่อเหตุการณ์ที่คั่นด้วยช่องว่างหลายรายการ:

$("blah").bind( "click mouseover mouseout", function() { alert( "Click! Or maybe mouse moved." ); } ); 

.liveเป็นวิธีการที่น่าสนใจมากขึ้น ลองพิจารณาตัวอย่างต่อไปนี้:

<a class="myLink">A link!</a>
<a id="another">Another link!</a>

<script>
    $("a.myLink").click( function() { alert( 'Click!' ); } );

    $("a#another").addClass( "myLink" );
</script>

หลังจากบรรทัดที่สองของสคริปต์ทำงานแล้วลิงค์ที่สองจะมีคลาส CSS เป็น "myLink" แต่มันจะไม่มีตัวจัดการเหตุการณ์เพราะมันไม่มีคลาสเมื่อมีการแนบเหตุการณ์

ตอนนี้ให้พิจารณาว่าคุณต้องการให้มันเป็นอย่างอื่น: ทุกครั้งที่ลิงก์ที่มีคลาส "myLink" ปรากฏที่ใดที่หนึ่งบนหน้าเว็บคุณต้องการให้มันมีตัวจัดการเหตุการณ์เดียวกันโดยอัตโนมัติ นี่เป็นเรื่องธรรมดามากเมื่อคุณมีรายการหรือตารางบางประเภทที่คุณเพิ่มแถวหรือเซลล์แบบไดนามิก แต่ต้องการให้พวกเขาทั้งหมดทำงานในลักษณะเดียวกัน แทนที่จะไปสู่ความเจ็บปวดทั้งหมดของการกำหนดตัวจัดการเหตุการณ์ใหม่ทุกครั้งคุณสามารถใช้.liveวิธีการ:

<a class="myLink">A link!</a>
<a id="another">Another link!</a>

<script>
    $("a.myLink").live( "click", function() { alert( 'Click!' ); } );

    $("a#another").addClass( "myLink" );
</script>

ในตัวอย่างนี้ลิงก์ที่สองจะได้รับตัวจัดการเหตุการณ์ทันทีที่ได้รับคลาส "myLink" มายากล! :-)

แน่นอนมันไม่ใช่ตัวอักษรที่แท้จริง สิ่งที่.liveจริง ๆ แล้วคือการแนบตัวจัดการไม่ได้กับองค์ประกอบที่ระบุตัวเอง แต่กับรากของต้นไม้ HTML (องค์ประกอบ "ร่างกาย") กิจกรรมใน DHTML มีคุณสมบัติที่ตลกของ "เดือดปุด ๆ " พิจารณาสิ่งนี้:

<div> <a> <b>text</b> </a> </div>

หากคุณคลิกที่ "ข้อความ" ดังนั้นองค์ประกอบ <b> แรกจะได้รับเหตุการณ์ "คลิก" หลังจากนั้นองค์ประกอบ <a> จะได้รับเหตุการณ์ "คลิก" และหลังจากนั้นองค์ประกอบ <div> จะได้รับเหตุการณ์ "คลิก" และตลอดไปจนถึงองค์ประกอบ <body> และนั่นคือสิ่งที่ jQuery จะจับเหตุการณ์และดูว่ามีตัวจัดการ "สด" ใดบ้างที่ใช้กับองค์ประกอบที่ทำให้เกิดเหตุการณ์ในตอนแรก เรียบร้อย!

และในที่สุด.delegateวิธีการ เพียงใช้ลูก ๆ ทั้งหมดขององค์ประกอบของคุณที่สอดคล้องกับตัวเลือกที่กำหนดและแนบตัวจัดการ "สด" กับพวกเขา ลองดูสิ:

$("table").delegate( "td", "click", function() { alert( "Click!" ); } );

// Is equivalent to:
$("table").each( function() {
    $(this).find( "td" ).live( "click", function() { alert( "Click!" ); } );
} );

คำถาม?


เพื่อความถูกต้อง.live()ผูกกับdocumentไม่ใช่<body>:) คุณสามารถดูตัวอย่างได้ที่นี่เพียงดึงคอนโซลขึ้นมาตรวจสอบ: jsfiddle.net/aJy2B
Nick

3
มันสะดวกกว่าที่จะอธิบายด้วยวิธีนี้ :-)
Fyodor Soikin

4
ยุติธรรมเพียงพอในส่วน "ร่างกาย" แต่ "ตลอดจนองค์ประกอบ <body>" ผิดกิจกรรมยังคงมีฟองผ่านไปที่นั่นและไม่ใช่ตำแหน่งที่.live()ตัวจัดการมีชีวิตอยู่เหนือสิ่งนั้นในdocument:) คุณสามารถเห็น ตัวอย่างของที่นี่: jsfiddle.net/S2VBX
Nick

8

ตั้งแต่ jQuery 1.7 วิธีการ. live () ถูกเลิกใช้แล้ว หากคุณใช้ jQuery เวอร์ชัน <1.7 มากกว่าแนะนำให้ใช้. delegate () มากกว่า. live ()

.live () ถูกแทนที่ด้วย. on ()

ดีที่สุดที่จะตรงไปยังเว็บไซต์ jQuery สำหรับข้อมูลเพิ่มเติม แต่นี่คือวิธีการ. on () รุ่นปัจจุบัน:

.on( events [, selector] [, data], handler(eventObject) )
.on( events-map [, selector] [, data] )

http://api.jquery.com/on/


2

$().click(fn)และ$().bind('click', fn)เหมือนกันตั้งแต่แรกเห็น แต่$.bindรุ่นมีประสิทธิภาพมากกว่าด้วยเหตุผลสองประการ:

  1. $().bind()ช่วยให้คุณสามารถกำหนดหนึ่งจัดการกับเหตุการณ์หลาย ๆ $().bind('click keyup', fn)ตัวอย่างเช่น
  2. $().bind()สนับสนุน namespaced เหตุการณ์ - คุณลักษณะที่มีประสิทธิภาพถ้าคุณต้องการที่จะลบ (ยกเลิกการเชื่อมโยง) เพียงจัดการเหตุการณ์บางอย่างที่เป็นองค์ประกอบที่ถูกผูกไว้กับ - อ่านเพิ่มเติมในnamespaced เหตุการณ์

ผู้ได้รับมอบหมาย vs สด: สิ่งนี้ได้รับการตอบแล้วในคำตอบอื่น ๆ


1

นี่คือที่การอ่าน API อาจช่วยได้ อย่างไรก็ตามฉันรู้ว่าส่วนบนของหัวของฉันดังนั้นคุณสามารถดำเนินการต่อขี้เกียจ (yay!)

$('#something').click(fn);
$('#something').bind('click',fn);

ที่นี่ไม่มีความแตกต่าง (ที่ฉันรู้) .clickเป็นเพียงวิธีการอำนวยความสะดวก / ช่วยเหลือ.bind('click'

// even after this is called, all <a>s in
// <div class="dynamic_els"> will continue
// to be assigned these event handlers

$('div.dynamic_els a').live(‘click’,fn);

สิ่งนี้แตกต่างอย่างมากเมื่อ.liveเพิ่มเหตุการณ์ให้กับตัวเลือกที่คุณส่งผ่าน (ซึ่งคุณไม่ได้อยู่ที่นี่) และดูที่ DOM ต่อไปเนื่องจากมีการแทรก / ลบโหนด

$('#some_element').delegate('td','click',fn);

สิ่งนี้จะแตกต่างกันเนื่องจากวิธีที่คุณกำหนดตัวจัดการเหตุการณ์ .delegateถูกจัดกึ่งกลางในการทำให้เกิดเหตุการณ์ DOM หลักการพื้นฐานคือทุกเหตุการณ์ฟองสบู่ขึ้นบนต้นไม้ DOM จนกว่าจะถึงองค์ประกอบราก ( documentหรือwindowหรือ<html>หรือ<body>ฉันจำไม่ได้)

ไม่ว่าคุณจะใช้onclickวิธีใด<td> s ภายใน$('#some_element')(คุณต้องระบุตัวเลือกแม้ว่าคุณอาจจะบอกว่า$(document)) <td>เมื่อหนึ่งในจำนวนเด็กที่มีการคลิกเหตุการณ์ฟองขึ้นไป จากนั้นคุณสามารถแยกองค์ประกอบแหล่งที่มาของเหตุการณ์ (ซึ่ง jQuery ทำเพื่อคุณโดยอัตโนมัติ)

สิ่งนี้มีประโยชน์เมื่อมีองค์ประกอบมากมายและคุณมีเพียงไม่กี่จุด (หรือหนึ่งศูนย์กลาง) ที่เหตุการณ์เหล่านี้จะผ่านไป สิ่งนี้จะช่วยประหยัดความพยายามของเบราว์เซอร์และหน่วยความจำเพื่อรวมตัวจัดการเหตุการณ์เหล่านี้เข้ากับวัตถุที่น้อยลง


1
.live() นอกจากนี้ยังใช้งานได้กับเหตุการณ์ฟองอากาศในความ.delegate()เป็นจริงเป็นเสื้อคลุมสำหรับ.live()มันเป็นเพียงการเพิ่มบริบทและผูกพันกับองค์ประกอบอื่นที่ไม่ใช่documentการจับฟอง ฉันคิดว่าคุณเข้าใจว่าตัวจัดการ bubbling ทำงานอย่างไร (นี่เป็นสิ่งที่เข้าใจกันโดยทั่วไปของ jQuery 1.4 ฉันคิดว่า) จัดการเป็นเพียงองค์ประกอบที่คุณผูกพันมันดังนั้นสิ่งที่คุณเรียกว่าองค์ประกอบ.delegate()บนหรือdocumentในกรณีของ.live()เมื่อเหตุการณ์ฟองที่จะมีมันจะตรวจสอบเป้าหมายเพื่อดูว่ามันตรงกับตัวเลือกและหากดังนั้นรัน
Nick Craver
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.