Hàm jQuery sau .append


90

Làm thế nào để gọi một hàm sau khi jQuery .appendhoàn tất?

Đây là một ví dụ:

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

Vấn đề: Khi một cấu trúc DOM phức tạp được thêm vào, việc tính toán kích thước mới của phần tử gốc trong lệnh gọi lại hàm append là sai. Các giả định là DOM trong vẫn chưa được tải hoàn toàn, chỉ có con đầu tiên của DOM phức tạp được thêm vào.


Bạn nên xâu chuỗi các sự kiện; append()mất rất ít thời gian.
Bojangle vào

thấy stackoverflow.com/q/8840580/176140 (dom 'reflow' trên các trình duyệt webkit)
schellmax

Điều này có thể hữu ích: stackoverflow.com/a/1489987/1375015
Konstantin Schubert

Câu trả lời:


70

Bạn có nhiều câu trả lời hợp lệ ở đây nhưng không có câu trả lời nào thực sự cho bạn biết tại sao nó hoạt động như vậy.

Trong JavaScript, các lệnh được thực thi từng lần một, đồng bộ theo thứ tự mà chúng đến, trừ khi bạn nói rõ ràng chúng là không đồng bộ bằng cách sử dụng thời gian chờ hoặc khoảng thời gian.

Điều này có nghĩa là .appendphương thức của bạn sẽ được thực thi và không có gì khác (bất kể thời gian chờ hoặc khoảng thời gian tiềm ẩn nào có thể tồn tại) sẽ thực thi cho đến khi phương thức đó hoàn thành công việc của nó.

Tóm lại, không cần gọi lại vì .appendnó sẽ được chạy đồng bộ.


39
Tất cả đều đúng, nhưng đây là vấn đề của tôi: Tôi nối thêm DOM phức tạp và ngay sau khi nối, tôi tính kích thước mới của phần tử gốc, nhưng ở đây tôi đã nhầm số. Và hãy nghĩ rằng, vấn đề là ở đây: DOM trong vẫn chưa được tải đầy đủ, chỉ có con đầu tiên (.append ("<div id =" child "> <div id =" subChild_with_SIZE "> </div> </div>"))
David Horák

@JinDave, Bạn đang đề cập đến kích thước này là bao nhiêu? Đó là số lượng con cái, chiều cao của cha mẹ hay những gì bạn cần tính toán?
mekwall

1
@ marcus-ekwall jsfiddle.net/brszE/3 đó chỉ là phương pháp viết, không phải mã hoạt động,
David Horák

22
@ DavidHorák vậy tại sao đây là câu trả lời được chấp nhận? Đúng là mặc dù thay đổi xảy ra nhưng mã sẽ không chặn cho đến khi chỉnh lại hoàn toàn, khá ngạc nhiên là điều này có 32 phiếu bầu tăng lên! (Hoặc nó thậm chí không gây ra chỉnh lại) một cái gì đó giống như các câu trả lời [tại đây] ( stackoverflow.com/questions/ 8840580 /… ) có thể liên quan đến quặng.
Andreas

17
Tôi đồng ý với Andreas, câu trả lời này không đúng. Vấn đề là mặc dù append đã trả về, phần tử vẫn có thể không có sẵn trong DOM (phụ thuộc vào trình duyệt, hoạt động trong một số ngắt trình duyệt ở những trình duyệt khác). Sử dụng thời gian chờ vẫn không đảm bảo rằng phần tử sẽ nằm trong DOM.
Severun

19

Tôi cũng gặp phải vấn đề tương tự với việc tính toán lại kích thước và sau nhiều giờ đau đầu, tôi phải không đồng ý với việc .append()cư xử đồng bộ nghiêm ngặt. Ít nhất là trong Google Chrome. Xem đoạn mã sau.

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

Thuộc tính padding-left được truy xuất chính xác trong Firefox nhưng nó trống trong Chrome. Giống như tất cả các thuộc tính CSS khác mà tôi cho là. Sau một số thử nghiệm, tôi đã phải giải quyết việc đưa CSS 'getter' vào setTimeout()với độ trễ 10 mili giây mà tôi biết là CỰC KỲ ÍT nhưng là cái duy nhất hoạt động trong Chrome. Nếu ai trong số các bạn có ý tưởng về cách giải quyết vấn đề này theo cách tốt hơn, tôi rất biết ơn.


15
điều này đã giúp tôi rất nhiều: stackoverflow.com/q/8840580/176140 - ý nghĩa, append () LÀ đồng bộ, nhưng bản cập nhật DOM không phải là
schellmax

