动态创建元素的jQuery“创建时”事件


80

我需要能够动态创建<select>元素并将其转换为jQuery .combobox()。这应该是元素创建事件,而不是某些“单击”事件,在这种情况下,我只能使用jQuery .on()

那么是否存在类似的东西?

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

我不愿意使用livequery,因为它已经过时了。

更新提到的选择/组合框通过ajax加载到jQuery彩色框(模式窗口)中,因此出现了问题-我只能使用colorbox来启动组合框onComplete,但是在更改一个组合框时,必须动态创建另一个选择/组合框,因此我需要一个检测元素创建的更通用方法(select在这种情况下)。

UPDATE2为了进一步解释问题-我select/combobox递归地创建了元素,内部还有很多启动代码.combobox(),因此,如果我使用经典方法(如@bipen的答案),我的代码会膨胀到疯狂的水平。希望这可以更好地解释问题。

UPDATE3谢谢大家,我现在了解到,由于弃用DOMNodeInsertedDOM突变会留下空白,因此无法解决此问题。我只需要重新考虑我的申请。


1
可能与准备活动有关吗?$(select).ready(function() { });
Pablo Banderas 2013年

@PabloBanderasready用于:“指定在DOM完全加载时执行的函数。” (来自jquery.com)
Caballero

1
为什么不在combobox()创建/插入时而不是之后应用该方法?
大卫说恢复莫妮卡

@DavidThomas试图在我的第二次更新中解释原因
Caballero

