Click Font Awesome icon within Handlebar template - javascript

I'm having some trouble triggering popover when clicking a Font Awesome icon which is created dynamically.
index.ejs
<!-- Template for Snazzy Window -->
<script id="marker-content-template" type="text/x-handlebars-template">
<div class="custom-img" style="background-image: url({{{bgImg}}})"></div>
<section class="custom-content">
<h1 class="custom-header">
{{title}} <i class="fa fa-question-circle" data-placement="right" data-toggle="popover" data-container="body" data-content="And here's some amazing content. It's very engaging. Right?"></i>
<small>{{governance}}</small>
</h1>
<div class="custom-body">{{{body}}}</div>
</section>
</script>
<script type="text/javascript>
$(function() {
var template = Handlebars.compile($('#marker-content-template').html());
});
</script>
I've tried various ways including
$(document).on('click', '.fa', function(){
$('[data-toggle="popover"]').popover('toggle');
});
This doesn't work, and now I'm thinking does it have something to do with the Handlebar template?
How can I approach this?

As you run the
$(document).on('click', '.fa', function(){
$('[data-toggle="popover"]').popover('toggle');
});
, handlebar created the element but hasn't added it to the DOM.
So in your code
// 1. Create the element from template with handlebar
var template = Handlebars.compile($('#marker-content-template').html());
// 2. Add the element to the DOM
document.getElementById('#yourTargetContainerId').innerHTML = template;
// 3. add the eventlistener to the elements
$(document).on('click', '.fa', function(){
$('[data-toggle="popover"]').popover('toggle');
});

Related

Bootstrap Tooltip remains stuck

I am dynamically creating a div using jquery which contains add & close button. I am using Bootstrap tooltip for the add & close buttons. The problem that I am facing is Tooltip of the first add button doesn't gets hidden even when the mouse is hovering other add button. The tooltip of first add button remains as it is.(Refer screenshot) Any idea as to how to make it hidden.
I am using jquery clone method to create the dynamic divs.
$(document).on('click', ".addFilesBtn", function(e) {
e.preventDefault();
$(".appendClass:first").clone().appendTo".addFiles");
$('.closeFilesBtn').show();
$('.closeFilesBtn:first').hide();
});
Also for hiding the tooltips, I am using the below code but still the first tooltip is not getting hidden.
$(document).on('mouseleave','[data-toggle="tooltip"]', function(){
$(this).tooltip('hide');
});
updated HTML Code
<div class="row addFiles">
<div class="appendClass" style="margin-bottom: 1.5%;">
<label style="white-space: nowrap;" class="responsive-enter-details col-sm-3 control-label">Select Files</label>
<div class="col-sm-8 col-md-9">
<div class="fileinput fileinput-new" data-provides="fileinput">
<div class="form-control" data-trigger="fileinput">
<i class="glyphicon glyphicon-file fileinput-exists"></i> <span class="fileinput-filename">Click to select file</span> <i class="fa fa-upload pull-right"></i>
</div>
<input id="inputfile" type="file" style="display: none;">
</div>
</div>
<button class="btn btn-box-tool addFilesBtn" data-toggle="tooltip" title="Click to add more files">
<i class="fa fa-plus"></i>
</button>
<button class="btn btn-box-tool closeFilesBtn" data-toggle="tooltip" title="Click to remove this block">
<i class="fa fa-times"></i>
</button>
</div>
</div>
Link to JS Fiddle:
https://jsfiddle.net/gLkrsbxc/4/
As you can see in JS Fiddle, the tooltip isn't getting closed.
Please check the last update for solution
As mentioned in the docs at http://getbootstrap.com/javascript/#tooltips, you need to initialize all the tooltips e.g.
$(function () {
$('[data-toggle="tooltip"]').tooltip()
})
So, here you just need to initialize the tooltips after adding them to the DOM.
Just add $('[data-toggle="tooltip"]').tooltip() in your click function after cloning.
$(document).on('click', ".addFilesBtn", function(e) {
e.preventDefault();
$(".appendClass:first").clone().appendTo".addFiles");
//initialize tooltips(add this line)
$('[data-toggle="tooltip"]').tooltip();
$('.closeFilesBtn').show();
$('.closeFilesBtn:first').hide();
});
And I think if initialized properly, you won't need the hide function.
Update:
I think calling the initializing function doesn't work properly because it is a problem when dom manipulation operations are performed. Add slight delay after the append function and before the initializing function with setTimeout like this:
$(document).on('click', ".addFilesBtn", function(e) {
e.preventDefault();
$(".appendClass:first").clone().appendTo".addFiles");
//initialize tooltips(give some time for dom changes)
setTimeout(function() {
$('[data-toggle="tooltip"]').tooltip();
}, 50);
$('.closeFilesBtn').show();
$('.closeFilesBtn:first').hide();
});
Update 2
Just hide the tooltip of the button you just clicked before cloning:
$(document).on('click', ".addFilesBtn", function(e) {
e.preventDefault();
//hide the tooltip
$(this).tooltip('hide');
$(".appendClass:first").clone().appendTo(".addFiles");
$('.closeFilesBtn').show();
$('.closeFilesBtn:first').hide();
});

