ฟังก์ชั่น jQuery หลัง. append


92

วิธีเรียกใช้ฟังก์ชันหลังจาก jQuery .appendเสร็จสมบูรณ์แล้ว?

นี่คือตัวอย่าง:

$("#root").append(child, function(){
   // Action after append is completly done
});

ปัญหา: เมื่อต่อท้ายโครงสร้าง DOM ที่ซับซ้อนการคำนวณขนาดใหม่ขององค์ประกอบรูทภายในการเรียกกลับของฟังก์ชันต่อท้ายจะไม่ถูกต้อง สมมติฐานคือ DOM ในยังโหลดไม่สมบูรณ์มีเพียงลูกแรกของ DOM ที่ซับซ้อนที่เพิ่มเข้ามาเท่านั้น


คุณควรผูกมัดเหตุการณ์ที่ดี append()ใช้เวลาน้อยมาก
Bojangles

ดูstackoverflow.com/q/8840580/176140 (dom 'reflow' บนเบราว์เซอร์ webkit)
schellmax

สิ่งนี้อาจเป็นประโยชน์: stackoverflow.com/a/1489987/1375015
Konstantin Schubert

คำตอบ:


71

คุณมีคำตอบที่ถูกต้องมากมายในที่นี้ แต่ไม่มีคำตอบใดที่บอกคุณได้ว่าทำไมมันถึงใช้งานได้จริง

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

ซึ่งหมายความว่า.appendเมธอดของคุณจะถูกดำเนินการและไม่มีสิ่งอื่นใด (โดยไม่คำนึงถึงการหมดเวลาหรือช่วงเวลาที่อาจมีอยู่) จะดำเนินการจนกว่าเมธอดนั้นจะเสร็จสิ้น

สรุปได้ว่าไม่จำเป็นต้องโทรกลับเนื่องจาก.appendจะทำงานพร้อมกัน


40
ทั้งหมดนี้เป็นความจริง แต่นี่คือปัญหาของฉัน: ฉันต่อท้าย DOM ที่ซับซ้อนและหลังจากผนวกฉันคำนวณขนาดขององค์ประกอบรูทใหม่ แต่ที่นี่ฉันได้หมายเลขผิด และคิดว่าปัญหาคือ: DOM ในยังโหลดไม่สมบูรณ์มีเพียงลูกแรก (.append ("<div id =" child "> <div id =" subChild_with_SIZE "> </div> </div>"))
David Horák

@ จินดาฟขนาดนี้คุณหมายถึงอะไร? จำนวนเด็กความสูงของพ่อแม่หรืออะไรที่คุณต้องคำนวณ?
mekwall

1
@ marcus-ekwall jsfiddle.net/brszE/3เป็นวิธีการเขียนเท่านั้นไม่ใช่รหัสที่ใช้งานได้
David Horák

22
@ DavidHorákเหตุใดจึงเป็นคำตอบที่ได้รับการยอมรับ เป็นเรื่องจริงที่แม้ว่าการเปลี่ยนแปลงจะเกิดขึ้น แต่โค้ดจะไม่ปิดกั้นจนกว่าจะมีการรีโฟลว์ใหม่ทั้งหมดทำให้ประหลาดใจว่ามี 32 โหวตขึ้น! (หรือฉันไม่ได้ทำให้ reflow) บางอย่างเช่นคำตอบ [ที่นี่] ( stackoverflow.com/questions/ 8840580 / … ) อาจเป็นแร่ที่เกี่ยวข้อง
Andreas

17
ฉันเห็นด้วยกับ Andreas คำตอบนี้ไม่ถูกต้อง ปัญหาคือแม้ว่าจะมีการส่งคืนภาคผนวก แต่องค์ประกอบก็ยังไม่สามารถใช้งานได้ใน DOM (ขึ้นอยู่กับเบราว์เซอร์ใช้งานได้ในบางเบราว์เซอร์แตกในตัวอื่น) การใช้การหมดเวลายังไม่รับประกันว่าองค์ประกอบจะอยู่ใน DOM
Severun

21

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

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

$('#root')
  .append(html)
  .ready(function () {
    // enter code here
  });

1
นี่เป็นเรื่องเท็จและไร้ประโยชน์ในทุกสถานการณ์ api.jquery.com/ready
Kevin B

