querySelector และ querySelectorAll เทียบกับ getElementsByClassName และ getElementById ใน JavaScript


165

ฉันต้องการทราบว่าอะไรคือความแตกต่างระหว่างquerySelectorและquerySelectorAllต่อ getElementsByClassNameและgetElementById?

จากลิงค์นี้ฉันสามารถรวบรวมว่าquerySelectorฉันจะเขียนdocument.querySelector(".myclass")เพื่อให้ได้องค์ประกอบที่มีระดับmyclassและdocument.querySelector("#myid")เพื่อให้ได้องค์ประกอบที่มี myidID แต่ผมอยู่แล้วสามารถทำเช่นนั้นและgetElementsByClassName getElementByIdควรเลือกอันไหน

นอกจากนี้การทำงานของผมในXPagesที่ ID view:_id1:inputText1ที่มีการสร้างแบบไดนามิกกับลำไส้ใหญ่และรูปลักษณ์เช่นนี้ ดังนั้นเมื่อฉันเขียนdocument.querySelector("#view:_id1:inputText1")มันไม่ทำงาน แต่document.getElementById("view:_id1:inputText1")งานเขียน ความคิดใดทำไม


1
querySelector ใช้สำหรับการสืบค้นแผนผัง HTML DOM ซึ่งสามารถรวมองค์ประกอบ html และแอตทริบิวต์เป็นองค์ประกอบสำคัญสำหรับการสอบถาม ... คุณสามารถใช้นิพจน์ทั่วไปเพื่อให้บรรลุ .. dojo.query () ทำสิ่งเดียวกัน
anix

1
คุณไม่หมายถึงdocument.querySelectorAll(".myclass")เหรอ การใช้document.querySelector(".myclass")จะส่งคืนองค์ประกอบแรกที่ตรงกันเท่านั้น
mhatch

คำตอบ:


113

ฉันต้องการทราบว่าอะไรคือข้อแตกต่างระหว่าง querySelector และ querySelectorAll เทียบกับ getElementsByClassName และ getElementById?

สนับสนุนไวยากรณ์และเบราว์เซอร์

querySelector มีประโยชน์มากขึ้นเมื่อคุณต้องการใช้ตัวเลือกที่ซับซ้อนมากขึ้น

เช่นรายการทั้งหมดลงมาจากองค์ประกอบที่เป็นสมาชิกของคลาส foo: .foo li

document.querySelector ("# view: _id1: inputText1") ไม่ทำงาน แต่การเขียน document.getElementById ("ดู: _id1: inputText1") ใช้งานได้ ความคิดใดทำไม

:ตัวละครที่มีความหมายพิเศษภายในผู้เลือก คุณต้องหลบหนี (ตัวละครตัวเลือกการหลบหนีมีความหมายเป็นพิเศษในสตริง JS เกินไปดังนั้นคุณจะต้องหลบหนีที่มากเกินไป)

document.querySelector("#view\\:_id1\\:inputText1")

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

1
ฉันสนับสนุนคำสั่งของ @ janaspage เว็บไซต์ก็หยุดเช่นกัน
doplumi

6
และเกี่ยวกับการเลือกระดับเห็นjsperf.com/getelementsbyclassname-vs-queryselectorall/25 สรุป: หนึ่งที่ไกลมากขึ้นควรต้องการจาวาสคริปต์บริสุทธิ์กว่า jQuery และฟังก์ชั่นที่เฉพาะเจาะจงและgetElementById getElementsByClassNameการเลือก className สามารถมีได้หลายร้อยgetElementsByClassNameครั้งช้าโดยไม่ต้อง
Atrahasis

101

รวบรวมจากเอกสาร Mozilla:

อินเทอร์เฟซ NodeSelector ข้อมูลจำเพาะนี้เพิ่มวิธีการใหม่สองวิธีไปยังวัตถุใด ๆ ที่ใช้อินเทอร์เฟซเอกสาร DocumentFragment หรือองค์ประกอบ:

querySelector

ส่งคืนโหนดองค์ประกอบที่ตรงกันแรกภายในทรีย่อยของโหนด หากไม่พบโหนดที่ตรงกันจะส่งคืน null

querySelectorAll

