knockout.js "with" binding and dynamic html - javascript

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

Related

Why my plugin fires multipe callback or clicks

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/

Attach one time event to dynamically added elements

I have a web page where there is a button, when the button is clicked a Textbox is added to a DIV. Here is a similar code that I'm working with:
HTML
<button class="addText">Add Textbox</button>
<div class="textCont">
</div>
JavaScript
$(document).on("click", ".addText", function() {
var textarea = $("<textarea/>", {class: "newText"});
$(".textCont").append(textarea);
});
$(document).one("focus", ".newText", function() {
alert("Great");
});
Fiddle: http://jsfiddle.net/ErRohitAgg/g3A7T/
What I'm trying to do is to show an alert for first focus of every textbox that is added. But, instead the focus event is executing only once, and not once for each Textbox.
Is there any way the event behaves according to the functionality I need??
Add the event handler to each textarea instead
$(document).on("click", ".addText", function() {
$("<textarea/>", {
'class': 'newText',
one : {
focus: function() {
alert("Great");
}
}
}).appendTo(".textCont");
});
FIDDLE
I would rather do it by adding newclass on first focus:
$(document).on("focus", ".newText", function() {
if(!$(this).hasClass('focused')){
$(this).addClass('focused')
alert("Great");
}});
Working Demo

Bootstrap Popover + Meteor JS click event

I'm trying to fire a meteor event by clicking on a button inside a bootstrap popover window. However, the event is not getting fired.
Here is my code:
<button name="newLetter" class="glyphicon glyphicon-plus newLetter" type="button" data-container="body" data-toggle="popover" data-placement="right"></button>
<div id="popover-content" class="hide">
<textarea></textarea>
<button class='glyphicon glyphicon-ok btn btn-success btn-xs addNewLetter'></button>
</div>
Template.templateName.events({
'click .newLetter': function(e, t) {
$(".newLetter").popover({
html: true,
title: 'New Letter',
content: function() {
return $("#popover-content").html();
}
});
},
'click .addNewLetter': function(e, t) {
console.log('test');
}
});
Any help would be greatly appreciated.
First with your code, this doesn't show the popup on the first click. What you should do:
Template.templateName.rendered = function() {
$(".newLetter").popover({
html: true,
title: 'New Letter',
content: function() {
return $("#popover-content").html();
}
});
}
If you check with your debugger, you will see that each time you click on the .newLetter button, bootstrap take the content of #popover-content and place it in a new div with a class .popover. If you want to see how to bind an event on dynamically created elements, you should check this answer. (the solution is to use on())
Now, for what is happening, Meteor is binding a click event on .addNewLetter inside #popover-content and not on the dynamically created element .addNewLetter inside the div.popover, that's why it is not working. One workaround I found:
Template.templateName.rendered = function() {
$(document).on('click', '.addNewLetter', function() {
console.log('hey');
});
}

Bootstrap Tooltip - Hide when another tooltip is click

