jQuery $ (เอกสาร). Ready และ UpdatePanels?


475

ฉันใช้ jQuery เพื่อเชื่อมโยงเอฟเฟกต์เมาส์โอเวอร์กับองค์ประกอบที่อยู่ใน UpdatePanel $(document).readyเหตุการณ์ที่เกิดขึ้นมีความผูกพันใน ตัวอย่างเช่น:

$(function() {    
    $('div._Foo').bind("mouseover", function(e) {
        // Do something exciting
    });    
});

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

อะไรคือแนวทางที่แนะนำสำหรับการวางสายใน jQuery ไม่เพียง แต่ในการโหลดหน้าแรก แต่ทุกครั้งที่ UpdatePanel ทำการอัปเดตหน้าเว็บบางส่วน ฉันควรใช้วงจร ajax ASP.NET แทน$(document).readyหรือไม่?


ลองดูสิฉันคิดว่ามันจะแก้ปัญหาของคุณcodeproject.com/Articles/21962/ …
Baxterboom

1
คล้ายกับคำถามนี้: stackoverflow.com/questions/301473/…
ต่อHornshøj-Schierbeck

คำตอบ:


545

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

สิ่งที่ฉันทำเพื่อหลีกเลี่ยงปัญหานี้คือการสมัครสมาชิกใหม่กับกิจกรรมที่ฉันต้องการหลังจากอัพเดททุกครั้ง ฉันใช้$(document).ready()สำหรับการโหลดครั้งแรกจากนั้นใช้ Microsoft PageRequestManager(มีให้หากคุณมีแผงการอัปเดตในหน้าของคุณ) เพื่อสมัครรับข้อมูลใหม่ทุกการอัปเดต

$(document).ready(function() {
    // bind your jQuery events here initially
});

var prm = Sys.WebForms.PageRequestManager.getInstance();

prm.add_endRequest(function() {
    // re-bind your jQuery events here
});

PageRequestManagerเป็นวัตถุจาวาสคริปต์ที่สามารถใช้ได้โดยอัตโนมัติหากแผงปรับปรุงอยู่บนหน้า คุณไม่จำเป็นต้องทำอะไรนอกจากรหัสด้านบนเพื่อใช้งานตราบใดที่ UpdatePanel อยู่ในหน้า

หากคุณต้องการการควบคุมอย่างละเอียดมากขึ้นเหตุการณ์นี้ส่งผ่านข้อโต้แย้งที่คล้ายกับว่าเหตุการณ์. NET ผ่านการขัดแย้งกันอย่างไร (sender, eventArgs)เพื่อให้คุณเห็นว่าเหตุการณ์ที่เกิดขึ้นนั้นเกิดขึ้นและผูกใหม่ได้เฉพาะเมื่อจำเป็น

นี่คือเอกสารเวอร์ชันล่าสุดจาก Microsoft: msdn.microsoft.com/.../bb383810.aspx


ตัวเลือกที่ดีคุณอาจจะต้องขึ้นอยู่กับความต้องการของคุณคือการใช้ของ .on()jQuery วิธีการเหล่านี้มีประสิทธิภาพมากกว่าการสมัครสมาชิกองค์ประกอบ DOM ใหม่ทุกครั้งที่อัพเดท อ่านเอกสารทั้งหมดก่อนที่คุณจะใช้วิธีการนี้เนื่องจากอาจเป็นไปตามที่คุณต้องการ มีปลั๊กอิน jQuery จำนวนมากที่ไม่เหมาะสมในการใช้ refactor .delegate()หรือ.on()ดังนั้นในกรณีเหล่านี้คุณควรสมัครสมาชิกใหม่


1
โปรดแม่นยำยิ่งขึ้นเมื่อคุณให้คำอธิบาย ฉันหมายถึงตัวอย่างของการใช้งานนั้นดีกว่าโค้ดที่กระจัดกระจายมาก ดูคำอธิบายของ Brian MacKay มันสมบูรณ์แบบ!
จริงผู้อ่าน

3
@Dan, ณ jQuery 1.7, .delegate()ถูกแทนที่โดย.on()
Gabriele Petrioli