ส่งคืนNodeList ที่มีโหนดElement ที่ตรงกันทั้งหมดภายในทรีย่อยของโหนดหรือ NodeList ว่างเปล่าหากไม่พบรายการที่ตรงกัน

และ

หมายเหตุ: NodeList ที่ส่งคืนโดยquerySelectorAll()ไม่มีการใช้งานอยู่ซึ่งหมายความว่าการเปลี่ยนแปลงใน DOM จะไม่ส่งผลต่อการรวบรวม สิ่งนี้แตกต่างจากวิธีการสืบค้น DOM อื่น ๆ ที่ส่งคืนรายการโหนดสด


32
+1 สำหรับการชี้ให้เห็นความแตกต่างของรายการสดโหนด นั่นเป็นข้อแตกต่างที่สำคัญอย่างยิ่งที่ต้องระวังทั้งนี้ขึ้นอยู่กับว่าคุณตั้งใจจะใช้ผลลัพธ์อย่างไร
jmbpiano

7
"live" หมายถึงโหนดที่เพิ่มใน DOM runtime และสามารถทำงานกับ newley ที่เพิ่มโหนดได้
diEcho

83

เกี่ยวกับความแตกต่างมีสิ่งสำคัญอย่างหนึ่งในผลลัพธ์ระหว่างquerySelectorAllและgetElementsByClassName: ค่าส่งคืนแตกต่างกัน querySelectorAllจะส่งคืนคอลเลกชันคงที่ในขณะที่getElementsByClassNameส่งคืนคอลเลกชันสด สิ่งนี้อาจนำไปสู่ความสับสนหากคุณเก็บผลลัพธ์ไว้ในตัวแปรเพื่อใช้ในภายหลัง:

  • ตัวแปรที่สร้างขึ้นด้วยquerySelectorAllจะมีองค์ประกอบที่ตอบสนองตัวเลือกในขณะที่วิธีการที่เรียกว่าในขณะที่วิธีการที่เรียกว่า
  • ตัวแปรที่สร้างขึ้นด้วยgetElementsByClassNameจะมีองค์ประกอบที่เติมเต็มตัวเลือกเมื่อมีการใช้งาน (ซึ่งอาจแตกต่างจากช่วงเวลาที่มีการเรียกเมธอด)

ตัวอย่างเช่นสังเกตว่าแม้ว่าคุณยังไม่ได้กำหนดตัวแปรใหม่aux1และaux2มีค่าต่างกันหลังจากอัปเดตคลาส:

// storing all the elements with class "blue" using the two methods
var aux1 = document.querySelectorAll(".blue");
var aux2 = document.getElementsByClassName("blue");

// write the number of elements in each array (values match)
console.log("Number of elements with querySelectorAll = " + aux1.length);
console.log("Number of elements with getElementsByClassName = " + aux2.length);

// change one element's class to "blue"
document.getElementById("div1").className = "blue";

// write the number of elements in each array (values differ)
console.log("Number of elements with querySelectorAll = " + aux1.length);
console.log("Number of elements with getElementsByClassName = " + aux2.length);
.red { color:red; }
.green { color:green; }
.blue { color:blue; }
<div id="div0" class="blue">Blue</div>
<div id="div1" class="red">Red</div>
<div id="div2" class="green">Green</div>


2
เพียงแค่พูดถึง - ทุกเก่า apis DOM กลับรายการโหนดคือdocument.getElementsByName, document.getElementsByTagNameNSหรือdocument.getElementsByTagNameจะแสดงพฤติกรรมเดียวกัน
RBT

2
วิเคราะห์บางคนกล่าวว่า querySelector ใช้เวลามากกว่า getElementById เช่นที่นี่dimlucas.com/index.php/2016/09/17/... ถ้าเราคำนึงถึงเวลาในการเข้าถึง ไม่โหนดสดที่ได้รับจาก getElementById ใช้เวลามากกว่าหนึ่งคงที่จาก querySelector?
Eric

1
@RBT ฉันพูดถึงว่า DOM API ที่เก่ากว่าเหล่านี้จะไม่ส่งคืนวัตถุ NodeList แต่จะส่งคืน HTMLCollections
สารเลว