I hope someone can help.
I'm trying to hide the tooltip when another tooltip icon is clicked. It works but when I decide to click the last tooltip again it 'flashes' the tooltip.
var Hastooltip = $('.hastooltip');
HasTooltip.on('click', function(e) {
e.preventDefault();
HasTooltip.tooltip('hide');
}).tooltip({
animation: true
}).parent().delegate('.close', 'click', function() {
HasTooltip.tooltip('hide');
});
HTML
<a href="#" class="hastooltip" data-original-title="Lorem ipsum dolor sit amet, consectetur adipisicing elit.">
<h3>Info 1</h3>
</a>
<a href="#" class="hastooltip" data-original-title="Lorem ipsum dolor sit amet, consectetur adipisicing elit.">
<h3>Info 2</h3>
</a>
If it helps a following markup is added to the DOM when the user clicks on the button to display the tooltip.
<div class="tooltip"</div>
This can be handled more easily than the above answers indicate. You can do this with a single line of javascript in your show handler:
$('.tooltip').not(this).hide();
Here's a complete example. Change 'element' to match your selector.
$(element).on('show.bs.tooltip', function() {
// Only one tooltip should ever be open at a time
$('.tooltip').not(this).hide();
});
The same technique is suggested for closing popovers in this SO thread:
How can I close a Twitter Bootstrap popover with a click from anywhere (else) on the page?
You need to check if the tooltip is showing and toggle its visibility manually. This is one way of doing it.
$(function() {
var HasTooltip = $('.hastooltip');
HasTooltip.on('click', function(e) {
e.preventDefault();
var isShowing = $(this).data('isShowing');
HasTooltip.removeData('isShowing');
if (isShowing !== 'true')
{
HasTooltip.not(this).tooltip('hide');
$(this).data('isShowing', "true");
$(this).tooltip('show');
}
else
{
$(this).tooltip('hide');
}
}).tooltip({
animation: true,
trigger: 'manual'
});
});
I slightly modified the code of kiprainey
const $tooltip = $('[data-toggle="tooltip"]');
$tooltip.tooltip({
html: true,
trigger: 'click',
placement: 'bottom',
});
$tooltip.on('show.bs.tooltip', () => {
$('.tooltip').not(this).remove();
});
I use remove() instead of hide()
I went into the same problem for regular tooltips. On an iPhone, they do not go away when clicking on the body (i.e. somewhere else).
My solution is that when you click on the tooltip itself, it hides. IMHO, this should be integrated in bootstrap distribution, because it is few code with a big effect.
When you have access to bootstrap sources, add
this.tip().click($.proxy(this.hide, this))
as the last line in method Tooltip.prototype.init in file tooltip.js:
Tooltip.prototype.init = function (type, element, options) {
this.enabled = true
this.type = type
this.$element = $(element)
this.options = this.getOptions(options)
var triggers = this.options.trigger.split(' ')
for (var i = triggers.length; i--;) {
var trigger = triggers[i]
if (trigger == 'click') {
this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
} else if (trigger != 'manual') {
var eventIn = trigger == 'hover' ? 'mouseenter' : 'focus'
var eventOut = trigger == 'hover' ? 'mouseleave' : 'blur'
this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
}
}
this.options.selector ?
(this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
this.fixTitle()
// Hide tooltip when clicking on it. Useful for mobile devices like iPhone where eventOut
// (see above) on $element is not triggered and you don't get rid of the tooltip anymore.
this.tip().click($.proxy(this.hide, this))
}
If you do not have the sources at hand, you can achieve the same effect with the following:
$(function()
{
// Apply tooltips
var hasTooltip = $("[data-toggle='tooltip']").tooltip();
// Loop over all elements having a tooltip now.
hasTooltip.each(function()
{
// Get the tooltip itself, i.e. the Javascript object
var $tooltip = $(this).data('bs.tooltip');
// Hide tooltip when clicking on it
$tooltip.tip().click($.proxy($tooltip.hide, $tooltip))
}
);
});
For me, that makes a good user experience on an iPhone: Click on the element to see the tooltip. Click on the tooltip that it goes away.
Re kiprainey’s answer, there is an issue in that once a tooltip has been hidden, it needs to be clicked twice to be shown again. I got around this by using tooltip('hide') instead of hide():
$(element).on('show.bs.tooltip', function() {
// Only one tooltip should ever be open at a time
$('.tooltip').not(this).tooltip('hide');
});
I was looking for a solution to this problem as well and it seems to me that $('.tooltip').not(this).hide(); will bypass any bootstrap show, shown, hide or hidden events you may have attached to the trigger element. After some thought, I've come up the following code allows for somewhat more transparent handling of attached events.
Note: tested on firefox and chrome only but should work fine in theory.
$(document).ready(function() {
$('[data-toggle="popover"]').popover();
$(document).on('show.bs.popover', function(event) {
// could use [data-toggle="popover"] instead
// using a different selector allows to have different sets of single instance popovers.
$('[data-popover-type="singleton"]').not(event.target).each(function(key, el) {
$(el).popover('hide'); // this way everything gets propagated properly
});
});
$(document).on('click', function(event) {
// choose to close all popovers if clicking on anything but a popover element.
if (!($(event.target).data('toggle') === "popover" /* the trigger buttons */
|| $(event.target).hasClass('popover') /* the popup menu */
|| $(event.target).parents('.popover[role="tooltip"]').length /* this one is a bit fiddly but also catches child elements of the popup menu. */ )) {
$('[data-toggle="popover"]').popover('hide');
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" rel="stylesheet" />
<button type="button" class="btn btn-danger" data-placement="bottom" data-toggle="popover" title="Popover One" data-content="Popover One Content. `focus` trigger still behaves as expected" data-trigger="focus" data-popover-type="singleton">Popover One</button>
<button type="button" class="btn btn-warning" data-placement="bottom" data-toggle="popover" title="Popover Two" data-content="Popover Two Content. for other triggers, clicking on content does not close popover" data-trigger="click" data-popover-type="singleton">Popover Two</button>
<button type="button" class="btn btn-success" data-placement="bottom" data-toggle="popover" title="Popover Three" data-content="Popover Three Content. clicking outside popover menu closes everything" data-trigger="click" data-popover-type="singleton">Popover Three</button>
fiddle example here: http://jsfiddle.net/ketwaroo/x6k1h7j4/
$('[data-toggle=tooltip],[rel=tooltip]').tooltip({
container: 'body' }).click(function () {
$('.tooltip').not(this).hide();
});
Thanks Jochen for the "Iphone" click on tooltip to close solution, exactly what I was looking for.
As for the original request (prevent multiple tooltip fonctionnality is an obvious need when you are asked to implement click tooltip instead of rollover ones), here is my take:
Just after , show: function () { add:
// HACK BEGIN
// Quick fix. Only one tooltip should be visible at all time.
// prototype level property are accessible to all instances so we use one to track last opened tooltip (ie. current this).
if ( (Tooltip.prototype.currentlyShownTooltip != null) || (Tooltip.prototype.currentlyShownTooltip != undefined) ) {
// Close previously opened tooltip.
if (Tooltip.prototype.currentlyShownTooltip != this) { // Conflict with toggle func. Re-show.
Tooltip.prototype.currentlyShownTooltip.hide();
Tooltip.prototype.currentlyShownTooltip = null
}
}
// Keep track of the currently opened tooltip.
Tooltip.prototype.currentlyShownTooltip = this
// HACK END
I will give you a good solution plus a bonus
//save the tooltip in variable (change the selector to suit your tooltip)
var $tooltips = $('a[data-toggle="tooltip"]');
//initialise the tooltip with 'click' trigger
$tooltips.tooltip({
animated: 'fade',
placement: 'top',
trigger: 'click',
delay: { "show": 100, "hide": 100 }
});
//Here is the juicy bit: when a tooltip is opened it
//it creates an 'aria-describedby' with the id of the tooltip
//opened we can leverage this to turn off all others but current
$tooltips.on('click', function () {
var toolTipId = $(this).attr('aria-describedby');
$('.tooltip').not('#'+ toolTipId).tooltip('hide');
});
//But wait theres more! if you call now we chuck in a free close on X seconds event!
$tooltips.on('shown.bs.tooltip', function (e) {
//Auto hide after 7 seconds
setTimeout(function () {
$(e.target).tooltip('hide');
}, 7000);
});
//call now! XD
I used class name to add tooltip and removed using class name. it's working.
Add Tooltip
$('.tooltips').tooltip({
animation: true
, container: 'body'
, html: true
, placement: 'auto'
, trigger: 'focus hover'
});
Hide Tooltip
$('.tooltips').tooltip('hide');

Hide modal window javascript

So to show this modal I have the following code:
<button type="button" onclick="openModal(); return false;">A button</button>
and the javascript for this is:
<script type='text/javascript'>
function openModal(a)
{
$.modal({
content: 'Some content here',
title: 'a title',
maxWidth: 500,
});
win.closeModal();
}
</script>
I need a function that will hide this. Can anyone give me some advice on how to do the hideModal() function which will hide the modal when I click anywhere on the screen?
With the modal open in the browser window, use the browser's console to try
var modal;
function btnsModal() {
var btns = {
'Close': function (win) {
modal.closeModal()
}
}
return btns;
}
function openModal(oLink, content) {
var btn = btnsModal();
modal = $.modal({
buttons: btn
});
}
You can add "open" event to dialog, and then bind on click listener to it which will close the dialog if you click anywhere---
open: function(){
jQuery('.ui-widget-overlay').bind('click',function(){
jQuery('#ID_of_ur_dialog').dialog('close');
})
}
for hiding effect you can use "hide" option---
hide: "highlight"
This is dumb...
I fixed this with display none in css... It turns out I didn't thick it trough... I added the display none and a JS event that triggers the CSS upon clicking anywhere else in the page, except the modal.
Thank you very much for all your input guys! Really appreciate it!

Categories

Resources