Sự kiện jQuery “on create” cho các phần tử được tạo động


81

Tôi cần có khả năng tạo động <select>phần tử và biến nó thành jQuery .combobox(). Đây phải là sự kiện tạo phần tử, trái ngược với một số sự kiện "nhấp chuột" trong trường hợp đó tôi chỉ có thể sử dụng jQuery .on().

Vì vậy, một cái gì đó như thế này tồn tại?

$(document).on("create", "select", function() {
    $(this).combobox();
}

Tôi không muốn sử dụng livequery vì nó rất lỗi thời.

CẬP NHẬT Lựa chọn / combobox được đề cập được tải qua ajax vào hộp màu jQuery (cửa sổ phương thức), do đó, vấn đề - tôi chỉ có thể khởi tạo combobox bằng cách sử dụng hộp màu onComplete, tuy nhiên khi thay đổi một combobox, một select / combobox khác phải được tạo động, vì vậy tôi cần cách chung chung hơn để phát hiện việc tạo một phần tử ( selecttrong trường hợp này).

CẬP NHẬT2 Để thử và giải thích thêm vấn đề - tôi có select/comboboxcác phần tử được tạo đệ quy, cũng có rất nhiều mã khởi tạo bên trong .combobox(), do đó nếu tôi sử dụng cách tiếp cận cổ điển, như trong câu trả lời của @ bipen , mã của tôi sẽ tăng lên mức điên rồ. Hy vọng điều này giải thích vấn đề tốt hơn.

UPDATE3 Cảm ơn tất cả mọi người, tôi hiểu rằng vì việc không dùng nữa nên DOMNodeInsertedsẽ có một khoảng trống trong đột biến DOM và không có giải pháp nào cho vấn đề này. Tôi sẽ phải xem xét lại đơn đăng ký của mình.


1
Có thể được với sự kiện đã sẵn sàng? $(select).ready(function() { });
Pablo Banderas

@PabloBanderas readyđược sử dụng để: "Chỉ định một hàm để thực thi khi DOM được tải đầy đủ." (từ jquery.com)
Caballero

1
Tại sao không áp dụng combobox()phương pháp tại điểm tạo / chèn, thay vì sau đó?
David nói hãy phục hồi Monica vào

@DavidThomas Cố gắng giải thích lý do trong bản cập nhật thứ hai của tôi
Caballero

Vì vậy $(document).on('onComplete', 'select', function(){ // bind here? });? (Rõ ràng là sử dụng một mẹ gần hơn document, mặc dù.)
David nói Khôi phục Monica

Câu trả lời:


59

Bạn có thể oncác DOMNodeInsertedsự kiện để có được một sự kiện khi nó được thêm vào các tài liệu bằng cách mã của bạn.

$('body').on('DOMNodeInserted', 'select', function () {
      //$(this).combobox();
});

$('<select>').appendTo('body');
$('<select>').appendTo('body');

Tìm hiểu tại đây: http://jsfiddle.net/Codesleuth/qLAB2/3/

CHỈNH SỬA: sau khi đọc xung quanh, tôi chỉ cần kiểm tra kỹ DOMNodeInsertedsẽ không gây ra sự cố trên các trình duyệt. Câu hỏi này từ năm 2010 cho thấy IE không hỗ trợ sự kiện này, vì vậy hãy kiểm tra nó nếu bạn có thể.

Xem tại đây: [link] Cảnh báo! loại sự kiện DOMNodeInserted được xác định trong đặc điểm kỹ thuật này để tham khảo và hoàn chỉnh, nhưng đặc điểm kỹ thuật này không chấp nhận việc sử dụng loại sự kiện này.


không .delegatebị phản đối từ jQuery phiên bản 1.7 và được thay .onthế bởi ?
Caballero

1
Đúng vậy, bạn nói đúng. Tôi sẽ sửa đổi câu trả lời này, nhưng thực sự tôi không chắc liệu bạn có nên sử dụng câu trả lời này hay không vì DOMNodeInsertedbây giờ không được dùng nữa.
Codesleuth

Tôi đã thử on('DOMNodeInserted'trước khi đăng câu hỏi này. Rõ ràng là nó đã không hoạt động.
Caballero

20
Vì nó không nằm trong câu hỏi của bạn mà bạn đã thử nó, tôi sẽ nói đó là một cuộc bỏ phiếu không công bằng. Trong mọi trường hợp, tôi không thể giúp bạn ở đây.
Codesleuth


20

Như đã đề cập trong một số câu trả lời khác, các sự kiện đột biến không được dùng nữa, vì vậy bạn nên sử dụng MutationObserver để thay thế. Vì chưa ai đưa ra bất kỳ chi tiết nào về điều đó, nên đây là ...

API JavaScript cơ bản

API cho MutationObserver khá đơn giản. Nó không hoàn toàn đơn giản như các sự kiện đột biến, nhưng nó vẫn ổn.

function callback(records) {
  records.forEach(function (record) {
    var list = record.addedNodes;
    var i = list.length - 1;
    
	for ( ; i > -1; i-- ) {
	  if (list[i].nodeName === 'SELECT') {
	    // Insert code here...
	    console.log(list[i]);
	  }
	}
  });
}

var observer = new MutationObserver(callback);

var targetNode = document.body;

observer.observe(targetNode, { childList: true, subtree: true });
<script>
  // For testing
  setTimeout(function() {
    var $el = document.createElement('select');
    document.body.appendChild($el);
  }, 500);
</script>

Hãy phá vỡ điều đó.

var observer = new MutationObserver(callback);

Điều này tạo ra người quan sát. Người quan sát vẫn chưa xem gì cả; đây chỉ là nơi người nghe sự kiện được đính kèm.

observer.observe(targetNode, { childList: true, subtree: true });

Điều này khiến người quan sát phải giật mình. Đối số đầu tiên là nút mà người quan sát sẽ theo dõi các thay đổi trên. Đối số thứ hai là các tùy chọn cho những gì cần xem .

  • childList nghĩa là tôi muốn xem các phần tử con được thêm vào hoặc bị xóa.
  • subtreelà một công cụ sửa đổi mở rộng childList để theo dõi các thay đổi ở bất kỳ đâu trong cây con của phần tử này (nếu không, nó sẽ chỉ xem xét các thay đổi trực tiếp bên trong targetNode).

Hai tùy chọn chính khác bên cạnh đó childListattributescharacterData, có nghĩa là về âm thanh của chúng. Bạn phải sử dụng một trong ba cái đó.

function callback(records) {
  records.forEach(function (record) {

Mọi thứ trở nên phức tạp một chút bên trong lệnh gọi lại. Lệnh gọi lại nhận một mảng các MutationRecord . Mỗi MutationRecord có thể mô tả một số thay đổi của một loại ( childList, attributeshoặc characterData). Vì tôi chỉ nói với người quan sát để theo dõi childList, tôi sẽ không bận tâm kiểm tra loại.

var list = record.addedNodes;

Ngay tại đây, tôi lấy một NodeList của tất cả các nút con đã được thêm vào. Điều này sẽ trống cho tất cả các bản ghi mà các nút không được thêm vào (và có thể có nhiều bản ghi như vậy).

Từ đó trở đi, tôi lặp qua các nút đã thêm và tìm bất kỳ nút nào là <select>phần tử.

Không có gì thực sự phức tạp ở đây.

jQuery

... nhưng bạn đã yêu cầu jQuery. Khỏe.

(function($) {

  var observers = [];

  $.event.special.domNodeInserted = {

    setup: function setup(data, namespaces) {
      var observer = new MutationObserver(checkObservers);

      observers.push([this, observer, []]);
    },

    teardown: function teardown(namespaces) {
      var obs = getObserverData(this);

      obs[1].disconnect();

      observers = $.grep(observers, function(item) {
        return item !== obs;
      });
    },

    remove: function remove(handleObj) {
      var obs = getObserverData(this);

      obs[2] = obs[2].filter(function(event) {
        return event[0] !== handleObj.selector && event[1] !== handleObj.handler;
      });
    },

    add: function add(handleObj) {
      var obs = getObserverData(this);

      var opts = $.extend({}, {
        childList: true,
        subtree: true
      }, handleObj.data);

      obs[1].observe(this, opts);

      obs[2].push([handleObj.selector, handleObj.handler]);
    }
  };

  function getObserverData(element) {
    var $el = $(element);

    return $.grep(observers, function(item) {
      return $el.is(item[0]);
    })[0];
  }

  function checkObservers(records, observer) {
    var obs = $.grep(observers, function(item) {
      return item[1] === observer;
    })[0];

    var triggers = obs[2];

    var changes = [];

    records.forEach(function(record) {
      if (record.type === 'attributes') {
        if (changes.indexOf(record.target) === -1) {
          changes.push(record.target);
        }

        return;
      }

      $(record.addedNodes).toArray().forEach(function(el) {
        if (changes.indexOf(el) === -1) {
          changes.push(el);
        }
      })
    });

    triggers.forEach(function checkTrigger(item) {
      changes.forEach(function(el) {
        var $el = $(el);

        if ($el.is(item[0])) {
          $el.trigger('domNodeInserted');
        }
      });
    });
  }

})(jQuery);

Điều này tạo ra một sự kiện mới được gọi domNodeInserted, sử dụng API sự kiện đặc biệt của jQuery . Bạn có thể sử dụng nó như vậy:

$(document).on("domNodeInserted", "select", function () {
  $(this).combobox();
});

Cá nhân tôi khuyên bạn nên tìm kiếm một lớp vì một số thư viện sẽ tạo ra selectcác phần tử cho mục đích thử nghiệm.

Đương nhiên, bạn cũng có thể sử dụng .off("domNodeInserted", ...)hoặc tinh chỉnh chế độ xem bằng cách chuyển dữ liệu như sau:

$(document.body).on("domNodeInserted", "select.test", {
  attributes: true,
  subtree: false
}, function () {
  $(this).combobox();
});

Điều này sẽ kích hoạt kiểm tra sự xuất hiện của một select.testphần tử bất cứ khi nào các thuộc tính thay đổi cho các phần tử trực tiếp bên trong phần thân.

Bạn có thể xem trực tiếp bên dưới hoặc trên jsFiddle .


Ghi chú

Mã jQuery này là một triển khai khá cơ bản. Nó không kích hoạt trong trường hợp các sửa đổi ở nơi khác làm cho bộ chọn của bạn hợp lệ.

Ví dụ: giả sử bộ chọn của bạn là .test selectvà tài liệu đã có <select>. Thêm lớp testvào <body>sẽ làm cho bộ chọn hợp lệ, nhưng vì tôi chỉ kiểm tra record.targetrecord.addedNodes, sự kiện sẽ không kích hoạt. Thay đổi phải xảy ra với phần tử bạn muốn chọn chính nó.

Điều này có thể tránh được bằng cách truy vấn bộ chọn bất cứ khi nào đột biến xảy ra. Tôi đã chọn không làm điều đó để tránh gây ra các sự kiện trùng lặp cho các phần tử đã được xử lý. Xử lý thích hợp với các tổ hợp anh chị em kế cận hoặc chung sẽ làm cho mọi thứ trở nên phức tạp hơn.

Để có giải pháp toàn diện hơn, hãy xem https://github.com/pie6k/jquery.initialize , như đã đề cập trong câu trả lời của Damien Ó Ceallaigh . Tuy nhiên, tác giả của thư viện đó đã thông báo rằng thư viện đã cũ và gợi ý rằng bạn không nên sử dụng jQuery cho việc này.


9

Chỉ cần đưa ra giải pháp này dường như giải quyết tất cả các vấn đề ajax của tôi.

Đối với các sự kiện đã sẵn sàng, bây giờ tôi sử dụng cái này:

function loaded(selector, callback){
    //trigger after page load.
    $(function () {
        callback($(selector));
    });
    //trigger after page update eg ajax event or jquery insert.
    $(document).on('DOMNodeInserted', selector, function () {
        callback($(this));
    });
}

loaded('.foo', function(el){
    //some action
    el.css('background', 'black');
});

Và đối với các sự kiện kích hoạt bình thường, bây giờ tôi sử dụng cái này:

$(document).on('click', '.foo', function () {
    //some action
    $(this).css('background', 'pink');
});


4

Điều này có thể được thực hiện với DOM4 MutationObserversnhưng sẽ chỉ hoạt động trong Firefox 14 + / Chrome 18+ (hiện tại).

Tuy nhiên, có một " bản hack hoành tráng " (lời của tác giả không phải của tôi!) Hoạt động trên tất cả các trình duyệt hỗ trợ hoạt ảnh CSS3 đó là: IE10, Firefox 5+, Chrome 3+, Opera 12, Android 2.0+, Safari 4+. Xem bản trình diễn từ blog. Việc hack là sử dụng một sự kiện hoạt ảnh CSS3 với một tên cụ thể được quan sát và thực hiện trong JavaScript.


4

Một cách có vẻ đáng tin cậy (mặc dù chỉ được thử nghiệm trong Firefox và Chrome) là sử dụng JavaScript để lắng nghe sự kiện animationend(hoặc camelCased và tiền tố, anh chị em của nó animationEnd) và áp dụng hoạt ảnh tồn tại trong thời gian ngắn (trong bản demo 0,01 giây) cho loại phần tử bạn định thêm. Tất nhiên, đây không phải là một onCreatesự kiện, nhưng gần đúng (trong các trình duyệt tuân thủ ) một onInsertionloại sự kiện; sau đây là một bằng chứng về khái niệm:

$(document).on('webkitAnimationEnd animationend MSAnimationEnd oanimationend', function(e){
    var eTarget = e.target;
    console.log(eTarget.tagName.toLowerCase() + ' added to ' + eTarget.parentNode.tagName.toLowerCase());
    $(eTarget).draggable(); // or whatever other method you'd prefer
});

Với HTML sau:

<div class="wrapper">
    <button class="add">add a div element</button>
</div>

Và (viết tắt, tiền tố-phiên bản-bị loại bỏ mặc dù có trong Fiddle, bên dưới) CSS:

/* vendor-prefixed alternatives removed for brevity */
@keyframes added {
    0% {
        color: #fff;
    }
}

div {
    color: #000;
    /* vendor-prefixed properties removed for brevity */
    animation: added 0.01s linear;
    animation-iteration-count: 1;
}

Bản demo JS Fiddle .

Rõ ràng là CSS có thể được điều chỉnh để phù hợp với vị trí của các phần tử có liên quan, cũng như bộ chọn được sử dụng trong jQuery (nó phải thực sự càng gần điểm chèn càng tốt).

Tài liệu về tên sự kiện:

Mozilla   |  animationend
Microsoft |  MSAnimationEnd
Opera     |  oanimationend
Webkit    |  webkitAnimationEnd
W3C       |  animationend

Người giới thiệu:


4

Đối với tôi ràng buộc với cơ thể không hoạt động. Liên kết với tài liệu bằng jQuery.bind () không.

$(document).bind('DOMNodeInserted',function(e){
             var target = e.target;
         });

1

Tôi nghĩ điều đáng nói là trong một số trường hợp, điều này sẽ hoạt động:

$( document ).ajaxComplete(function() {
// Do Stuff
});

1

thay vì...

$(".class").click( function() {
    // do something
});

Bạn có thể viết...

$('body').on('click', '.class', function() {
    // do something
});

0

tạo một <select>với id, nối nó vào tài liệu .. và gọi.combobox

  var dynamicScript='<select id="selectid"><option value="1">...</option>.....</select>'
  $('body').append(dynamicScript); //append this to the place your wanted.
  $('#selectid').combobox();  //get the id and add .combobox();

điều này sẽ thực hiện thủ thuật .. bạn có thể ẩn lựa chọn nếu bạn muốn và sau khi .comboboxhiển thị nó..hoặc khác sử dụng tìm ..

 $(document).find('select').combobox() //though this is not good performancewise

-1

nếu bạn đang sử dụng anglejs, bạn có thể viết chỉ thị của riêng mình. Tôi đã gặp vấn đề tương tự khi bootstrapSwitch. Tôi phải gọi $("[name='my-checkbox']").bootstrapSwitch(); bằng javascript nhưng đối tượng đầu vào html của tôi chưa được tạo vào thời điểm đó. Vì vậy, tôi viết một chỉ thị riêng và tạo phần tử đầu vào với <input type="checkbox" checkbox-switch>

Trong chỉ thị, tôi biên dịch phần tử để có quyền truy cập thông qua javascript và thực thi lệnh jquery (giống như .combobox()lệnh của bạn ). Rất quan trọng là loại bỏ thuộc tính. Nếu không, chỉ thị này sẽ tự gọi và bạn có một vòng lặp

app.directive("checkboxSwitch", function($compile) {
return {
    link: function($scope, element) {
        var input = element[0];
        input.removeAttribute("checkbox-switch");
        var inputCompiled = $compile(input)($scope.$parent);
        inputCompiled.bootstrapSwitch();
    }
}
});

Câu hỏi chỉ liên quan đến jQuery, không phải AngularJS. Giải pháp nên được giới hạn trong các plugin jQuery và jQuery.
James Dunne
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.