jQuery“on create”事件,用于动态创建的元素

时间:2013-03-07 10:25:58

标签: javascript jquery combobox

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

这样的事情存在吗?

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

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

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

UPDATE2 为了尝试进一步解释这个问题 - 我递归地创建了select/combobox个元素,.combobox()中也有很多启动代码,因此如果我使用了经典的方法,就像@bipen's answer一样,我的代码会膨胀到疯狂的水平。希望这能更好地解释问题。

UPDATE3 谢谢大家,我现在明白,由于DOMNodeInserted的弃用,DOM变异仍然存在空白,并且没有解决此问题的方法。我只需要重新考虑我的申请。

12 个答案:

答案 0 :(得分:51)

您可以on DOMNodeInserted事件来获取代码将其添加到文档中的事件。

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

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

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

编辑:阅读后我只需要仔细检查DOMNodeInserted不会导致浏览器出现问题。来自2010年的This question表明IE不支持该事件,因此如果可以,请进行测试。

请看这里:[link] 警告! DOMNodeInserted事件类型在本规范中定义,以供参考和完整,但此规范deprecates使用此事件类型。

答案 1 :(得分:19)

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

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

编辑:Mutation events 已弃用,请改用<{3}}

答案 2 :(得分:6)

刚刚想出了解决我所有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');
});

答案 3 :(得分:4)

这可以通过DOM4 MutationObservers完成,但只适用于Firefox 14 + / Chrome 18+(目前)。

然而,有一个“epic hack”(作者的话不是我的!)适用于支持CSS3动画的所有浏览器:IE10,Firefox 5 +,Chrome 3 +,Opera 12,Android 2.0 +, Safari 4+。请参阅博客中的demo。黑客是使用具有给定名称的CSS3动画事件,该事件在JavaScript中被观察和操作。

答案 4 :(得分:4)

似乎可靠的一种方式(虽然只在Firefox和Chrome中测试)是使用JavaScript来监听animationend(或者它的camelCased,以及前缀,兄弟{{1}事件,并将短暂的(在演示0.01秒内)动画应用于您计划添加的元素类型。当然,这不是animationEnd事件,而是近似(在兼容的浏览器中)onCreate类型的事件;以下是概念验证:

onInsertion

使用以下HTML:

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

并且(缩写为prefixed-versions-removed虽然存在于下面的Fiddle中)CSS:

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

JS Fiddle demo

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

事件名称的文档:

/* 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;
}

参考文献:

答案 5 :(得分:4)

正如其他几个答案所述,mutation events已被弃用,因此您应该使用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 });

这使观察者启动。第一个参数是观察者将观察更改的节点。第二个参数是options for what to watch forchildList表示我想要监视要添加或删除的子元素。 subtree是一个修饰符,扩展childList以监视此元素子树中任何位置的更改(否则,它只会直接查看<body>内的更改)。另外两个重要的可能性是attributescharacterData,这意味着它们的含义。

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

在回调中,事情变得有点棘手。回调接收MutationRecord个数组。每个MutationRecord都可以描述一种类型的多个更改(childListattributescharacterData)。由于我只是告诉观察者注意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);

这会使用jQuery special events API创建一个名为domNodeInserted的新事件。您可以像这样使用它:

$(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下方或adjacent上看到它。

(function($) {
  $(document).on("domNodeInserted", "select", function() {
    console.log(this);
    //$(this).combobox();
  });
})(jQuery);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<script>
  // For testing
  setTimeout(function() {
    var $el = document.createElement('select');
    document.body.appendChild($el);
  }, 500);
</script>

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

注意

这个jQuery代码是一个相当基本的实现。如果其他地方的修改使您的选择器有效,则不会触发它。

例如,假设您的选择器为.test select且文档已有<select>。将班级test添加到<body>会使选择器生效,但由于我只检查record.targetrecord.addedNodes,因此该事件不会触发。更改必须发生在您希望自己选择的元素上。

这可以通过在发生突变时查询选择器来避免。我选择不这样做以避免为已经处理过的元素造成重复事件。正确处理general sibling combinatorshttps://github.com/pie6k/jquery.initialize会使事情变得更加棘手。

要获得更全面的解决方案,请参阅Damien Ó Ceallaigh answer中提及的information here

答案 6 :(得分:3)

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

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

答案 7 :(得分:3)

有一个插件adampietrasiak/jquery.initialize,它基于 MutationObserver ,实现了这一目标。

$.initialize(".some-element", function() {
    $(this).css("color", "blue");
});

答案 8 :(得分:1)

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

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

答案 9 :(得分:1)

而不是...

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

你可以写...

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

答案 10 :(得分:0)

创建一个带有ID的<select>,将其附加到文档..并调用.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显示它之后......或者使用find ..

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

答案 11 :(得分:-1)

如果您使用angularjs,您可以编写自己的指令。我和bootstrapSwitch有同样的问题。我得打电话     $("[name='my-checkbox']").bootstrapSwitch(); 在javascript但我的html输入对象当时没有创建。所以我编写了一个自己的指令并创建了输入元素     <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();
    }
}
});