Trong trường hợp đó, setTimeout không xấu (chỉ có 10ms). Mỗi khi bạn yêu cầu thao tác "dom", chrome sẽ đợi mã của bạn kết thúc trước khi thực thi. Vì vậy, nếu bạn gọi item.hide theo sau là item.show, nó sẽ không làm gì cả. Khi chức năng thực thi của bạn kết thúc, thì nó sẽ áp dụng các thay đổi của bạn cho DOM. Nếu bạn cần đợi bản cập nhật "dom" trước khi tiếp tục, chỉ cần thực hiện hành động CSS / DOM, sau đó gọi settimeout (function () {what to do next}) ;. Thời gian chờ hoạt động giống như một "doevents" cũ tốt trong vb6! Nó yêu cầu trình duyệt thực hiện các tác vụ chờ đợi (cập nhật DOM) và quay lại mã của bạn sau đó.
foxontherock

Xem câu trả lời làm cho việc sử dụng .ready()giải pháp tốt hơn và thanh lịch hơn nhiều.
Mark Carpenter Jr

19

Mặc dù Marcus Ekwall hoàn toàn đúng về tính đồng bộ của append, tôi cũng nhận thấy rằng trong những tình huống kỳ lạ, đôi khi DOM không được trình duyệt hiển thị hoàn toàn khi dòng mã tiếp theo chạy.

Trong trường hợp này, các giải pháp shadowdiver sẽ đi đúng hướng - với việc sử dụng .ready - tuy nhiên việc xâu chuỗi lệnh gọi đến phần phụ ban đầu của bạn sẽ gọn gàng hơn rất nhiều.

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

Điều này hoàn toàn sai lầm và vô ích trong mọi tình huống. api.jquery.com/ready
Kevin B

Chào Kevin, theo cách nào?
Daniel - Nhóm SDS

hoàn toàn không mang lại lợi ích gì khi sử dụng. ready để chờ các phần tử được nối thêm để “hiển thị” bất kỳ điều gì hơn là bạn sẽ nhận được từ việc sử dụng settimeout, nếu và chỉ khi tài liệu chưa sẵn sàng. nếu tài liệu sẵn sàng, mã sẽ được chạy đồng bộ, do đó không có tác động hữu ích.
Kevin B

Tôi không nghĩ rằng chế độ sẵn sàng sẽ đợi tải tài liệu được nối thêm mà thay vào đó hãy đợi toàn bộ DOM tải. Đã khoảng 3 năm kể từ khi tôi viết câu trả lời này nhưng tôi nghĩ rằng tôi đã bình luận nhiều hơn về người lặn ở trên, nhưng không có cơ sở để bình luận vào thời điểm đó.
Daniel - Nhóm SDS

1
Trên thực tế, điều này hoạt động tốt hơn mong đợi. Tốt hơn bất kỳ câu trả lời nào khác ở đây.
Blue

8

Tôi ngạc nhiên về tất cả các câu trả lời ở đây ...

Thử cái này:

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

Lưu ý thời gian chờ 0. Đó không phải là một con số tùy ý ... như tôi hiểu (mặc dù sự hiểu biết của tôi có thể hơi sai), có hai hàng đợi sự kiện javascript - một cho các sự kiện vĩ mô và một cho các sự kiện vi mô. Hàng đợi có phạm vi "lớn hơn" chứa các tác vụ cập nhật giao diện người dùng (và DOM), trong khi hàng đợi vi mô thực hiện các thao tác loại tác vụ nhanh.

Cũng nhận ra rằng việc đặt thời gian chờ không đảm bảo rằng mã hoạt động chính xác ở giá trị được chỉ định đó. Những gì điều này làm về cơ bản là đặt hàm vào hàng đợi cao hơn (hàng xử lý UI / DOM) và không chạy nó trước thời gian đã chỉ định.

Điều này có nghĩa là việc đặt thời gian chờ bằng 0 sẽ đưa nó vào phần UI / DOM của hàng đợi sự kiện của javascript, để được chạy vào cơ hội có thể tiếp theo.