@ GabyakaG.Petrioli ฉันรู้ว่ามันถูกแทนที่แล้ว แต่เนื่องจากคำตอบนี้ดูเหมือนจะแก้ปัญหาทั่วไปฉันต้องการเพิ่มความเข้ากันได้สูงสุดกับ jQuery เวอร์ชันเก่าที่ยังไม่ได้อัปเกรด คำตอบของฉันแนะนำก่อนหน้านี้.live(...)ซึ่งได้เลิกอย่างเป็นทางการใน 1.7 และอาจถูกลบออกในรุ่นอนาคต ฉันเลือกอย่างตั้งใจ.delegate()เนื่องจากได้รับการสนับสนุนใน jQuery มาระยะหนึ่งแล้วและไม่น่าจะถูกลบออกเร็ว ๆ นี้ อย่างไรก็ตามฉันจะอัปเกรดคำตอบเพื่อพูดถึง.on()เช่นกันสำหรับผู้ที่สามารถใช้งานได้
Dan Herbert

12
เป็นการดีกว่าที่จะประกาศและวางสาย prm ภายใน $ (document). ready เนื่องจากเป็นไปได้ที่ Sys ยังไม่ได้ประกาศ (ขึ้นอยู่กับตำแหน่งของสคริปต์)
drake7707

1
@AhmedSamy ไม่แนะนำให้ใช้jQuery.delegate()อีกต่อไป มีการแทนที่ด้วยjQuery.on()ซึ่งเป็น API ที่ต้องการใช้ delegate()เป็นเสื้อคลุมสำหรับใช้โดยเฉพาะon()และเป็นไปได้ว่าอาจเลิกใช้ในอนาคต ความคิดเห็นก่อนหน้าของฉันdelegate()ถูกกล่าวถึงถูกเขียนเมื่อปีที่แล้วเมื่อ jQuery 1.7 (ซึ่งแนะนำon()API) ได้รับการเผยแพร่ใหม่ ด้วย jQuery 1.9 ที่ออกมาแล้วและ 2.0 ในเบต้ากำลังเตรียมวางจำหน่ายจึงเป็นความคิดที่ดีที่จะหลีกเลี่ยงการใช้delegate()ในโค้ดใหม่
Dan Herbert