Get the HTML Class Name inside Partial View in JavaScript or jQuery

I have the following markup inside my .cshtml Partial View and it is placed in the Views/Shared folder:
_Modal.cshtml
<div class="loadingOverlay">
<button class="btn loading-button" disabled><i class="fa fa-spinner fa-pulse fa-2x fa-fw"></i></button>
</div>
And also the External JavaScript (different file from the Partial View .cshtml):
Modal.js
var Modal;
Modal = Modal || (function () {
var loadingDiv = $('.loadingOverlay');
return {
Loading: function () {
loadingDiv.css('display', 'block');
$('body').prepend(loadingDiv);
},
LoadingDismiss: function () {
loadingDiv.fadeOut('500');
},
};
})();
And I call from my page like below:
Index.cshtml
<!DOCTYPE html>
<html>
<head>
#Styles.Render("~/Content/font-awesome", "~/Content/css")
</head>
<body>
<div class="container-fluid">
<div class="container">
<div class="row">
<button id="showOverlay" class="btn btn-default"></button>
</div>
</div>
</div>
#Scripts.Render("~/bundles/jquery")
<script type="text/javascript">
$(document).ready(function () {
$('#showOverlay').click(function () {
Modal.Loading();
});
})
</script>
</body>
</html>
My problem is: whenever I clicked the button, nothing happens. But it works when I put the html element not inside _Modal.cshtml (Refer to the below code).
My question is: how can I get the class name in the _Modal.cshtml using JavaScript or JQuery?
I can do this in the Modal.js (Note that it is same as I put that into _Modal.cshtml) but that would be hard to maintain and I would like to put all of the content in the Partial View, and call it when needed by class name, instead of write string that will converted to JavaScript Object:
var loadingDiv = $('<div class="loadingOverlay"><button class="btn loading-button" disabled>'
+ '<i class="fa fa-spinner fa-pulse fa-2x fa-fw"></i></button></div>');
Your answer much appreciated.
Edit:
Question on 31st of May 2016:
What's the difference between #Html.Partial("_Modal") with #Html.Partial("~/Views/Shared/_Modal.cshtml"). is it just to better understand for where the compiler get the partial views from?
Answered on 1st of June 2016 by #Guruprasad Rao:
There is nothing difference between two except the mentioning of partialview name. The one I suggested was providing fully qualified path which helps compiler from where to fetch the partialview. To one you use will search in all the directories under the View folder. So the one I mentioned will be good in the aspect of performance.
Thanks for the #Guruprasad Rao for the answer and the explanation.
Cheers~
You should import the partialview first from server to client browser first and then you can manipulate it with jquery. Otherwise there would be no element with the selector you are trying to find. User, #Html.Partial or #Html.RenderPartial to import it, hide it at initial state and show it on click. Take a below example:
<body>
<div class="container-fluid">
<div class="container">
<div class="row">
<button id="showOverlay" class="btn btn-default"></button>
</div>
</div>
</div>
#Html.Partial("~/Views/Shared/_Modal.cshtml")
<!--I assume that the content inside this will be initially in hidden state-->
#Scripts.Render("~/bundles/jquery")
<script type="text/javascript">
$(document).ready(function() {
$('#showOverlay').click(function() {
Modal.Loading();
});
})
</script>
</body>
Modal.js changes
var Modal;
Modal = Modal || (function () {
var loadingDiv = $('.loadingOverlay');
return {
Loading: function () {
loadingDiv.css('display', 'block');
//$('body').prepend(loadingDiv);
//No need to prepend here since we are importing it through partial method
},
LoadingDismiss: function () {
loadingDiv.fadeOut('500');
},
};
})();

