Why my plugin fires multipe callback or clicks - javascript

I am creating my simple jQuery plugin that can be use to attach for any action's confirmation. But facing very strange issue, It work fine for single element click, But when i am going to click for second element which also bind with my plugin it work fine but it's also fires for previous clicked element as well.
(function ($) {
$.fn.BootConfirm = function (options) {
// Establish our default settings
var settings = $.extend({
message: 'Are you sure about this ?',
complete: null
}, options);
var self = this;
var cointainer = '\
<div class="modal fade" id="confirmBoot" role="dialog" aria-labelledby="confirmDeleteLabel" aria-hidden="true">\
<div class="modal-dialog">\
<div class="modal-content">\
<div class="modal-header">\
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>\
<h4 class="modal-title">Confirm action</h4>\
</div>\
<div class="modal-body">\
<p>Are you sure about this ?</p>\
</div>\
<div class="modal-footer">\
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>\
<button type="button" class="btn btn-success btn-ok" id="confirm">Ok</button>\
</div>\
</div>\
</div>\
</div>';
return this.each(function () {
var self = this;
$(this).click(function () {
if (!$('#confirmBoot').length) {
$('body').append(cointainer);
}
if (settings.message) {
$('#confirmBoot').find('.modal-body').text($(this).attr('data-confirm'));
}
$('#confirmBoot').modal({ show: true });
if ($.isFunction(settings.complete)) {
$('#confirmBoot').find('.btn-ok').click(function () {
$.when(settings.complete.call(this, self)).done(function () {
$('#confirmBoot').modal("hide"); // Alerts "123"
});
});
}
});
});
}
}(jQuery));
This is my callback function :
function kaushik(myObject) {
ManageAcriveProducts(myObject);
};
and i am calling it by following way
$('a[data-confirm]').BootConfirm({
complete: kaushik
});
For more detail check this js fidder Jsfiddle. Can anyone one share possible solution or better way to do this. Or is there any better way to achieve this ?

The problem is that you're assigning a click on your btn-ok on every click event on a bootconfirmed object. And each click is linked to the object that has been clicked, so it ends up in your callback every time you click btn-ok.
One simple fix, though I'm not sure it's the best, is to remove the click on your btn-ok after the action is complete. Like this:
$.when(settings.complete.call(this, self)).done(function () {
$('#confirmBoot').modal("hide");
$('#confirmBoot').find('.btn-ok').off('click');
});
Working fiddle: http://jsfiddle.net/ywunutyw/
EDIT:
A little improvement on previous solution, it might need some adjustments, since I didn't look into details, but it should give you some ideas. To prevent adding click events and removing them every time user clicks on a button, you can define the click on modal window outside click behavior of each active/inactive button. And on click of active/inactive you define target that will be used in modal confirmation. Like this:
Just before calling behaviors with this.each:
$(document).on('click', '#confirmBoot .btn-ok',
function (e) {
if ($.isFunction(settings.complete)) {
console.log(self)
$.when(settings.complete.call(this, click_origin)).done(function () {
$('#confirmBoot').modal("hide");
});
}
});
Then on the click event of you active/inactive:
click_origin = e.target;
See fiddle: http://jsfiddle.net/ywunutyw/1/

Related

jQuery off() not removing click handler