@Eric document.getElementById()ไม่ส่งคืนโหนดสด มันเร็วกว่าdocument.querySelector('#id_here')อาจเป็นเพราะquerySelectorจะต้องแยกวิเคราะห์ CSS selector ก่อน
สารเลว

68

สำหรับคำตอบนี้ผมหมายถึงquerySelectorและquerySelectorAllเป็น querySelector * และเพื่อgetElementById, getElementsByClassName, getElementsByTagNameและgetElementsByNameเป็น getElement *

ความแตกต่างหลัก

  1. querySelector * มีความยืดหยุ่นมากขึ้นเนื่องจากคุณสามารถส่งผ่านตัวเลือก CSS3 ใด ๆ ได้ไม่ใช่แค่ตัวเลือกง่ายๆสำหรับ id แท็กหรือคลาส
  2. ประสิทธิภาพของ querySelectorจะเปลี่ยนตามขนาดของ DOM ที่ถูกเรียกใช้* เพื่อความแม่นยำการเรียก querySelector * จะทำงานในเวลา O (n) และการเรียก getElement * จะทำงานในเวลา O (1) โดยที่ n คือจำนวนทั้งหมดของลูกทั้งหมดขององค์ประกอบหรือเอกสารที่เรียกใช้ ความจริงเรื่องนี้ดูเหมือนจะเป็นที่รู้จักน้อยที่สุดดังนั้นฉันจึงถือมัน
  3. การเรียก getElement * คืนการอ้างอิงโดยตรงไปยัง DOM ในขณะที่ querySelector * สร้างสำเนาภายในขององค์ประกอบที่เลือกไว้ก่อนที่จะส่งกลับการอ้างอิงถึงพวกเขา สิ่งเหล่านี้เรียกว่าองค์ประกอบ "สด" และ "คงที่" สิ่งนี้ไม่เกี่ยวข้องกับประเภทที่ส่งคืนอย่างเคร่งครัด ไม่มีทางที่ฉันจะรู้ว่าองค์ประกอบนั้นเป็นรายการสดหรือเป็นแบบคงที่โดยทางโปรแกรมขึ้นอยู่กับว่าองค์ประกอบถูกคัดลอกในบางจุดและไม่ได้เป็นคุณสมบัติที่แท้จริงของข้อมูล การเปลี่ยนแปลงองค์ประกอบที่ใช้งานจะมีผลทันที - การเปลี่ยนแปลงองค์ประกอบที่มีการเปลี่ยนแปลงจะเปลี่ยนแปลงโดยตรงใน DOM และดังนั้นบรรทัดถัดไปของ JS จะเห็นการเปลี่ยนแปลงนั้นและจะเผยแพร่ไปยังองค์ประกอบอื่น ๆ ที่อ้างอิงองค์ประกอบนั้นทันที การเปลี่ยนแปลงองค์ประกอบแบบสแตติกจะถูกเขียนกลับไปที่ DOM หลังจากสคริปต์ปัจจุบันดำเนินการเสร็จสิ้น
  4. ผลตอบแทนประเภทของสายเหล่านี้แตกต่างกันไป querySelectorและgetElementByIdทั้งสองส่งคืนองค์ประกอบเดียว querySelectorAllและgetElementsByNameทั้งสองกลับ NodeLists เป็นฟังก์ชั่นใหม่ที่ถูกเพิ่มเข้ามาหลังจากที่ HTMLCollection ล้าสมัยไปแล้ว HTMLC ที่เก่ากว่าgetElementsByClassNameและgetElementsByTagNameทั้งคู่ส่งคืน HTMLCollections อีกครั้งนี้เป็นหลักไม่เกี่ยวข้องกับองค์ประกอบที่มีชีวิตอยู่หรือคงที่

แนวคิดเหล่านี้ถูกสรุปไว้ในตารางต่อไปนี้

Function               | Live? | Type           | Time Complexity
querySelector          |   N   | Element        |  O(n)
querySelectorAll       |   N   | NodeList       |  O(n)
getElementById         |   Y   | Element        |  O(1)
getElementsByClassName |   Y   | HTMLCollection |  O(1)
getElementsByTagName   |   Y   | HTMLCollection |  O(1)
getElementsByName      |   Y   | NodeList       |  O(1)