159
<script type="text/javascript">

        function BindEvents() {
            $(document).ready(function() {
                $(".tr-base").mouseover(function() {
                    $(this).toggleClass("trHover");
                }).mouseout(function() {
                    $(this).removeClass("trHover");
                });
         }
</script>

พื้นที่ที่กำลังจะได้รับการปรับปรุง

<asp:UpdatePanel...
<ContentTemplate
     <script type="text/javascript">
                    Sys.Application.add_load(BindEvents);
     </script>
 *// Staff*
</ContentTemplate>
    </asp:UpdatePanel>

ทำงานได้ดีสำหรับ asp: GridView TextBox ข้างใน <EditItemTemplate>
Dan Randolph

ปัญหาเกี่ยวกับวิธีแก้ปัญหานี้คือในขณะที่ใช้งานได้มันจะฆ่าโพสต์แบบอะซิงโครนัสของ UpdatePanel ซึ่งทำให้ UpdatePanel ไร้ประโยชน์อีกครั้ง (ถอนหายใจ)
MC9000

63

การควบคุมผู้ใช้ด้วย jQuery ภายใน UpdatePanel

นี่ไม่ใช่คำตอบที่ตรงกับคำถาม แต่ฉันได้รวมโซลูชันนี้เข้าด้วยกันโดยการอ่านคำตอบที่ฉันพบที่นี่และฉันคิดว่าบางคนอาจพบว่ามีประโยชน์

ฉันพยายามใช้ตัว จำกัด ข้อความ jQuery textarea ภายในการควบคุมผู้ใช้ สิ่งนี้เป็นเรื่องยากเนื่องจากการควบคุมผู้ใช้ทำงานภายใน UpdatePanel และสูญเสียการเชื่อมโยงกับการติดต่อกลับ

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

ฉันลงเอยด้วยการวางบล็อกสคริปต์นี้ไว้ด้านบนของมาร์กอัปการควบคุมผู้ใช้ของฉัน สำหรับการผูกเริ่มต้นจะใช้ $ (เอกสาร). ready แล้วใช้ prm.add_endRequest จากที่นั่น:

<script type="text/javascript">
    function BindControlEvents() {
        //jQuery is wrapped in BindEvents function so it can be re-bound after each callback.
        //Your code would replace the following line:
            $('#<%= TextProtocolDrugInstructions.ClientID %>').limit('100', '#charsLeft_Instructions');            
    }

    //Initial bind
    $(document).ready(function () {
        BindControlEvents();
    });

    //Re-bind for callbacks
    var prm = Sys.WebForms.PageRequestManager.getInstance(); 

    prm.add_endRequest(function() { 
        BindControlEvents();
    }); 

</script>

ดังนั้น ... แค่คิดว่าใครบางคนอาจชอบที่จะรู้ว่าสิ่งนี้ได้ผล


2
ฉันไม่สามารถทำสิ่งนี้เพื่อชีวิตของฉัน จากนั้นฉันก็ขยับจากหัวถึงท้ายกระดาษและใช้งานได้ ไม่ใช่เชิงบวก แต่ฉันคิดว่ามันต้องมาหลังจาก ScriptManager ที่ฉันใช้อยู่
Adam Youngers

ในกรณีของฉันฉันกำลังเพิ่มสคริปต์โดยใช้ ScriptManager.RegisterClientScriptBlock ซึ่งเพิ่มสคริปต์ก่อนที่จะมีการกำหนด Sys ดังนั้นฉันจึงลงเอยด้วยการใช้ RegisterStartupScript แทน (ดูความแตกต่างที่นี่ )
Firas Assaad

2
คำตอบที่ยอดเยี่ยม! มันทำงานได้อย่างมีเสน่ห์ในเว็บแอป asp.net ของฉัน + สำหรับคุณ
Pranesh Janarthanan

33

อัปเกรดเป็น jQuery 1.3 และใช้:

$(function() {

    $('div._Foo').live("mouseover", function(e) {
        // Do something exciting
    });

});

หมายเหตุ: การแสดงสดใช้งานได้กับเหตุการณ์ส่วนใหญ่ แต่ไม่ใช่ทั้งหมด มีรายชื่อทั้งหมดในเอกสาร


การใช้งานสดแก้ไขปัญหาด้วยแผงการอัพเดท ไม่จำเป็นต้องใช้ห่วงเพื่อกระโดดข้าม
Tim Scarborough

สิ่งที่เกี่ยวกับสิ่งที่ต้องเกิดขึ้นในเจดีย์โหลด? ตัวอย่างเช่นตารางแถบม้าลาย $ ('ตาราง TR: nth-child (คี่)'). addClass ('alt-row');
Adam Youngers

3
เพิ่งอัปเดตเนื่องจาก jQuery 1.7 แนะนำให้ใช้. on () แทน
George

1
เพิ่งพบข้อผิดพลาดร้ายแรงในขณะที่ใช้live()วิธีการใน IE7 (โครงการ jQuery 1.5.1 / VS2010) เมื่อใช้live()ภายใน UpdatePanel ใน v3.5 ASP.NET ปกติAutoPostbackของ<asp:DropDownList />สาเหตุ 2 postbacks บางส่วนเมื่อเทียบกับที่คาดว่า 1. postback พิเศษที่ออกโดยเบราว์เซอร์ขาดเนื้อหา (ยกเลิก) ที่ก่อให้เกิดPage.IsPostBackจะเป็นfalse(ซึ่งฆ่ารัฐภายในขอบเขตของฉัน ควบคุม) ฉันพบผู้ร้ายว่าเป็นวิธีการถ่ายทอดสด () ฉันรู้ว่าการโพสต์ครั้งที่ 1 จะถูกยกเลิกโดย UpdatePanel ต่อสเป็ค แต่ถ้ามีอีกอันในคิวที่ไม่มี
timmi4sa

20

คุณสามารถลอง:

<asp:UpdatePanel runat="server" ID="myUpdatePanel">
    <ContentTemplate>

        <script type="text/javascript" language="javascript">
        function pageLoad() {
           $('div._Foo').bind("mouseover", function(e) {
               // Do something exciting
           });
        }
        </script>

    </ContentTemplate>
</asp:UpdatePanel>

เนื่องจาก pageLoad () เป็นเหตุการณ์ ajax ASP.NET ซึ่งจะดำเนินการทุกครั้งที่โหลดหน้าเว็บที่ฝั่งไคลเอ็นต์


pageLoad เหตุการณ์ที่ซ้ำกันในทุก ๆ ASync postback ของ Update Panel
Zo มี

17

คำตอบของฉัน?

function pageLoad() {

  $(document).ready(function(){

เป็นต้น

ทำงานเหมือนมีมนต์เสน่ห์ซึ่งโซลูชันอื่น ๆ จำนวนหนึ่งล้มเหลวอย่างน่าสังเวช


เหมาะสำหรับการปรับขนาดของ textareas ใน Listview ของฉันใน UpdatePanel ใน UserControl ของฉันตามปริมาณข้อความ
ทรัพยากร

ฉันพยายามทำให้ฟังก์ชั่น dom jquery ทำงานอย่างถูกต้องใน postback ด้วยแผงการปรับปรุงวิธีแก้ปัญหานี้ใช้งานได้และฉันไม่จำเป็นต้องทำการเรียกฟังก์ชั่นซ้ำใน 2 สถานการณ์ สิ่งนี้ทำให้ฟังก์ชั่น jquery และผู้ฟังใช้ได้ หมายเหตุฉันใส่สคริปต์และ jquery library ทั้งหมดของฉันไว้ข้างหน้าแท็ก FORM [สิ้นสุด] ที่ด้านล่างของหน้า ขอบคุณ !!!
moto_geek

7

ฉันจะใช้หนึ่งในวิธีต่อไปนี้:

  1. สรุปเหตุการณ์ผูกพันในฟังก์ชั่นและเรียกใช้มันทุกครั้งที่คุณปรับปรุงหน้า คุณสามารถมีเหตุการณ์ที่มีผลผูกพันกับองค์ประกอบที่เฉพาะเจาะจงเพื่อที่จะไม่ผูกเหตุการณ์หลายครั้งกับองค์ประกอบเดียวกัน

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


7

สิ่งนี้ใช้ได้กับฉัน:

$(document).ready(function() {

    // Do something exciting

    var prm = Sys.WebForms.PageRequestManager.getInstance();

    prm.add_endRequest(function() {
        // re-bind your jQuery events here
    });

});

6

ฟังก์ชั่น pageLoad () อันตรายมากที่จะใช้ในสถานการณ์นี้ คุณสามารถให้กิจกรรมกลายเป็นแบบมีสายหลายครั้ง ฉันจะอยู่ห่างจาก. live () ตามที่แนบกับองค์ประกอบเอกสารและต้องข้ามทั้งหน้า (ช้าและเส็งเคร็ง)

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


6

เมื่อ$(document).ready(function (){...})ไม่ทำงานหลังจากโพสต์หน้ากลับมาให้ใช้ฟังก์ชัน JavaScript pageLoad ใน Asp.page ดังนี้

<script type="text/javascript" language="javascript">
function pageLoad() {
// Initialization code here, meant to run once. 
}
</script>

ใช่ตรวจสอบ isPartialLoad เช่นกัน: stackoverflow.com/questions/301473//
Steve Greene

4

ฉันมีปัญหาที่คล้ายกันและพบว่าวิธีที่ได้ผลดีที่สุดคือการพึ่งพา Event Bubbling และการมอบหมายกิจกรรมให้จัดการ สิ่งที่ดีเกี่ยวกับการมอบหมายกิจกรรมคือเมื่อตั้งค่าแล้วคุณไม่จำเป็นต้องเชื่อมโยงเหตุการณ์อีกครั้งหลังจากอัปเดต AJAX

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

มีบทความและปลั๊กอินที่ดีจำนวนหนึ่งที่จะจัดการกับการมอบหมายกิจกรรมใน jQuery และฟีเจอร์นั้นน่าจะถูกนำไปใช้ในรุ่น 1.3 บทความ / ปลั๊กอินที่ฉันใช้สำหรับการอ้างอิงคือ:

http://www.danwebb.net/2008/2/8/event-delegation-made-easy-in-jquery

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


4

FWIW ฉันพบปัญหาเดียวกันกับ woot mootools การแนบกิจกรรมของฉันอีกครั้งเป็นการย้ายที่ถูกต้อง แต่จำเป็นต้องทำในตอนท้ายของคำขอ ..

var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_endRequest(function() {... 

สิ่งที่ควรคำนึงถึงถ้า startRequest ทำให้คุณได้รับข้อยกเว้นการอ้างอิง JS แบบ null

ไชโย



3
pageLoad = function () {
    $('#div').unbind();
    //jquery here
}

ฟังก์ชัน pageLoad นั้นสมบูรณ์แบบสำหรับเคสนี้เนื่องจากรันบนการโหลดหน้าเริ่มต้นและ postback updatepanel async ทุกอัน ฉันเพียงแค่ต้องเพิ่มวิธีการยกเลิกการผูกไว้เพื่อให้ jQuery ทำงานบน postpanel updatepanel

http://encosia.com/document-ready-and-pageload-are-not-the-same/


2

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

ในกรณีของฉันฉันมีการควบคุมผู้ใช้ภายในหน้า เพียงวางรหัสด้านล่างในการควบคุมผู้ใช้ของคุณ

<script type="text/javascript"> 
        var prm = Sys.WebForms.PageRequestManager.getInstance();
    prm.add_endRequest(EndRequestHandler);
    function EndRequestHandler(sender, args) {
        if (args.get_error() == undefined) {
            UPDATEPANELFUNCTION();
        }                   
    }

    function UPDATEPANELFUNCTION() {
        jQuery(document).ready(function ($) {
            /* Insert all your jQuery events and function calls */
        });
    }

    UPDATEPANELFUNCTION(); 

</script>

2

แผงการอัปเดตจะแทนที่ Jquery ของคุณด้วยสคริปต์ของ inbuilt Scriptmanager ทุกครั้งที่โหลด จะดีกว่าถ้าคุณใช้วิธีการอินสแตนซ์ของ pageRequestManager เช่นนี้ ...

Sys.WebForms.PageRequestManager.getInstance().add_endRequest(onEndRequest)
    function onEndRequest(sender, args) {
       // your jquery code here
      });

มันจะทำงานได้ดี ...


โพสต์เก่า แต่หนึ่งซึ่งช่วยฉันออก สิ่งหนึ่งที่ฉันต้องการเพิ่มในนี้: ตำแหน่งของรหัส add_endRequest จะต้องอยู่ใน UpdatePanel เช่น Sys.WebForms.PageRequestManager มีให้เฉพาะจุดนั้น อย่างไรก็ตามฟังก์ชั่นจริงไม่จำเป็นต้องอยู่ในตำแหน่งนั้น ดังนั้นในกรณีของฉันฉันมีไฟล์ script.js ซึ่งมีรหัสที่ฉันต้องการผูกไว้ภายในฟังก์ชั่น ในไฟล์ script.js นั้นฉันมีการเรียก document.ready ซึ่งจะเรียกใช้ฟังก์ชันข้างต้น จากนั้นใน UpdatePanel ของฉันฉันมีรหัส add_endRequest ซึ่งยังเรียกใช้ฟังก์ชันข้างต้น
Kiran Ramaswamy

1

ใช้สคริปต์ด้านล่างและเปลี่ยนเนื้อหาของสคริปต์ตามนั้น

       <script>
        //Re-Create for on page postbacks
        var prm = Sys.WebForms.PageRequestManager.getInstance();
        prm.add_endRequest(function () {
           //your codes here!
        });
    </script>

0

ในการตอบสนองต่อคำตอบของ Brian MacKay:

ฉันฉีด JavaScript ลงในหน้าของฉันผ่าน ScriptManager แทนที่จะใส่ลงใน HTML ของ UserControl โดยตรง ในกรณีของฉันฉันต้องเลื่อนไปยังแบบฟอร์มที่ปรากฏให้เห็นหลังจาก UpdatePanel เสร็จสิ้นและส่งคืนแล้ว สิ่งนี้จะอยู่ในโค้ดด้านหลังไฟล์ ในตัวอย่างของฉันฉันได้สร้างตัวแปรprmในหน้าเนื้อหาหลักแล้ว

private void ShowForm(bool pShowForm) {
    //other code here...
    if (pShowForm) {
        FocusOnControl(GetFocusOnFormScript(yourControl.ClientID), yourControl.ClientID);
    }
}

private void FocusOnControl(string pScript, string pControlId) {
    ScriptManager.RegisterStartupScript(this.Page, this.Page.GetType(), "focusControl_" + pControlId, pScript, true);
}

/// <summary>
/// Scrolls to the form that is made visible
/// </summary>
/// <param name="pControlId">The ClientID of the control to focus on after the form is made visible</param>
/// <returns></returns>
private string GetFocusOnFormScript(string pControlId) {
    string script = @"
    function FocusOnForm() {
        var scrollToForm = $('#" + pControlId + @"').offset().top;
        $('html, body').animate({ 
            scrollTop: scrollToForm}, 
            'slow'
        );
        /* This removes the event from the PageRequestManager immediately after the desired functionality is completed so that multiple events are not added */
        prm.remove_endRequest(ScrollFocusToFormCaller);
    }
    prm.add_endRequest(ScrollFocusToFormCaller);
    function ScrollFocusToFormCaller(sender, args) {
        if (args.get_error() == undefined) {
            FocusOnForm();
        }
    }";
    return script;
}

0
Sys.Application.add_load(LoadHandler); //This load handler solved update panel did not bind control after partial postback
function LoadHandler() {
        $(document).ready(function () {
        //rebind any events here for controls under update panel
        });
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.