Điều này có nghĩa là DOM được cập nhật với tất cả các mục hàng đợi trước đó (chẳng hạn như được chèn qua $.append(...); và khi mã của bạn chạy, DOM hoàn toàn có sẵn.

(ps - Tôi học được điều này từ Secrects of the JavaScript Ninja - một cuốn sách tuyệt vời: https://www.manning.com/books/secrets-of-the-javascript-ninja )


Tôi đã thêm setTimeout()nhiều năm mà không biết tại sao. Cảm ơn vì lời giải thích!
steampowered

5

Tôi có một biến thể khác có thể hữu ích cho ai đó:

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

1
Đối với những suy nghĩ này không được chấp và loại bỏ, nó là đúng, nhưng bạn vẫn có thể sử dụng nó như ...on('load', function(){ ... nhìn thấy ở đây: stackoverflow.com/a/37751413/2008111
Caramba

5

Tôi đã gặp cùng một vấn đề và đã tìm ra một giải pháp đơn giản. Thêm sau khi gọi hàm append, một tài liệu đã sẵn sàng.

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


Đẹp. Điều này đã hiệu quả với tôi (tôi đang bổ sung HTML bằng Handlebars và JSON và tất nhiên là một tài liệu thông thường. Đã xảy ra quá sớm cho tất cả những điều đó).
Katharine Osborne,

1
@KatharineOsborne Đây không khác gì một "document.ready". document.ready chỉ được gọi trong một kịch bản cụ thể, việc sử dụng nó theo cách khác sẽ không làm cho nó hoạt động khác.
Kevin B

Vâng, đã hơn một năm kể từ khi tôi để lại nhận xét, nhưng IIRC, bối cảnh nơi bạn gọi điều này là quan trọng (tức là trong một hàm). Mã này đã được chuyển cho một khách hàng nên tôi không có quyền truy cập vào nó nữa để đào lại.
Katharine Osborne

5

Việc sử dụng MutationObservercó thể hoạt động giống như một cuộc gọi lại cho appendphương thức jQuery :

Tôi đã giải thích nó trong một câu hỏi khác và lần này tôi sẽ chỉ đưa ra ví dụ cho các trình duyệt hiện đại:

// 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. Trong tất cả các câu trả lời của bạn phù hợp nhất. Tuy nhiên, nó không hoạt động trong trường hợp của tôi, thêm nhiều hàng khác nhau vào một bảng, vì nó đi vào một vòng lặp vô hạn. Plugin sắp xếp bảng sẽ thay đổi DOM nên nó sẽ gọi trình quan sát là vô hạn.
Azevedo

@Azevedo - tại sao nó sẽ là vô hạn? tôi chắc chắn rằng nó là hữu hạn. dù sao đi nữa, bạn có thể tách chức năng sắp xếp khỏi một "sự kiện" hàng đã thêm bằng cách sáng tạo một chút. có những con đường.
vsync

Khi plugin sắp xếp sắp xếp bảng, nó sẽ kích hoạt trình quan sát (dom đã thay đổi), điều này sẽ kích hoạt lại sắp xếp. Bây giờ tôi đang nghĩ ... Tôi có thể đếm các hàng trong bảng và làm cho người quan sát gọi bộ sắp xếp chỉ khi bộ đếm hàng được thay đổi.
Azevedo

3

Tôi nghĩ điều này đã được trả lời tốt nhưng điều này cụ thể hơn một chút để xử lý "subChild_with_SIZE" (nếu điều đó đến từ cấp độ gốc, nhưng bạn có thể điều chỉnh nó từ nơi nó có thể có)

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

2

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


8
$.whenchỉ hoạt động đối với những đối tượng mà trả về một đối tượng hứa hẹn
Rifat

đó là làm việc nhưng đưa ra một lỗi trên giao diện điều khiển .. "sau đó không phải là một chức năng"
Karan Datwani

1
Điều này chỉ hoạt động vì bạn đang gọi anotherMethod()ngay lập tức .. Nó không được chuyển vào dưới dạng gọi lại.
yts

điều này không có ý nghĩa.
Kevin B

1

hàm append của Jquery trả về một đối tượng jQuery để bạn có thể gắn thẻ một phương thức vào cuối

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

Tôi cần gọi tôi là chức năng tùy chỉnh javascript sau .append, tôi cần phải tính toán lại kích thước của thư mục gốc
David Horak

bạn có thể sử dụng jQuery cho mỗi phương thức, nhưng append là một phương thức đồng bộ, vì vậy bạn có thể yên tâm làm bất cứ điều gì bạn cần trên dòng tiếp theo.
DVE

@JinDave, chính xác thì bạn cần tính toán lại những gì? Số lượng con cái, chiều cao của cha mẹ, hoặc kích thước có nghĩa là gì?
mekwall

0

Đối với hình ảnh và các nguồn khác, bạn có thể sử dụng:

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

0

Cách sạch nhất là làm từng bước một. Sử dụng mỗi chức năng để phân tích thông qua từng phần tử. Ngay sau khi phần tử đó được thêm vào, hãy chuyển phần tử đó cho một hàm tiếp theo để xử lý phần tử đó.

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

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

-1

Tôi gặp sự cố này khi mã hóa HTML5 cho thiết bị di động. Một số kết hợp trình duyệt / thiết bị gây ra lỗi vì phương thức .append () không phản ánh các thay đổi trong DOM ngay lập tức (khiến JS bị lỗi).

Giải pháp nhanh chóng và bẩn thỉu cho tình huống này là:

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>');

Khoảng thời gian không bao giờ được sử dụng để đợi kết thúc một hàm tạo mã. Nó hoàn toàn không đáng tin cậy.
Gabe Hiemstra

-1

Có, bạn có thể thêm một hàm gọi lại vào bất kỳ chèn DOM nào:
$myDiv.append( function(index_myDiv, HTML_myDiv){ //.... return child })

Kiểm tra tài liệu JQuery: http://api.jquery.com/append/
Và đây là một ví dụ thực tế, tương tự: http://www.w3schools.com/jquery/ tryit.asp? filename = tryjquery_html_prepend_func


Tìm hiểu ví dụ về w3schools, bạn có thể kiểm tra tham số thứ 2, thay thế .prepend()phương thức bằng: $ ("p"). Prepend (function (n, pHTML) {return "<b> Phần tử p này </b> (\" <i > "+ pHTML +" </i> \ ") <b> có chỉ mục" + n + "</b>.";
Pedro Ferreira

1
Bạn rõ ràng không hiểu mục đích của ví dụ đó..nó không phải là một lệnh gọi lại cho biết nội dung đã được thêm vào, mà là một hàm gọi lại trả về những gì sẽ được thêm vào.
vsync

@vsync: chính xác. Bạn rõ ràng không hiểu câu trả lời của tôi. "con" là những gì đã được thêm vào chứ không phải khi nó được thêm vào.
Pedro Ferreira

-1

Tôi đã gặp phải một vấn đề tương tự gần đây. Giải pháp cho tôi là tạo lại bộ sưu tập. Hãy để tôi cố gắng chứng minh:

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();

Hi vọng điêu nay co ich!


Nó không giúp tôi. :(
Pal

-1

Trong jquery, bạn có thể sử dụng chỉ sau khi nối thêm

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

$ () gọi một hàm đăng ký một lệnh gọi lại sẵn sàng cho DOM (nếu một hàm được chuyển cho nó) hoặc trả về các phần tử từ DOM (nếu một chuỗi bộ chọn hoặc phần tử được chuyển cho nó)

Bạn có thể tìm thêm tại đây
sự khác biệt giữa $ và $ () trong jQuery
http://api.jquery.com/ready/


-2

Tôi biết đó không phải là giải pháp tốt nhất, nhưng là cách tốt nhất:

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

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

8
Tôi không nghĩ đây là một thực hành tốt. 100 là một số tùy ý và một sự kiện có thể mất nhiều thời gian hơn.
JasTonAChair

1
Đi sai đường. Nó có thể không phải lúc nào mất 100ms
axelvnk

Xem câu trả lời của tôi ở đây cho một lời giải thích lý do tại sao công trình này (số lượng có thể / phải là 0, không phải 100): stackoverflow.com/questions/6068955/...
jleach

1
nó ít nhất là thực hành tốt hơn các câu trả lời ở đây bằng cách sử dụng .ready.
Kevin B

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

Append không có lệnh gọi lại và đây là mã thực thi đồng bộ - không có nguy cơ nó KHÔNG được thực hiện


3
Tôi thấy quá nhiều bình luận như thế này và chúng không hữu ích. @david thất bại, vâng, JS IS đồng bộ, nhưng DOM cần thời gian để cập nhật. Nếu bạn cần thao tác các phần tử được nối thêm DOM của mình nhưng các phần tử chưa được hiển thị vào DOM của bạn, ngay cả khi phần nối thêm được thực hiện, thao tác mới của bạn sẽ KHÔNG hoạt động. (Ví dụ: $ ('# element'). On ().
NoobishPro

-4
$('#root').append(child).anotherMethod();

1
Tôi cần gọi cho tôi hàm javascript, không phải hàm jQuery
David Horák
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.