สวัสดีเควินในทางใด?
Daniel - SDS Group

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

ฉันไม่คิดว่าพร้อมจะรอให้เอกสารที่ต่อท้ายโหลด แต่รอให้ DOM โหลดทั้งหมดแทน เป็นเวลาประมาณ 3 ปีแล้วที่ฉันเขียนคำตอบนี้ แต่ฉันคิดว่าฉันแสดงความคิดเห็นเกี่ยวกับนักดำน้ำเงาด้านบนมากกว่า แต่ไม่มีสิ่งอำนวยความสะดวกในการแสดงความคิดเห็นในเวลานั้น
Daniel - SDS Group

1
อันนี้ใช้ได้ดีเกินคาด ดีกว่าคำตอบอื่น ๆ ที่นี่
สีน้ำเงิน

20

ฉันมีปัญหาเดียวกันกับการคำนวณขนาดใหม่และหลังจากปวดหัวมาหลายชั่วโมงฉันต้องไม่เห็นด้วยกับการ.append()ทำงานแบบซิงโครนัสอย่างเคร่งครัด อย่างน้อยก็ใน Google Chrome ดูรหัสต่อไปนี้

var input = $( '<input />' );
input.append( arbitraryElement );
input.css( 'padding-left' );

คุณสมบัติ padding-left ถูกดึงมาอย่างถูกต้องใน Firefox แต่ว่างเปล่าใน Chrome เช่นเดียวกับคุณสมบัติ CSS อื่น ๆ ที่ฉันคิด หลังจากการทดลองบางครั้งฉันต้องตัดสินใจเพื่อตัด CSS 'getter' setTimeout()ด้วยความล่าช้า 10 ms ซึ่งฉันรู้ว่าน่าเกลียดเหมือนนรก แต่มีเพียงอันเดียวที่ทำงานใน Chrome หากคุณมีความคิดที่จะแก้ไขปัญหานี้ด้วยวิธีที่ดีกว่านี้ฉันจะขอบคุณมาก


15
สิ่งนี้ช่วยฉันได้มาก: stackoverflow.com/q/8840580/176140 - หมายถึงผนวก () เป็นซิงโครนัส แต่การอัปเดต DOM ไม่ใช่
schellmax

ในกรณีนี้ setTimeout ไม่น่าเกลียด (เฉพาะ 10ms เท่านั้น) ทุกครั้งที่คุณร้องขอการจัดการ "dom" chrome จะรอให้โค้ดของคุณสิ้นสุดก่อนที่จะดำเนินการ ดังนั้นถ้าคุณเรียก item.hide ตามด้วย item.show มันก็จะไม่ทำอะไรเลย เมื่อฟังก์ชันการดำเนินการของคุณสิ้นสุดลงฟังก์ชันนี้จะใช้การเปลี่ยนแปลงของคุณกับ DOM หากคุณต้องรอการอัปเดต "dom" ก่อนดำเนินการต่อเพียงดำเนินการ CSS / DOM จากนั้นเรียก settimeout (function () {สิ่งที่ต้องทำต่อไป}); settimeout ทำหน้าที่เหมือน "doevents" เก่า ๆ ใน vb6! มันบอกให้เบราว์เซอร์ทำงานรอ (อัปเดต DOM) และกลับไปที่รหัสของคุณหลังจากนั้น
foxontherock

ดูคำตอบที่ใช้ประโยชน์จาก.ready()โซลูชันที่ดีกว่าและสวยงามกว่ามาก
Mark Carpenter Jr

8

ฉันประหลาดใจกับคำตอบทั้งหมดที่นี่ ...

ลองสิ่งนี้:

window.setTimeout(function() { /* your stuff */ }, 0);

หมายเหตุการหมดเวลา 0 ไม่ใช่ตัวเลขตามอำเภอใจ ... อย่างที่ฉันเข้าใจ (แม้ว่าความเข้าใจของฉันอาจจะสั่นคลอนเล็กน้อย) มีคิวเหตุการณ์จาวาสคริปต์สองคิว - คิวหนึ่งสำหรับเหตุการณ์มาโครและอีกหนึ่งสำหรับเหตุการณ์ขนาดเล็ก คิวที่มีขอบเขต "ใหญ่กว่า" เก็บงานที่อัปเดต UI (และ DOM) ในขณะที่คิวขนาดเล็กดำเนินการประเภทงานด่วน