I have the following code:
$(document).ready(function () {
EnableModal();
});
function EnableModal() {
if (isModalEnabled) { return; }
// Initialize modal dialog
// attach modal-container bootstrap attributes to links with .modal-link class.
// when a link is clicked with these attributes, bootstrap will display the href content in a modal dialog.
$('body').on('click', '.modal-link', function (e) {
e.preventDefault();
$(this).attr('data-target', '#modal-container');
$(this).attr('data-toggle', 'modal');
});
}
function DisableModal() {
$('body').off('click', '.modal-link');
}
I am attempting to turn this off under certain circumstances, so I am calling:
$('body').off('click', '.modal-link');
However, the button with the modal-link class is still allowing click events through. I see no errors in the developers console.
I have verified it is calling these correctly and that each is only being called once in my test case.
What am I doing wrong here?
I met this issue before. I wasn't sure what happened at the very beginning and wonder if it was because the selectors weren't actually the same. I checked them and found out they were the same but still couldn't remove the event handler.
I finally fixed this by giving a dummy function as event handler after I removed the original one.
function DisableModal() {
$('body').off('click', '.modal-link');
$('body').on('click', '.modal-link', () => {});
}
Feel free to use ES5 version if you don't like the lambda expression. as
$('body').on('click', '.modal-link', function(){});
Works fine here:
var isModalEnabled;
$(document).ready(function () {
EnableModal();
$(".disableModal").click(DisableModal);
});
function EnableModal() {
if (isModalEnabled) { return; }
// Initialize modal dialog
// attach modal-container bootstrap attributes to links with .modal-link class.
// when a link is clicked with these attributes, bootstrap will display the href content in a modal dialog.
$('body').on('click', '.modal-link', function (e) {
e.preventDefault();
$(this).attr('data-target', '#modal-container');
$(this).attr('data-toggle', 'modal');
});
}
function DisableModal() {
$('body').off('click', '.modal-link');
}
body { font-family: sans-serif; }
[data-target='#modal-container'] {
font-weight: bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Click a few "Modal Link" buttons, and watch the button text turn bold. Then click the "Disable Modal" button, and click the remaining "Modal Link" buttons. The button text does <em>not</em> turn bold.</p>
<button class="modal-link">Modal Link</button>
<button class="modal-link">Modal Link</button>
<button class="modal-link">Modal Link</button>
<p><button class="disableModal">Disable Modal</button></p>
<button class="modal-link">Modal Link</button>
<button class="modal-link">Modal Link</button>
<button class="modal-link">Modal Link</button>
<p>To reset, click the "Run code snippet" button above.</p>
Without knowing the real cause of this, maybe the solution is to use namespaced events.
$('body').on('click.modal', '.modal-link', function (e) { });
and to remove it
$('body').off('.modal');
But I have a feeling it has nothing to do with this, but with the real issue is with the bootstrap modal. Maybe you need to clean up those attributes.
$('[data-toggle="modal"]').removeAttr("data-target data-toggle");

Closing bootstrap modal

I have two buttons on modal, on first I need to save changes and close modal, on second I need to close modal and reset changes - same thing when person closes modal by clicking anywhere. The problem is I don't know how to check whether save button was clicked.
Here is html:
<div class="modal fade" id="modalNumber" tabindex="-1" role="dialog" aria-labelledby="modalNumberLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
blablabla
<div class="modal-footer">
<button data-dismiss="modal" class="btn-u btn-u-default" type="button">{{ 'backButton'|trans }}</button>
<button type="submit" class="btn-u saveChanges">{{ 'confirmChangesButton'|trans }}</button>
</div>
</div>
</div>
</div>
and here is JS code:
$('.saveChanges').on('click', function () {
// doing something here
// ....
// and closing modal in the end
$(this).closest('.modal.fade').modal('hide');;
});
$('.modal.fade').each(function(index, object) {
$(object).on('hidden.bs.modal', function (e) {
console.log(e.target);
// here is part of irrelevant code - what's important is that I use index and I need to know if saveButton was clicked
mainContainer.resetGroup(index);
});
});
e.target always returns div class="modal fade" element, not the button
What's important - I don't want to call mainContainer.resetGroup(index); when save button was clicked.
When you attach a function to your save-btn you don't need to know which button was clicked. In my example are two functions, similar to your example. The first function is called when the save-btn is clicked. In this function you can fetch the data, which you have to save and save it (e.g. via ajax).
The second method is called every time when the modal is closed. But when the save-btn was pressed, the first function was invoked before, and therefore you don't need to check any more which button was pressed...
You just need to replace the alert(data) function with your 'save procedure'. I hope that I didn't misunderstood your question...
$(document).ready(function(){
$('.save-btn').on('click', function(evt){
evt.preventDefault();
var $btn = $(this);
var $modal = $btn.closest('.modal');
var data = $modal.find('form').serializeArray();
// save data
alert(data);
// close modal
$modal.modal('hide');
});
$('#myModal').on('hidden.bs.modal', function(evt) {
alert('modal was hidden -- clear input fields');
$('#myModal').find('input').val('');
});
});
Check out this fiddle: https://jsfiddle.net/216q3jgk/4/
EDIT:
In the case that you would know which button was pressed, I would suggest to add a data-attribute to your modal, when the save-btn is pressed.
To add the data attribute call this in the first function before the modal is closed:
$modal.data('savePressed', true);
The data-attribute can be checked in the hidden.bs.modal function with the following code:
var $modal = $(evt.target);
if (!$modal.data('savePressed')) {
// save button was NOT pressed
alert('save not pressed');
}
You can find a full example in this fiddle: http://jsfiddle.net/216q3jgk/5/

Getting the collection in a jQuery plugin

Basically, what I am trying to do is create a bbcode editor with a textbox, some buttons and jQuery. Here is my form:
<div class="form-group">
<div class="btn-group btn-group-sm">
<button type="button" class="btn glyphicon bbcode" rel="bold"><b>B</b></button>
<button type="button" class="btn glyphicon bbcode" rel="italic"><i>I</i></button>
</div>
</div>
<div class="form-group">
<textarea class="bbcode" rel="editor" cols="100" rows="12"></textarea>
</div>
and my plugin is called using:
<script>
$('document').ready(function() {
$('.bbcode').bbcode();
});
</script>
and the plugin itself, I am just trying to get the basics done at the minute to update the textbox data when a button is clicked:
(function($) {
"use strict";
$.fn.bbcode = function() {
this.click(function() {
var rel = $(this).attr('rel');
if (rel == 'editor') {
return this;
} else {
alert($(this).attr('rel')); // I can see this pop up so the click event is firing
$('.bbcode[rel=editor]').val('test');
return this;
}
});
}
} (jQuery));
This seems to be the only way I can pick up the textbox, I don't really want to hardcode the class I want like that. I think what I am looking for is a way to get the collection from the function call in the script tags.
This is more than likely something stupid/obvious I have overlooked.
The value of this in the immediate function refers to the collection. However, it is shadowed by the this inside your click handler (which refers to the element being clicked) so you cannot access it.
Create a variable to store this and that'll be your collection.
(function ($) {
"use strict";
$.fn.bbcode = function () {
var $editors = this;
this.click(function () {
var rel = $(this).attr('rel');
if (rel == 'editor') {
return this;
} else {
alert($(this).attr('rel')); // I can see this pop up so the click event is firing
$editors.val('test');
return this;
}
});
}
}(jQuery));

jQuery custom Confirm

I want to have a very simple custom dialog. When I click remove, I want a simple panel opens up with the option to confirm or cancel. If confirmed, more things will run.
Since I want to use this confirmation in different files, this was my approach:
In index.js that runs on all pages I have:
var confirmation = -1
$(document).ready(function(){
$('html').on({
click: function() {
confirmation = 1;
}
},'#confirmation_yes');
$('html').on({
click: function() {
confirmation = 0;
}
},'#confirmation_no');
});
function confirmAction(callback)
{
if( confirmation == 1 ) {
$('#popup_panel').slideUp('slow', function(){
$('#popup_fader').fadeOut('fast');
});
confirmation = -1;
callback(true);
}
if( confirmation == 0 ) {
$('#popup_panel').slideUp('slow', function(){
$('#popup_fader').fadeOut('fast');
});
confirmation = -1;
callback(true);
}
setTimeout(confirmAction, 50)
}
So my idea was that then inside other JS files, we have
$('#element').click(function(){
confirmAction(function(result){
// do my stuff
});
})
So when I do this, the system returns error and says "callback" is a not a function. What is wrong with this code?
Thanks
I made another approach. It is based on jQueryMobile but can also be used with normal jQuery after a few minor modifications. The basic is simple: you call a function that opens the popup and adds two buttons that react on functions that you provide by calling the opener-function. Here is my example:
function jqConfirm(data) {
var container = $('#genericConfirm');
container.find('h1').html(data.title); // title of the confirm dialog
container.find('p').html(data.message); // message to show
// .off('click') removes all click-events. click() attaches new events
container.find('.yes').off("click").click(data.yes); // data.yes/no are functions that you provide
container.find('.no').off("click").click(data.no);
container.popup({ positionTo: "window" }).popup("open"); // .popup("open") opens the popup
}
var confirmData = {
title: "Send this message?",
message: "Are you sure you want to send this message?",
yes: function() {
sendMessage();
$('#genericConfirm').popup("close");
},
no: function() {
$('#genericConfirm').popup("close");
}
};
jqConfirm(confirmData); // you open the popup with this function (indirectly)
And the HTML part (jQueryMobile specific, must be modified a bit to match plain jQuery)
<div data-role="popup" id="genericConfirm" data-overlay-theme="a" data-theme="c" style="max-width:400px;" class="ui-corner-all">
<div data-role="header" data-theme="a" class="ui-corner-top">
<h1>Title</h1>
</div>
<div data-role="content" data-theme="d" class="ui-corner-bottom ui-content">
<p style="margin-bottom: 15px;">Message</p>
<div class="ui-grid-a">
<div class="ui-block-a">
Cancel
</div>
<div class="ui-block-b">
Ok
</div>
</div>
</div>
</div>
Attach another handler to the button that deals with the logic. After you are done with the dialog, simply remove the dialog to get rid of the handlers. If you create an other dialog later, maybe with the same or an other layout, you can define different handlers for that dialog. As events 'bubble' up, both the handler on the button itself, as the handler on html (that only fires when the button is clicked), will be fired.
The following is merely pseudo-code, but it should give you an idea what you can do with this:
//This will create the html for your dialog
createMyFancyDialog();
showDialog();
//This is where you'll do the logic for this dialog
$('#confirmation_yes').on( 'click', function() {
doWhatEveryIWantToDo();
//After this dialog is done, destroy the dialog.
//This will get rid of the handlers we don't need in a future dialog.
//The handler attached to html will persist.
destroyDialog();
} );
$('#confirmation_no').on( 'click', function() {
doSomethingMean();
//After this dialog is done, destroy the dialog.
//This will get rid of the handlers we don't need in a future dialog.
//The handler attached to html will persist.
destroyDialog();
} );
I've tried to rewrite and fix your code, but there are too many things to adjust.
So, I strongly recommend you to rewrite your code in a simpler way, like this:
<button id="element">Element</button>
<div id="popup_panel" style="display: none;">
<p>Your msg?</p>
<button id="confirmation_yes" >Yes</button>
<button id="confirmation_no">No</button>
</div>
<script>
$(document).ready(function () {
$('#confirmation_yes').click(actionConfirmed);
$('#confirmation_no').click(actionNotConfirmed);
$('#element').click(showPopupPanel);
});
function actionConfirmed() {
$('#popup_panel').slideUp();
// User confirmed. Now continue.
// e.g. alert('Yes was clicked');
}
function actionNotConfirmed() {
$('#popup_panel').slideUp();
// User said no.
}
function showPopupPanel() {
$('#popup_panel').slideDown();
}
</script>
You can see this code in action here:
http://jsfiddle.net/LGTSK/
Keep it simple:
<!doctype html>
<html lang="en">
<head>
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
</head>
<body>
<input id='yes' type="button" value="Yes" data-result="Yes" />
<input id='no' type="button" value="No" data-result="No" />
<script type="text/javascript">
var result;
function confirmAction() {
if (result) alert(result);
else setTimeout(confirmAction, 100);
}
$(function() {
$('#yes, #no').on('click', function(e) {
result = $(this).attr('data-result');
});
confirmAction()
});
</script>
</body>
</html>

knockout.js "with" binding and dynamic html

I want to have a modal dialog to appear with some content and buttons inside it. The dialog should be bound to some observable property or not, the dialog also must have close buttons, one inside its body, another on the top right corner. My main aim is to close this modal form with these buttons, but "Cancel" button inside dialog's body doesn't work as expected.
1) First approach:
In this example dialog is created with static dialog, on "Open dialog" button click it shows up, it gets closed if clicked on top right X link, but it doesn't close on "Close" button click, however I set my observable to null. I was pretty much sure about this approach, as it was described in this brilliant explanation.
Excerpt from my code:
HTML:
<button data-bind="click: openDialog">Open dialog</button>
<div data-bind="with: dialogOpener">
<div data-bind="dialog: { data: $data, options: { close: Close } }">
<button data-bind="click: Save">Save</button>
<button data-bind="click: Close">Cancel</button>
</div>
</div>
JS:
self.dialogOpener = ko.observable();
self.openDialog = function () {
var data = {
Save: function() {
alert('Saved');
},
Close: function() {
alert('Closed');
self.dialogOpener(null);
}
}
self.dialogOpener(data);
}
Fully working example:
http://jsfiddle.net/cQLbX/
2) Second approach shows how my dialog html is dynamically created and it has the contents and the same results as in the first example.
Excerpt from my code:
HTML:
<button data-bind="click: openDialog">Open dialog</button>
JS:
self.dialogOpener = ko.observable();
self.openDialog = function () {
var element = "";
element += '<div data-bind="with: $data">';
element += '<div data-bind="dialog: { data: $data, options: { close: Close } }">';
element += '<button data-bind="click: Save">Save</button>';
element += '<button data-bind="click: Close">Cancel</button>';
element += '</div>';
element += '</div>';
var data = {
Save: function() {
alert('Saved');
},
Close: function() {
alert('Closed');
self.dialogOpener(null);
}
}
self.dialogOpener(data);
ko.applyBindings(data, $(element)[0]);
}
Fully working example:
http://jsfiddle.net/6T3Ra/
My question is:
On both examples "Cancel" button inside body doesn't work, the dialog doesn't close, what am I doing wrong and how to solve this?
Thanks a lot!
made a bunch of changes to your fiddle, maybe not how you want to do it, but the cancel and x buttons both do the same thing now
http://jsfiddle.net/cQLbX/3/
<div data-bind="dialog: dialogOpener, dialogOptions: { autoOpen: false, close: Close, buttons: { 'Save': Save, 'Cancel': Close } }">
<div data-bind='with: dialogContent'>
<div data-bind="text: Test"></div>
</div>
</div>
i usually structure my dialogs like this, and i've had success with them.
I don't know if you use any plugins and what not, but looking at your js fiddle example no2 with the help of a great thing called debugger is that you aren't explicitly telling the element to hide. A solution to this could be the following:
//If you look at E, E would be the ViewModel and X would be the jQuery Event Click
Close: function(e, x) {
//from the event we have currentTarget which is the button that was pressed.
//parentElement would be the first element, and the next parentElement was
//the modal in your demo. When we call hide() it hides the modal from
//which the button was pressed.
$(x.currentTarget.parentElement.parentElement).hide();
//left these as is from your example
alert('Closed');
self.dialogOpener(null);
}

Categories

Resources