รายละเอียดเคล็ดลับและตัวอย่าง

  • HTMLCollections ไม่เหมือนอาร์เรย์เหมือน NodeLists และไม่สนับสนุน. forEach () ฉันพบว่าตัวดำเนินการสเปรดมีประโยชน์ในการแก้ไขปัญหานี้:

    [...document.getElementsByClassName("someClass")].forEach()

  • ทุกองค์ประกอบและทั่วโลกdocumentมีการเข้าถึงทุกฟังก์ชั่นเหล่านี้ยกเว้นgetElementByIdและซึ่งจะดำเนินการเฉพาะในgetElementsByNamedocument

  • การเรียกใช้ getElement * Chaining แทนการใช้ querySelector * จะปรับปรุงประสิทธิภาพโดยเฉพาะใน DOM ที่มีขนาดใหญ่มาก แม้แต่ใน DOM ขนาดเล็กและ / หรือด้วยโซ่ที่ยาวมากโดยทั่วไปจะเร็วกว่า อย่างไรก็ตามถ้าคุณไม่ทราบว่าคุณต้องการประสิทธิภาพการทำงานควรอ่านความต้องการของ querySelector * querySelectorAllมักจะยากที่จะเขียนซ้ำเพราะคุณต้องเลือกองค์ประกอบจาก NodeList หรือ HTMLCollection ในทุกขั้นตอน ตัวอย่างเช่นรหัสต่อไปนี้ใช้งานไม่ได้ :

document.getElementsByClassName("someClass").getElementsByTagName("div")

because you can only use getElements* on single elements, not collections. For example:

`document.querySelector("#someId .someClass div")`

could be written as:

document.getElementById("someId").getElementsByClassName("someClass")[0].getElementsByTagName("div")[0]