โปรดทราบด้วยว่าการตั้งค่าการหมดเวลาไม่ได้รับประกันว่าโค้ดจะทำงานตรงตามค่าที่ระบุ สิ่งนี้ทำให้ฟังก์ชันอยู่ในคิวที่สูงขึ้นเป็นหลัก (ฟังก์ชันที่จัดการ UI / DOM) และไม่เรียกใช้ก่อนเวลาที่กำหนด

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

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

(ps - ฉันเรียนรู้สิ่งนี้จากSecrects of the JavaScript Ninja - หนังสือที่ยอดเยี่ยม: https://www.manning.com/books/secrets-of-the-javascript-ninja )


ฉันเพิ่มsetTimeout()มาหลายปีแล้วโดยไม่รู้ว่าทำไม ขอบคุณสำหรับคำอธิบาย!
นึ่ง

5

ฉันมีตัวแปรอื่นที่อาจเป็นประโยชน์สำหรับใครบางคน:

$('<img src="http://example.com/someresource.jpg">').load(function() {
    $('#login').submit();
}).appendTo("body");

1
สำหรับคนที่คิดว่าสิ่งนี้เลิกใช้แล้วและถูกลบออกมันถูกต้อง แต่คุณยังสามารถใช้มันได้เช่น...on('load', function(){ ... ดูที่นี่: stackoverflow.com/a/37751413/2008111
caramba

5

ฉันเจอปัญหาเดียวกันและพบวิธีง่ายๆ เพิ่มหลังจากเรียกใช้ฟังก์ชันผนวกเอกสารพร้อม

$("#root").append(child);
$(document).ready(function () {
    // Action after append is completly done
});


ดี. สิ่งนี้ใช้ได้ผลสำหรับฉัน (ฉันต่อท้าย HTML โดยใช้ Handlebars และ JSON และแน่นอนว่าเป็นเอกสารปกติเกิดขึ้นเร็วเกินไปสำหรับทั้งหมดนั้น)
Katharine Osborne

1
@KatharineOsborne นี่ไม่ต่างจาก "document.ready" document.ready ได้รับการเรียกใช้ในสถานการณ์เฉพาะเท่านั้นการใช้งานที่แตกต่างกันไม่ได้ทำให้มันทำงานแตกต่างกัน
Kevin B

เป็นเวลากว่าหนึ่งปีแล้วที่ฉันแสดงความคิดเห็น แต่ IIRC บริบทที่คุณเรียกสิ่งนี้มีความสำคัญ (เช่นในฟังก์ชัน) ตั้งแต่นั้นมารหัสได้ถูกส่งต่อไปยังไคลเอนต์ดังนั้นฉันจึงไม่สามารถเข้าถึงรหัสนี้ได้อีกเพื่อขุดกลับมา
Katharine Osborne

5

การใช้MutationObserverสามารถทำหน้าที่เหมือนการโทรกลับสำหรับappendเมธอดjQuery :

ฉันได้อธิบายไว้ในคำถามอื่นและคราวนี้ฉันจะยกตัวอย่างสำหรับเบราว์เซอร์ที่ทันสมัยเท่านั้น:

// Somewhere in your app:
var observeDOM = (() => {
    var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;

    return function(obj, callback){
        if( MutationObserver ){
            // define a new observer
            var obs = new MutationObserver(function(mutations, observer){
                if( mutations[0].addedNodes.length || mutations[0].removedNodes.length )
                    callback(mutations);
            });
            // have the observer observe foo for changes in children
            obs.observe( obj, { childList:true, subtree:true });

            return obs;
        }
    }
})();

//////////////////
// Your code:

// setup the DOM observer (on the appended content's parent) before appending anything
observeDOM( document.body, ()=>{
    // something was added/removed
}).disconnect(); // don't listen to any more changes

// append something
$('body').append('<p>foo</p>');

+1. คำตอบทั้งหมดของคุณเหมาะสมที่สุด อย่างไรก็ตามมันไม่ได้ผลในกรณีของฉันการเพิ่มแถวต่างๆลงในตารางเพราะมันจะวนซ้ำไม่สิ้นสุด ปลั๊กอินที่จัดเรียงตารางจะเปลี่ยน DOM ดังนั้นมันจะเรียกผู้สังเกตการณ์ครั้งที่ไม่มีที่สิ้นสุด
Azevedo

@Azevedo - ทำไมมันจะไม่มีที่สิ้นสุด? ฉันแน่ใจว่ามัน จำกัด อย่างไรก็ตามคุณสามารถแยกฟังก์ชันการจัดเรียงออกจากแถว "เหตุการณ์" ที่เพิ่มเข้ามาได้โดยใช้ความคิดสร้างสรรค์เล็กน้อย มีหลายวิธี
vsync

เมื่อปลั๊กอินการจัดเรียงจัดเรียงตารางจะทริกเกอร์ผู้สังเกตการณ์ (dom เปลี่ยน) ซึ่งจะเรียกการจัดเรียงอีกครั้ง ตอนนี้ฉันกำลังคิดว่า ... ฉันสามารถนับแถวของตารางและให้ผู้สังเกตการณ์เรียกตัวเรียงลำดับก็ต่อเมื่อมีการเปลี่ยนแปลงตัวนับแถว
Azevedo

3

ฉันคิดว่านี่เป็นคำตอบที่ดี แต่นี่เป็นวิธีที่เฉพาะเจาะจงมากขึ้นในการจัดการ "subChild_with_SIZE" (หากมาจากพาเรนต์ แต่คุณสามารถปรับเปลี่ยนจากที่ที่อาจเป็นได้)

$("#root").append(
    $('<div />',{
        'id': 'child'
    })
)
.children()
.last()
.each(function() {
    $(this).append(
        $('<div />',{
            'id': $(this).parent().width()
        })
    );
});

2

$.when($('#root').append(child)).then(anotherMethod());


8
$.whenใช้ได้เฉพาะกับวัตถุที่ส่งคืนวัตถุสัญญา
Rifat

มันใช้งานได้ แต่เกิดข้อผิดพลาดบนคอนโซล .. "จากนั้นไม่ใช่ฟังก์ชัน"
Karan Datwani

1
ใช้งานได้เพราะคุณกำลังเรียกใช้anotherMethod()ทันที .. มันไม่ได้ถูกส่งต่อเป็นการโทรกลับ
yts

สิ่งนี้ไม่สมเหตุสมผล
Kevin B

1

ฟังก์ชันผนวก Jquery ส่งคืนวัตถุ jQuery เพื่อให้คุณสามารถแท็กเมธอดในตอนท้าย

$("#root").append(child).anotherJqueryMethod();

ฉันต้องการเรียกฉันว่าฟังก์ชัน javascript ที่กำหนดเองหลังจาก. append ฉันต้องการคำนวณขนาดของรูทใหม่
David Horák

คุณสามารถใช้ jQuery แต่ละวิธีได้ แต่การผนวกเป็นวิธีซิงโครนัสดังนั้นคุณควรจะทำทุกอย่างที่คุณต้องการในบรรทัดถัดไป
Dve

@ JinDave คุณต้องคำนวณอะไรใหม่? จำนวนเด็กความสูงของผู้ปกครองหรือขนาดหมายถึงอะไร?
mekwall

0

สำหรับรูปภาพและแหล่งที่มาอื่น ๆ คุณสามารถใช้ได้:

$(el).one('load', function(){
    // completed
}).each(function() {
    if (this.complete)
        $(this).load();
});

0

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

    function processAppended(el){
        //process appended element
    }

    var remove = '<a href="#">remove</a>' ;
    $('li').each(function(){
        $(this).append(remove);   
        processAppended(this);    
    });​

-1

ฉันพบปัญหานี้ขณะเขียนโค้ด HTML5 สำหรับอุปกรณ์เคลื่อนที่ การผสมผสานเบราว์เซอร์ / อุปกรณ์บางอย่างทำให้เกิดข้อผิดพลาดเนื่องจากเมธอด. append () ไม่ได้แสดงถึงการเปลี่ยนแปลงใน DOM ในทันที (ทำให้ JS ล้มเหลว)

วิธีแก้ปัญหาที่รวดเร็วและสกปรกสำหรับสถานการณ์นี้คือ:

var appint = setInterval(function(){
    if ( $('#foobar').length > 0 ) {
        //now you can be sure append is ready
        //$('#foobar').remove(); if elem is just for checking
        //clearInterval(appint)
    }
}, 100);
$(body).append('<div>...</div><div id="foobar"></div>');

ไม่ควรใช้ช่วงเวลาเพื่อรอให้ตัวสร้างโค้ดเสร็จสิ้น มันไม่น่าเชื่อถือโดยสิ้นเชิง
Gabe Hiemstra

-1

ใช่คุณสามารถเพิ่มฟังก์ชันการโทรกลับไปยังการแทรก DOM ใดก็ได้:
$myDiv.append( function(index_myDiv, HTML_myDiv){ //.... return child })

ตรวจสอบเอกสาร JQuery: http://api.jquery.com/append/
และนี่คือตัวอย่างที่ใช้ได้จริงและคล้ายกัน: http://www.w3schools.com/jquery/ tryit.asp? filename = tryjquery_html_prepend_func


การเล่นซอกับตัวอย่าง w3schools คุณสามารถตรวจสอบพารามิเตอร์ที่ 2 โดยแทนที่.prepend()method ด้วย: $ ("p") นำหน้า (function (n, pHTML) {return "<b> this p element </b> (\" <i > "+ pHTML +" </i> \ ") <b> มีดัชนี" + n + "</b>";
Pedro Ferreira

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

@vsync: eactly. คุณไม่เข้าใจคำตอบของฉันอย่างชัดเจน "เด็ก" คือสิ่งที่ต่อท้ายไม่ใช่เมื่อต่อท้าย
Pedro Ferreira

-1

ฉันพบปัญหาที่คล้ายกันเมื่อเร็ว ๆ นี้ ทางออกสำหรับฉันคือสร้าง colletion ขึ้นมาใหม่ ให้ฉันลองสาธิต:

var $element = $(selector);
$element.append(content);

// This Doesn't work, because $element still contains old structure:
$element.fooBar();    

// This should work: the collection is re-created with the new content:
$(selector).fooBar();

หวังว่านี่จะช่วยได้!


มันไม่ได้ช่วยฉัน :(
เพื่อน

-1

ใน jquery คุณสามารถใช้หลังจากผนวก

$(function(){
//code that needs to be executed when DOM is ready, after manipulation
});

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

คุณสามารถค้นหาความ
แตกต่างระหว่าง $ และ $ () เพิ่มเติมได้ที่นี่ใน jQuery
http://api.jquery.com/ready/


-2

ฉันรู้ว่ามันไม่ใช่ทางออกที่ดีที่สุด แต่เป็นแนวทางปฏิบัติที่ดีที่สุด:

$("#root").append(child);

setTimeout(function(){
  // Action after append
},100);

8
ฉันไม่คิดว่านี่เป็นแนวทางปฏิบัติที่ดี 100 เป็นหมายเลขที่กำหนดเองและเหตุการณ์อาจใช้เวลานานกว่านั้น
JasTonAChair

1
ผิดทางไปเลย อาจไม่ได้ใช้เวลา 100ms เสมอไป
axelvnk

ดูคำตอบของฉันที่นี่สำหรับคำอธิบายว่าเหตุใดจึงใช้งานได้ (ตัวเลขสามารถ / ควรเป็น 0 ไม่ใช่ 100): stackoverflow.com/questions/6068955/…
jleach

1
อย่างน้อยก็เป็นแนวทางปฏิบัติที่ดีกว่าคำตอบที่นี่โดยใช้. พร้อม
Kevin B

-4
$('#root').append(child);
// do your work here

Append ไม่มีการเรียกกลับและนี่คือรหัสที่ดำเนินการพร้อมกัน - ไม่มีความเสี่ยงที่จะไม่ทำ


3
ฉันเห็นความคิดเห็นแบบนี้มากเกินไปและไม่เป็นประโยชน์ @david ล้มเหลวใช่ JS IS ซิงโครไนซ์ แต่ DOM ต้องใช้เวลาในการอัปเดต หากคุณต้องการปรับแต่งองค์ประกอบที่ต่อท้าย DOM ของคุณ แต่องค์ประกอบยังไม่ได้รับการแสดงผลลงใน DOM ของคุณแม้ว่าการต่อท้ายจะเสร็จสิ้นการปรับแต่งใหม่ของคุณจะไม่ทำงาน (ตัวอย่างเช่น: $ ('# element'). on ().
NoobishPro

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