$(document).on('onComplete', 'select', function(){ // bind here? });呢 (不过,显然使用比父母更亲近的父母document。)
大卫说,请恢复莫妮卡(Monica

Answers:


58

您可以onDOMNodeInserted事件得到,当它在你的代码添加到文档中的事件。

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

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

在这里摆弄:http : //jsfiddle.net/Codesleuth/qLAB2/3/

编辑:阅读后,我只需要仔细检查DOMNodeInserted就不会在所有浏览器中引起问题。2010年的这个问题表明IE不支持该活动,因此请测试一下。

看到这里:[链接] 警告!为了参考和完整性,本规范中定义了DOMNodeInserted事件类型,但本规范不赞成使用此事件类型。


是不是.delegate从jQuery 1.7版本弃用并由取代.on
Caballero 2013年

1
是的,你是对的。我将修改此答案,但是老实说,由于DOMNodeInserted现在已弃用,因此我不确定您是否应该使用此答案。
Codesleuth

on('DOMNodeInserted'在发布此问题之前,我已经尝试过。显然没有用。
Caballero 2013年

19
既然您没有尝试过此问题,那么我想说那是不公平的投票。无论如何,我无法在这里为您提供帮助。
Codesleuth

21

您可以使用DOMNodeInserted突变事件(无需委派):

$('body').on('DOMNodeInserted', function(e) {
    var target = e.target; //inserted element;
});

编辑:突变事件弃用,使用突变观察者,而不是


19

如其他几个答案中所述,不建议使用突变事件,因此应改用MutationObserver。既然还没有人提供任何细节,那就去吧...

基本的JavaScript API

MutationObserver的API非常简单。它不像突变事件那么简单,但是仍然可以。

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>

让我们分解一下。

var observer = new MutationObserver(callback);

这将创建观察者。观察者还什么都没看。这就是事件监听器的附加位置。

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

这使观察者开始。第一个参数是观察者将监视其更改的节点。第二个参数是要注意什么选项

  • childList 表示我要注意添加或删除的子元素。
  • subtree是一个修饰符,可扩展 childList为监视此元素的子树中任何地方的更改(否则,它将直接在中查看更改targetNode)。

另外两个主要选项childListattributescharacterData,这意味着它们听起来像什么。您必须使用这三个之一。

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

回调内部有些棘手。回调接收一个MutationRecord数组。每个MutationRecord可以描述一种类型的几个变化(childListattributes,或characterData)。因为我只告诉观察者要注意childList,所以我不会费心检查类型。

var list = record.addedNodes;

在这里,我抓取了所有添加的所有子节点的NodeList。对于未添加节点的所有记录,该字段将为空(并且可能会有很多此类记录)。

从那里开始,我遍历添加的节点并找到任何<select>元素。

这里没有什么复杂的。

jQuery的

...但是您要使用jQuery。精细。

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

这将domNodeInserted使用jQuery特殊事件API创建一个名为的新事件。您可以这样使用它:

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

我个人建议寻找一个类,因为某些库将创建select用于测试目的的元素。

当然,您还可以.off("domNodeInserted", ...)通过传递如下数据来使用或微调观看:

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

select.test每当直接在主体内部更改元素的属性时,这将触发检查元素的外观。

您可以在下面或jsFiddle上看到它。


注意

此jQuery代码是一个相当基本的实现。在其他地方进行的修改使选择器有效的情况下,它不会触发。

例如,假设您的选择器为.test select并且文档已经有一个<select>。添加类test,以<body>将选择有效的,但因为我只检查record.targetrecord.addedNodes,该事件将不会触发。您必须自行选择要更改的元素。

每当发生突变时,都可以通过查询选择器来避免这种情况。我选择不这样做是为了避免对已处理的元素造成重复事件。适当地处理相邻通用的同级组合器会使事情变得更加棘手。

有关更全面的解决方案,请参见https://github.com/pie6k/jquery.initialize,如DamienÓCeallaigh回答中所述。但是,该库的作者宣布该库已过时,建议您不要为此使用jQuery。


9

只是想出了这个解决方案,似乎解决了我所有的ajax问题。

对于准备好的事件,我现在使用以下代码:

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

对于正常的触发事件,我现在使用以下代码:

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


4

可以这样做,DOM4 MutationObservers但仅适用于Firefox 14 + / Chrome 18+(目前)。

但是,在所有支持CSS3动画的浏览器中都可以使用“史诗般的hack ”(作者的话不是我的话!),这些浏览器支持IE10,Firefox 5 +,Chrome 3 +,Opera 12,Android 2.0 +,Safari 4+。请参阅博客中的演示。技巧是使用具有给定名称的CSS3动画事件,该事件在JavaScript中得到观察并作用。


4

一种似乎可靠的方法(尽管仅在Firefox和Chrome中进行了测试)是使用JavaScript侦听animationend(或它的camelCased和带前缀的同级animationEnd事件)事件,然后将短暂的(在演示中为0.01秒)动画应用于您计划添加的元素类型。当然,这不是onCreate事件,而是(在兼容的浏览器中)近似onInsertion事件的类型。以下是概念证明:

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

使用以下HTML:

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

并且(尽管存在于下面的小提琴中,但已删除了前缀版本的缩写)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;
}

JS小提琴演示

显然,可以调整CSS以适合相关元素的位置以及jQuery中使用的选择器(它实际上应该尽可能地靠近插入点)。

事件名称的文档:

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

参考文献:


4

对我来说,束缚身体是行不通的。使用jQuery.bind()绑定到文档。

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

1

我认为值得一提的是,在某些情况下,这会起作用:

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


0

创建一个<select>id,将其附加到document ..并调用.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();

这应该做的伎俩..你可以隐藏,如果你想选择后.combobox显示it..or其他使用查找..

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

-1

如果您正在使用angularjs,则可以编写自己的指令。我在bootstrapSwitch上遇到了同样的问题。我必须调用 $("[name='my-checkbox']").bootstrapSwitch(); javascript,但当时未创建html输入对象。所以我写了一个自己的指令并创建了input元素 <input type="checkbox" checkbox-switch>

在指令中,我编译该元素以通过javascript获取访问权限,然后执行jquery命令(如您的.combobox()命令)。非常重要的是删除该属性。否则,该指令将自行调用,并且您已经建立了一个循环

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

这个问题只与jQuery有关,而不与AngularJS有关。该解决方案应限于jQuery和jQuery插件。
James Dunne
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.