Reputation: 259
I have written a 'plugin' for add & edit items to a list. Adding and editing is working fine for existing items, but it doesn't work for a new item which is added via .before()
. I make use of the following script, which isn't optimal I think?
<div id="test">
<div class="item">Test</div>
<div class="add">Add item</div>
</div>
<script>
(function ($) {
$.fn.pim = function() {
$(this).click(function() {
if ($(this).hasClass('add')) {
$($(this)).before('<div class="item">Test</div>');
} else {
alert('Edit succes');
}
});
}
}(jQuery));
$("#test div").pim();
</script>
Upvotes: 0
Views: 1786
Reputation: 747
What happens is that .on()
function allows you to delegate the event. While .click()
it only takes elements created.
Delegate the event to all child nodes, created and creating. Without loading the plugin for every element created.
jQuery .on( events [, selector ] [, data ], handler ): A selector string to filter the descendants of the selected elements that trigger the event. If the selector is null or omitted, the event is always triggered when it reaches the selected element.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="test">
<div class="item">Test</div>
<div class="add">Add item</div>
</div>
<script>
(function($){
$.fn.pim = function() {
$(this).on('click', '>', function(event) {
var $target = $(event.target);
if ($target.hasClass('add')) {
$target.before('<div class="item">Test</div>');
} else {
alert('Edit succes');
}
});
}
})(jQuery);
$("#test").pim();
</script>
Said that; You can modify the >
to determine which elements want to delegate the event. In this example, it takes all direct children of "#test".
Upvotes: 1
Reputation: 12508
I think this achieves the desired effect:
(function ($) {
$.fn.pim = function() {
this.click(function() {
if ($(this).hasClass('add')) {
$(this).before($('<div />', { "class": "item", "text": "Test" }).pim());
} else {
alert('Edit succes');
}
});
return this;
}
}(jQuery));
$("#test div").pim();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="test">
<div class="item">Test</div>
<div class="add">Add item</div>
</div>
Explanation:
I've simplified a few things here. First the inital $(this).click
can be simplified to this.click
. Inside the $.fn.*
namespace, this
takes the context of the current jQuery instance so there is no need to create a new instance of it. However, inside the callback you will need to leave it as $(this)
since it will be executing outside the $.fn.*
scope.
In addition, I've changed the .before()
to flow with the correct method implementation. When using .before()
the html or jQuery object passed in is inserted before matched set of elements. In this case, the <div>
that we're generating is added before $(this)
.
Next, you're event is not setup to be delegated. Therefore, when you create a new .item
element, you'll need to call .pim()
on the new element to ensure an event handler is created and attached to the new element. This will allow it to function properly when inserted in the DOM. Note that you don't want to call it on the full set (i.e. $('#test div')
again because you'll end up attaching multiple event handlers to the already existing elements.
Finally, to allow your plugin to flow in the standard jQuery fashion, I've added return this;
at the end of your function to allow your plugin to be changed with existing functions. The return this;
statement will return the jQuery object of matched elements allowing for further chain methods to be executed. To show the effect of method chaining, see these two examples:
.css('background', 'red')
results in a console error. Select F12 to open the browser's console and see this error. The method chaining ability of jQuery is broken because the jQuery object is not returned after the operation is complete. The error in the console will be:
Uncaught TypeError: Cannot read property 'css' of undefined
return this;
as the last statement and then calling the additional .css('background', 'red')
function results in the new item added with a red background. This demonstrates how the proper plugin setup can allowed for continued manipulation after it's function contents have been executed and completed.I know this wasn't part of your original question. However, this is the optimal way that you want your plugins to perform so I figured it was worth mentioning.
Upvotes: 0
Reputation: 343
Try:
(function ($) {
$.fn.pim = function() {
$(this).click(function() {
if ($(this).hasClass('add')) {
$($(this)).before('<div class="item">Test</div>');
$("#test div").pim();
} else {
alert('Edit succes');
}
});
}
}(jQuery));
$("#test div").pim();
Upvotes: 0