How to remove data-target and data-toggle from child elements or disable elements from triggering the event?

HTML:
<div id="card_{{_id}}" class="card-panel" data-toggle="modal" data-target="#{{_id}}">
<span class="white-text">{{text}}</span>
<div class="card-action">
{{#each tags}}
<div class="chip">
<tag class="tag">{{this}}</tag>
<i id="removeTag" class="material-icons fa fa-ban"></i>
</div>
{{/each}}
<div class="chip" id="likeButton">
<i class="fa fa-thumbs-o-up"></i> {{likes}} Likes
</div>
</div>
</div>
above is sample html code from my meteor project. i want the div#card element to activate the modal by clicking on it. But i do not want the div.chip elements to toggle the modal when clicked. Is there a way i can disable child element from the data-toggle of the modal?
If you can use javascript, you can do stopPropagation for stopping click event on div.chip from bubbling to div#card.
$('.chip').on('click', function (ev) {
ev.stopPropagation();
});
For elements in a meteor template, you do something like this (replace yourTemplate with your template name),
Template.yourTemplate.events({
'click .div', function (ev, template) {
ev.stopPropagation();
}
});
See the JSFiddle

jQuery Selector - <i class>

I'm trying to inject jQuery into a HTML page to create a popup window when I click a link on the page. I'm having trouble with the Selector and/or Syntax.
HTML
<a href="/leads/19365876/edit" class="hoverable edit">
<i class="icon-pencil">
</i>
</a>
Attempted Solution
jQuery(document).ready(function($) {
jQuery('a.hoverable.edit').live('click', function(){
newwindow=window.open($(this).attr('href'),'','height=200,width=150');
if (window.focus) {newwindow.focus()}
return false;
});
});
you forgot to give '_blank' name to your window:
window.open($(this).attr('href'),'_blank','height=200,width=150');

How can I apply KnockoutJS on a master page and an individual page?

I'm stuck on an older WebForms project and I'd like to know if there's a recommended approach for my scenario.
Goal
I have a feedback form in a modal dialog that I bound up using KnockoutJS.
I would like the feedback form to be available on all pages, via a link in the footer of the site.
I would like to have several other pages using knockout as well with their own individual scripts & bindings, irrespective of the feedback form bindings in the modal.
I have some pages that do not use knockout at all. I would like them not to have to insert code to accomplish this.
I would like to avoid global variables, if possible, in favor of namespaced JavaScript.
In essence, I would like for the viewmodels on the page and the feedback viewmodel not to be aware of each others' existence.
Current Setup
Our footer links are in a Site.master file, and so that's where I've placed the Feedback.js script and the div for the modal which has the bindings. So on the master page, I call ko.applyBindings(vm, referenceToFeedbackDiv), which works fine to wire up the feedback form.
Our individual pages occasionally have a knockout viewmodel, and so they may call ko.applyBindings(vm), since to their knowledge they'd like to apply the vm to their entire page.
Problem
This causes a conflict in knockout because one vm is being applied to the feedback form via the Site.master call, and one vm is being applied to the entire body by the page after it.
Question
How can I enable these two things -- a modal dialog across all pages that uses knockout, and individual knockout pages -- to work in harmony?
Demonstration of the Issue in (the Current) Code
Remember, the issue is that I want to be able to have one feedback VM that applies only to the feedback div across the client site, and I want to have other VMs able to be applied that aren't required to know anything about the feedback vm.
Master Page file (Site.Master) -- Excerpt
This is on every page:
<div class="page">
<div class="main">
<asp:ContentPlaceHolder ID="MainContent" runat="server" />
</div>
</div>
<div class="footer">
© <%=DateTime.Now.Year.ToString() %> Company, Inc. | Home | About |
<!-- begin feedback area -->
<span id="FeedbackArea">
<a data-bind="click: showModal">Feedback</a>
<div id="feedback-modal" title="What's on your mind?">
<div class="btn-group" id="feedbackButtonGroup">
<button class="btn" data-bind="click: UpdateFeedbackType" style="padding-top: 6px;">
<i class="fa fa-warning fa-2x fa-align-center"></i>
<br />
<span>Problem</span>
</button>
<button class="btn" data-bind="click: UpdateFeedbackType" style="padding-top: 6px;">
<i class="fa fa-question-circle fa-2x fa-align-center"></i>
<br />
<span>Question</span>
</button>
<button class="btn" data-bind="click: UpdateFeedbackType" style="padding-top: 6px;">
<i class="fa fa-lightbulb-o fa-2x fa-align-center"></i>
<br />
<span>Suggestion</span>
</button>
<button class="btn" data-bind="click: UpdateFeedbackType" style="padding-top: 6px;">
<i class="fa fa-thumbs-o-up fa-2x fa-align-center"></i>
<br />
<span>Praise</span>
</button>
<button class="btn" data-bind="click: UpdateFeedbackType" style="padding-top: 6px;">
<i class="fa fa-info-circle fa-2x fa-align-center"></i>
<br />
<span>General</span>
</button>
</div>
<br />
<br />
<textarea rows="5" placeholder="Enter feedback here" data-bind="value: feedbackText, valueUpdate: 'afterkeydown'"></textarea>
<br />
<br />
<button>Send Feedback</button>
<button data-bind="click: CancelFeedback">Cancel</button>
<h3>Other Information: </h3>
<ul>
<li><strong>Feedback Type:</strong> <span data-bind="text: feedbackType"></span></li>
<li><strong>Current URL:</strong> <span data-bind="text: pageUserIsOn"></span></li>
<li><strong>Current User: </strong><%=hdnLoggedInUsername.Value %></li>
<li><strong>Current Client: </strong>[Not yet captured]</li>
<li><strong>Current Tab: </strong>[Not yet captured]</li>
</ul>
</div>
</span>
<!-- End feedback area -->
</div>
Feedback.JS -- This is also included in every page
...a somewhat-namespaced definition of a FeedbackVM:
var FeedbackNamespace = FeedbackNamespace || {};
..the definition of the namespace itself:
FeedbackNamespace = {
ViewModel: function () {
// etc. etc.
}
};
...and the declaration of a VM variable plus wiring it up on document.ready():
var FeedbackVM;
$(document).ready(function () {
FeedbackVM = new FeedbackNamespace.ViewModel();
ko.applyBindings(FeedbackVM, $('#FeedbackArea')[0]);
FeedbackVM.Start();
log('FeedbackVM started');
});
Other Pages without Knockout / JS
Other pages may or may not have any javascript on them at all, let alone knockout. On these pages, the FeedbackVM currently works fine.
Pages with their own Knockout ViewModel
These pages would have their own namespaced JS file with their own document.ready() event, that creates a vm of say invoiceUploaderVM = new InvoiceUploader.ViewModel(), and then calls ko.applyBindings(invoiceUploaderVM).
This is where we run into trouble.
Update: One potential Approach and a little trouble
In the Site.master page, I wrapped my entire footer in a "stopBindings: true" div:
<div data-bind="stopBindings: true">
<div class="footer" id="footerDiv">
<!-- Feedback Viewmodel stuff in here -->
</div>
</div>
I've defined stopBindings as:
ko.bindingHandlers.stopBindings = {
init: function () {
return { controlsDescendantBindings: true };
}
};
My Feedback.js file, loaded on every page as part of a global JS file, has:
var FeedbackNamespace = FeedbackNamespace || {};
FeedbackNamespace = {
// defines viewmodel, etc. etc.
};
var FeedbackVM;
$(document).ready(function () {
FeedbackVM = new FeedbackNamespace.ViewModel();
ko.applyBindings(FeedbackVM, $('#footerDiv')[0]);
FeedbackVM.Start();
log('FeedbackVM started');
});
This approach works perfectly well -- as long as there are no other viewmodels being bound. On the pages that inherit from my master page, I might have something like:
$(document).ready(function () {
'use strict';
vm = new invoiceUploader.ViewModel();
ko.applyBindings(vm);
});
I would expect that this:
Sets up the feedback viewmodel applied to the div, stopping other viewmodels
Sets up the invoiceUploader viewmodel and applies it to the body (which is then stopped by the stopBindings div)
However, instead I get an error upon loading the child page along the lines of:
Commenting the line to apply the feedback bindings makes this work just fine again.
What am I doing wrong?
I think I would put the view model for your modal in a global object and do whatever you need to do with it aside from applying the bindings in a shared script:
window.feedbackModal = {
foo: ko.observable("Whatever you need to do here"),
bar: ko.observable("assuming it can be done the same on every page")
};
Then in the Site.master
<div class="feedback-modal" data-bind="with: feedbackModal">
<p data-bind="text: foo"></p>
<p data-bind="text: bar"></p>
</div>
And in every individual page's script:
function ViewModel() {
this.individualProperty = ko.observable(true);
this.specificAction = function() { /* do something specific to this page */ };
this.feedbackModal = window.feedbackModal;
}
ko.applyBindings(new ViewModel());
So window.feedbackModal could be undefined and it won't cause you problems, but if you ko.applyBindings, you have to have a feedbackModal property exposed in the view model or you'll get errors applying those bindings.
Of course, there are more clever ways you could implement this basic idea in order to fit your patterns the best, but the big point is, as you know, you can't apply bindings twice, so you need to defer that task to your most specific code and expose your reusable code to to it.
Here is another strategy for separation of common modules from page dependant modules:
// An example of a module that runs on everypage
var modalDialog = function(){
this.name = "dialog1";
this.title = ko.observable("My Modal Title");
this.content = ko.observable("My Modal content is also something");
}
// An example of a module that runs on everypage
var modalDialog2 = function(){
this.name = "dialog2";
this.title = ko.observable("My Modal Title 2");
this.content = ko.observable("My Modal content is also something 2");
}
// Either generate it automatically or by hand
// to represent which modules are common
var commonModules = [modalDialog, modalDialog2];
// An example of a module only for this page
var pageModule = function(){
this.pageFunction = function(){
alert("Called page function");
}
}
// Composition is the final object you will actually bind to the page
var composition = {
pageMod: new pageModule()
}
// Let's add the common modules to the composition
ko.utils.arrayForEach(commonModules, function(item){
var module = new item();
composition[module.name] = module;
});
// Bind the composition
ko.applyBindings(composition);
example HTML for this would be:
<div class="modalDialog">
<h2 data-bind="text: dialog1.title"><h2>
<h2 data-bind="text: dialog1.content"><h2>
</div>
<div class="modalDialog">
<h2 data-bind="text: dialog2.title"><h2>
<h2 data-bind="text: dialog2.content"><h2>
</div>
<div id="content">
<h2>Welcome to page</h2>
<div id="somePageStuff">
Click me
</div>
</div>
Link to the jsfille for this
You can set this up by using a technique to not have scope your bindings to a specific area in your page.
Check out: How to stop knockout.js bindings evaluating on child elements
Example:
http://jsfiddle.net/anAgent/RfM2R/
HTML
<div id="Main">
<label data-bind="text: ViewModel.Name">default</label>
<div data-bind="stopBindings: true">
<div id="ChildBinding">
<label data-bind="text: AnotherViewModel.Name">default</label>
</div>
</div>
</div>
JavaScript
$(function () {
ko.bindingHandlers.stopBindings = {
init: function () {
return {
controlsDescendantBindings: true
};
}
};
var data = {
ViewModel: {
Name: "Testing"
}
};
var data2 = {
AnotherViewModel: {
Name: "More Testing"
}
};
ko.applyBindings(data, $("#Main")[0]);
ko.applyBindings(data2, $("#MyModalHtml")[0]);
});

Categories

Resources