Note the use of `[0]` to get just the first element of the collection at each step that returns a collection, resulting in one element at the end just like with `querySelector`.
  • เนื่องจากองค์ประกอบทั้งหมดมีการเข้าถึงทั้งการสืบค้น querySelector * และ getElement * คุณสามารถทำการโยงโดยใช้การโทรทั้งสองซึ่งอาจมีประโยชน์หากคุณต้องการประสิทธิภาพที่เพิ่มขึ้น แต่ไม่สามารถหลีกเลี่ยงการ querySelector ที่ไม่สามารถเขียนในแง่ของการโทร getElement * .

  • แม้ว่าโดยทั่วไปจะง่ายต่อการบอกว่าตัวเลือกสามารถเขียนได้โดยใช้การเรียก getElement * เท่านั้น แต่มีบางกรณีที่อาจไม่ชัดเจน:

    document.querySelectorAll(".class1.class2")

    สามารถเขียนใหม่เป็น

    document.getElementsByClassName("class1 class2")

  • การใช้ getElement * บนองค์ประกอบคงที่ที่ดึงมาด้วย querySelector * จะส่งผลให้องค์ประกอบที่มีชีวิตอยู่ด้วยความเคารพต่อชุดย่อยคงที่ของ DOM คัดลอกโดย querySelector แต่ไม่ได้อยู่กับความเคารพในเอกสารเต็ม DOM ... นี่คือที่ง่าย การตีความองค์ประกอบ / สดคงที่เริ่มกระจุย คุณควรหลีกเลี่ยงสถานการณ์ที่คุณต้องกังวลเกี่ยวกับเรื่องนี้ แต่ถ้าคุณทำโปรดจำไว้ว่า querySelector * เรียกองค์ประกอบการคัดลอกที่พวกเขาพบก่อนที่จะส่งกลับการอ้างอิงถึงพวกเขา แต่การเรียก getElement * เรียกการอ้างอิงโดยตรงโดยไม่คัดลอก

  • API จะไม่ระบุองค์ประกอบที่ควรเลือกก่อนหากมีการแข่งขันหลายรายการ

  • เนื่องจาก querySelector * ทำซ้ำผ่าน DOM จนกว่าจะพบการแข่งขัน (ดูความแตกต่างหลัก # 2) ข้างต้นก็หมายความว่าคุณไม่สามารถพึ่งพาตำแหน่งขององค์ประกอบที่คุณกำลังมองหาใน DOM เพื่อรับประกันว่าจะพบได้อย่างรวดเร็ว - เบราว์เซอร์อาจทำซ้ำผ่าน DOM ย้อนกลับไปข้างหน้าลึกก่อนกว้างหรืออื่น ๆ getElement * จะยังคงค้นหาองค์ประกอบในเวลาประมาณเดียวกันโดยไม่คำนึงถึงตำแหน่ง


4
โดยคำตอบที่แม่นยำที่สุดในหัวข้อนี้ ควร upvoted มากขึ้น
SeaWarrior404

แม่นยำมากควรใส่ในบล็อกของคุณ Sasha
theking2

25

ฉันมาที่หน้านี้เพื่อค้นหาวิธีที่ดีกว่าที่จะใช้ในแง่ของประสิทธิภาพ - นั่นคือเร็วกว่า:

querySelector / querySelectorAll or getElementsByClassName

และฉันพบสิ่งนี้: https://jsperf.com/getelementsbyclassname-vs-queryselectorall/18

มันรันการทดสอบในตัวอย่าง 2 x ข้างต้นบวกกับ chucks ในการทดสอบสำหรับตัวเลือกที่เทียบเท่าของ jQuery เช่นกัน ผลการทดสอบของฉันมีดังนี้:

getElementsByClassName = 1,138,018 operations / sec - <<< clear winner
querySelectorAll = 39,033 operations / sec
jquery select = 381,648 operations / sec

1
ว้าวมันแตกต่างกันมากขอบคุณที่มองมัน querySelectorAllต้องการงานเพิ่มเติมอย่างชัดเจนหลังฉาก (รวมถึงการแยกวิเคราะห์นิพจน์ของตัวเลือกการบัญชีสำหรับองค์ประกอบที่หลอก ฯลฯ ) ในขณะที่getElementsByClassNameเป็นเพียงการวนซ้ำของวัตถุแบบวนซ้ำ
John Weisz

18

querySelector สามารถเป็น CSS (3) -Selector ที่สมบูรณ์ด้วย ID และ Classes และ Pseudo-Classes ด้วยกันดังนี้:

'#id.class:pseudo'

// or

'tag #id .class .class.class'

ด้วยgetElementByClassNameคุณสามารถกำหนดชั้นเรียนได้

'class'

ด้วยgetElementByIdคุณสามารถกำหนด ID

'id'

1
คือ:firstตัวเลือก CSS, ตอนนี้หรือไม่ :first-classหรือ:first-of-typeอาจจะ แต่ฉันคิดว่า:firstเป็นการเพิ่ม JavaScript / jQuery / Sizzle
เดวิดบอกว่าจะคืนสถานะโมนิก้าเมื่อ

@DavidThomas ใช่มันเป็นส่วนหนึ่งของ CSS3 มันสามารถใช้เช่นนี้: css-tricks.com/almanac/selectors/f/first-child
algorhythm

2
แต่มีอย่างเห็นได้ชัดไม่ได้:first :first-child
เดวิดบอกว่าจะคืนสถานะโมนิก้า

3
"ผู้เขียนได้รับคำแนะนำว่าในขณะที่อนุญาตให้ใช้องค์ประกอบหลอกในตัวเลือกพวกเขาจะไม่ตรงกับองค์ประกอบใด ๆ ในเอกสารและจะไม่ส่งผลให้องค์ประกอบใด ๆ ถูกส่งคืนดังนั้นผู้เขียนจึงควรหลีกเลี่ยงการใช้หลอก - องค์ประกอบในตัวเลือกที่ส่งผ่านไปยังวิธีการที่กำหนดไว้ในข้อกำหนดนี้ " w3.org/TR/selectors-api/#grammar
rich remer

นอกจากนี้ยังมีข้อบกพร่องใน IE (แน่นอน) ที่ทำให้องค์ประกอบคืนค่ารูต html แทนที่จะเป็นรายการองค์ประกอบที่ว่างเปล่าเมื่อเลือกองค์ประกอบหลอก
รวย remer

7

querySelectorและquerySelectorAllเป็น API ที่ค่อนข้างใหม่ในขณะที่getElementByIdและ getElementsByClassNameอยู่กับเรามานานกว่ามาก นั่นหมายความว่าสิ่งที่คุณใช้ส่วนใหญ่จะขึ้นอยู่กับเบราว์เซอร์ที่คุณต้องการสนับสนุน

สำหรับ:มันมันมีความหมายพิเศษดังนั้นคุณต้องหลบมันถ้าคุณต้องใช้มันเป็นส่วนหนึ่งของชื่อ ID / คลาส


13
สิ่งนี้ไม่เป็นความจริง ตัวอย่างเช่นquerySelectorAllมีอยู่ใน IE8 ในขณะที่getElementsByClassNameไม่ใช่
DaveJ

querySelectorAll... โดยทั่วไปทุกอย่าง: caniuse.com/#search=querySelectorAll
dsdsdsdsd


5

querySelectorเป็นของ w3c Selector API

getElementByเป็นของ w3c DOM API

IMO ข้อแตกต่างที่น่าสังเกตมากที่สุดคือชนิดส่งคืนของquerySelectorAllเป็นรายการโหนดแบบสแตติกและgetElementsByเป็นรายการโหนดสด ดังนั้นการวนซ้ำในตัวอย่าง 2 จะไม่สิ้นสุดเพราะlisมีการถ่ายทอดสดและอัพเดตตัวเองในระหว่างการทำซ้ำแต่ละครั้ง

// Demo 1 correct
var ul = document.querySelectorAll('ul')[0],
    lis = ul.querySelectorAll("li");
for(var i = 0; i < lis.length ; i++){
    ul.appendChild(document.createElement("li"));
}

// Demo 2 wrong
var ul = document.getElementsByTagName('ul')[0], 
    lis = ul.getElementsByTagName("li"); 
for(var i = 0; i < lis.length ; i++){
    ul.appendChild(document.createElement("li")); 
}

4

ความแตกต่างระหว่าง "querySelector" และ "querySelectorAll"

//querySelector returns single element
let listsingle = document.querySelector('li');
console.log(listsingle);


//querySelectorAll returns lit/array of elements
let list = document.querySelectorAll('li');
console.log(list);


//Note : output will be visible in Console
<ul>
<li class="test">ffff</li>
<li class="test">vvvv</li>
<li>dddd</li>
<li class="test">ddff</li>
</ul>


2

ดูนี่สิ

https://codepen.io/bagdaulet/pen/bzdKjL

getElementById เร็วกว่า querySelector 25%

jquery ช้าที่สุด

var q = time_my_script(function() {

    for (i = 0; i < 1000000; i++) {
         var w = document.querySelector('#ll');
    }

});

console.log('querySelector: '+q+'ms');

-3

ความแตกต่างที่สำคัญระหว่าง querySelector และ getlementbyID (Claassname, Tagname ฯลฯ ) คือถ้ามีองค์ประกอบมากกว่าหนึ่งองค์ประกอบที่ satifies แบบสอบถามเงื่อนไขตัวเลือกจะส่งกลับเพียงหนึ่งเอาต์พุตในขณะที่ getElementBy * จะส่งกลับองค์ประกอบทั้งหมด

ให้พิจารณาตัวอย่างเพื่อทำให้ชัดเจนยิ่งขึ้น

 <nav id="primary" class="menu">
                            <a class="link" href="#">For Business</a>
                            <a class="link" href="#">Become an Instructor</a>
                            <a class="link" href="#">Mobile Applications</a>
                            <a class="link" href="#">Support</a>
                            <a class="link" href="#">Help</a>
   </nav> 

โค้ดด้านล่างจะอธิบายความแตกต่าง

**QUERY SELECTOR**
document.querySelector('.link'); // Output : For Business (element)

document.querySelectorAll('.link'); //Out All the element with class link

**GET ELEMENT**
document.getElementsByClassName('link') // Output : will return all the element with a class "link" but whereas in query selector it will return only one element which encounters first.

Inshort ถ้าเราต้องการเลือกอิลิเมนต์เดี่ยวสำหรับ queryslector หรือถ้าเราต้องการให้อิลิเมนต์หลายองค์ประกอบเป็น getElement


1
getElementById คืนค่าองค์ประกอบเดียวนี่ไม่ใช่ความแตกต่างระหว่างทั้งสองเลย
Timofey 'Sasha' Kondrashov
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.