I have a bootstrap 4 popover with list inside which comes from a viewcomponent element (asp net core 3.1 project).
This popover is inside layout navigation bar (not sure if that's relevant) and after hovering over it the list should appear.
Now every list element appears correctly but after I click any of the list elements I want to send ajax post request but I cannot make my javascript work. I am not sure what is the problem. Is it because my script is only loaded when I hover on popover? Am I using events incorrectly? I tried a lot of variations to test if JS is working and seems like everything failed.
ViewComponent:
#model mymodel;
<div class='list-group'>
#foreach (var item in Model.Item)
{
<a href='#item.Url' class='btn list-group-item list-group-item-action mb-1'>You have been invited to join event: <b>#item.Name</b></a>
}
#foreach (var item in Model.AnotherItem)
{
<a id='lmao' href='#' onclick='doSomething(#item.Id);' class='list-group-item list-group-item-action mb-1'>You have been invited to join team: <b>#item.Name</b></a>
}
</div>
<script>
$('#lmao').click(function () { MyFunction(); return false; });
function MyFunction() {
alert('hello');
console.log(data);
console.log(this);
}
</script>
_Layout Page with popover:
<div class="navbar-collapse">
<ul class="navbar-nav">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" data-toggle="dropdown">
Settings
</a>
...
<a tabindex="0" class="dropdown-item pop" data-container="body" data-html="true" title="Your invitations" data-toggle="popover" data-placement="left" data-content="#await Component.InvokeAsync("InvitationList")" style="cursor: pointer;">Invitations (number coming from another viewcomponent)</a>
...
</div>
</li>
...
Popover JS in Layout:
<script>
$('.pop').popover({
trigger: 'manual',
html: true
})
.on('mouseenter', function () {
var _this = this;
$(this).popover('show');
$('.popover').on('mouseleave', function () {
$(_this).popover('hide');
});
}).on('mouseleave', function () {
var _this = this;
setTimeout(function () {
if (!$('.popover:hover').length) {
$(_this).popover('hide');
}
}, 300);
});
</script>
My popover works correctly, I see the items loaded from viewcomponent, I can press on each of them and go to preset href.
What doesn't work is javascript coming from viewcomponent or atleast I am failing to test it. My end goal is to make a post call if any of the items in the popover are clicked.
I tested your codes and found if you directly call viewcomponent in data-content, the jacascripts in ViewComponent will be recongnized as html, so it will never be called. Instead, you can define an area for viewcomponent separately
<a id="invitations" tabindex="0" class="dropdown-item pop" data-container="body" data-html="true" title="Your invitations" data-toggle="popover" data-placement="left" style="cursor: pointer;" data-content="">invitations</a>
<div id="vc" style="display:none">
#await Component.InvokeAsync("InvitationList")
</div>
Use js to append it to data-content
$("#invitations").attr("data-content", $("#vc").html())
Since it is a new element from other page, the handler won't be bound on that new element, your click function should be like this:
<script>
$('body').on('click', '#lmao', function () { MyFunction(); return false; });
function MyFunction() {
alert('hello');
console.log(data);
console.log(this);
}
</script>
Test result:
Related
I am trying to display HTML inside a bootstrap popover, but somehow it's not working. I found some answers here but it won't work for me. Please let me know if I'm doing something wrong.
<script>
$(function(){
$('[rel=popover]').popover({
html : true,
content: function() {
return $('#popover_content_wrapper').html();
}
});
});
</script>
<li href="#" id="example" rel="popover" data-content="" data-original-title="A Title">
popover
</li>
<div id="popover_content_wrapper" style="display: none">
<div>This is your div content</div>
</div>
You cannot use <li href="#" since it belongs to <a href="#" that's why it wasn't working, change it and it's all good.
Here is working JSFiddle which shows you how to create bootstrap popover.
Relevant parts of the code is below:
HTML:
<!--
Note: Popover content is read from "data-content" and "title" tags.
-->
<a tabindex="0"
class="btn btn-lg btn-primary"
role="button"
data-html="true"
data-toggle="popover"
data-trigger="focus"
title="<b>Example popover</b> - title"
data-content="<div><b>Example popover</b> - content</div>">Example popover</a>
JavaScript:
$(function(){
// Enables popover
$("[data-toggle=popover]").popover();
});
And by the way, you always need at least $("[data-toggle=popover]").popover(); to enable the popover. But in place of data-toggle="popover" you can also use id="my-popover" or class="my-popover". Just remember to enable them using e.g: $("#my-popover").popover(); in those cases.
Here is the link to the complete spec:
Bootstrap Popover
Bonus:
If for some reason you don't like or cannot read content of a popup from the data-content and title tags. You can also use e.g. hidden divs and a bit more JavaScript. Here is an example about that.
you can use attribute data-html="true":
<a href="#" id="example" rel="popover"
data-content="<div>This <b>is</b> your div content</div>"
data-html="true" data-original-title="A Title">popover</a>
Another way to specify the popover content in a reusable way is to create a new data attribute like data-popover-content and use it like this:
HTML:
<!-- Popover #1 -->
<a class="btn btn-primary" data-placement="top" data-popover-content="#a1" data-toggle="popover" data-trigger="focus" href="#" tabindex="0">Popover Example</a>
<!-- Content for Popover #1 -->
<div class="hidden" id="a1">
<div class="popover-heading">
This is the heading for #1
</div>
<div class="popover-body">
This is the body for #1
</div>
</div>
JS:
$(function(){
$("[data-toggle=popover]").popover({
html : true,
content: function() {
var content = $(this).attr("data-popover-content");
return $(content).children(".popover-body").html();
},
title: function() {
var title = $(this).attr("data-popover-content");
return $(title).children(".popover-heading").html();
}
});
});
This can be useful when you have a lot of html to place into your popovers.
Here is an example fiddle: http://jsfiddle.net/z824fn6b/
You need to create a popover instance that has the html option enabled (place this in your javascript file after the popover JS code):
$('.popover-with-html').popover({ html : true });
I used a pop over inside a list, Im giving an example via HTML
<a type="button" data-container="body" data-toggle="popover" data-html="true" data-placement="right" data-content='<ul class="nav"><li><a href="#">hola</li><li><a href="#">hola2</li></ul>'>
You only need put data-html="true" in the link popover. Is gonna work.
This is an old question, but this is another way, using jQuery to reuse the popover and to keep using the original bootstrap data attributes to make it more semantic:
The link
<a href="#" rel="popover" data-trigger="focus" data-popover-content="#popover">
Show it!
</a>
Custom content to show
<!-- Let's show the Bootstrap nav on the popover-->
<div id="list-popover" class="hide">
<ul class="nav nav-pills nav-stacked">
<li>Action</li>
<li>Another action</li>
<li>Something else here</li>
<li>Separated link</li>
</ul>
</div>
Javascript
$('[rel="popover"]').popover({
container: 'body',
html: true,
content: function () {
var clone = $($(this).data('popover-content')).clone(true).removeClass('hide');
return clone;
}
});
Fiddle with complete example:
http://jsfiddle.net/tomsarduy/262w45L5/
This is a slight modification on Jack's excellent answer.
The following makes sure simple popovers, without HTML content, remain unaffected.
JavaScript:
$(function(){
$('[data-toggle=popover]:not([data-popover-content])').popover();
$('[data-toggle=popover][data-popover-content]').popover({
html : true,
content: function() {
var content = $(this).attr("data-popover-content");
return $(content).children(".popover-body").html();
},
title: function() {
var title = $(this).attr("data-popover-content");
return $(title).children(".popover-heading").html();
}
});
});
On the latest version of bootstrap 4.6, you might also need to use sanitize:false for adding complex html.
$('.popover-with-html').popover({ html : true, sanitize : false })
I really hate to put long HTML inside of the attribute, here is my solution, clear and simple (replace ? with whatever you want):
<a class="btn-lg popover-dismiss" data-placement="bottom" data-toggle="popover" title="Help">
<h2>Some title</h2>
Some text
</a>
then
var help = $('.popover-dismiss');
help.attr('data-content', help.html()).text(' ? ').popover({trigger: 'hover', html: true});
You can change the 'template/popover/popover.html' in file 'ui-bootstrap-tpls-0.11.0.js'
Write: "bind-html-unsafe" instead of "ng-bind"
It will show all popover with html.
*its unsafe html. Use only if you trust the html.
For Bootstrap >= 5.2
To enable HTML content in Popovers: data-bs-html="true"
Example:
<a href="#"
data-bs-toggle="popover"
data-bs-title="A Title"
data-bs-html="true"
data-bs-content="This is <strong>bold</strong>">popover</a>
Doc: https://getbootstrap.com/docs/5.3/components/popovers/#options
You can use the popover event, and control the width by attribute 'data-width'
$('[data-toggle="popover-huongdan"]').popover({ html: true });
$('[data-toggle="popover-huongdan"]').on("shown.bs.popover", function () {
var width = $(this).attr("data-width") == undefined ? 276 : parseInt($(this).attr("data-width"));
$("div[id^=popover]").css("max-width", width);
});
<a class="position-absolute" href="javascript:void(0);" data-toggle="popover-huongdan" data-trigger="hover" data-width="500" title="title-popover" data-content="html-content-code">
<i class="far fa-question-circle"></i>
</a>
Actually if you're using Bootstrap5 with Django then their method of passing in content as a string is perfect and in line with Django's template inclusion. You can create a template file with whatever partial HTML that you need, so for example, there is not X-editable for Bootstrap5 that seems to work, so maybe you'd want to make a line edit together with Ok|Cancel buttons as content. Anyway, this is what I mean:
<button data-bs-content="{% include './popover_content.html' %}" type="button" class="btn btn-lg btn-danger" data-bs-toggle="popover" title="Popover title" >
Click to toggle popover
</button>
Where my settings.py templates section looks like this:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True, # True is necessary for django-bootstrap5 to work!
'OPTIONS': {
'debug': True,
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
I keep my templates (of every single app) in a <project dir>/templates/<app name> folder. I have MyMainApp/popover_content.html right beside MyMainApp/home.html wher the above example code was tested. But if you keep your templates in each app's Django folder, then you'll need to add "MyApp/templates" to the TEMPLATES[0]{'DIRS': ['MyApp/templates', 'MyApp2/templates']} list.
So at least this will give you the ability to put your popover HTML in the usual, syntax-highlighted Django template format, and makes good use of modularizaton of your Django template into components.
I'm personally going to use it to make an editable label (title and description fields of some data in my app).
One drawback is that if you use doublequotes (") when including: "{% include './popover_content.html' %}", then you must use single quotes all throughout the popover_content.html` template.
You also need to enable html for popovers, so your site-wide popover initializer would go:
<script type="text/javascript">
$(document).ready(() => {
var popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'))
var popoverList = popoverTriggerList.map(
function (popoverTriggerEl) {
return new bootstrap.Popover(popoverTriggerEl, {
html: true,
});
});
});
</script>
Here is the (unstyled) result. In conclusion, use the default-provided string method of passing in, and pass in an included Django template file. Problem solved!
I would like to keep my dropdown opened after a page load. For example if I click on an item(href) into an dropdown, I would like to keep the dropdown opened after the redirection.
I've seen that there is a method jquery named stopPropagation, but it seems that this does not work for me
HTML
<div class="sidenav">
<div class="item_sn">
Groups
</div>
<div class="item_list_body">
<div class="link_sidenav">
<a href="#" class="sub_item">
group 1
</a>
</div>
<div class="link_sidenav">
<a href="#" class="sub_item">
group 2
</a>
</div>
</div>
<div class="item_sn">
Users
</div>
<div class="item_list_body">
<div class="link_sidenav">
<a href="#" class="sub_item">
user 1
</a>
</div>
<div class="link_sidenav">
<a href="#" class="sub_item">
user 2
</a>
</div>
</div>
</div>
JQuery
<script>
$(document).ready(function () {
$('.item_list_body').hide();
$('.item_sn').on('click', function (event) {
var content = $(this).next('.item_list_body')
content.slideToggle();
$('.item_list_body').not(content).slideUp();
});
});
</script>
Solution 1 (not working) :
$(document).on('click', '.sub_item', function (e) {
$(this).parent('.link_sidenav').parent('.item_list_body').toggle();
});
you can use sessionStorage to store the state of the menu and then open the menu on page load by checking the state see below
EDIT
rather than using state of the menu we should save the index of the clicked menu as discussed in the comment so updated my answer
$(document).ready(function () {
//sessionStorage.removeItem('opened');
$('.item_list_body').hide();
if (sessionStorage.getItem('opened') !== null) {
$('.sidenav>div:eq(' + sessionStorage.getItem('opened') + ')').next('.item_list_body').show();
}
$('.item_sn').on('click', function (event) {
var content = $(this).next('.item_list_body');
var elem = $(this);
content.slideDown(0, function () {
sessionStorage.setItem('opened', elem.index());
});
$('.item_list_body').not(content).slideUp();
});
});
Hope it helps
I have a Backbone view, it uses an html template, placed on the page:
<script type="text/template" id="webphone-template">
<audio id="audio-ringtone" src="{% static "prime_app/assets/audio/ringtone.mp3" %}"></audio>
<div class="well">
<p class="text-muted text-center"><%= status %></p>
<div class="text-center">
<button id="btn-webphone-answer" class="btn btn-success">
<i class="fa fa-phone"></i>
</button>
<button id="btn-webphone-terminate" class="btn btn-danger">
<i class="fa fa-phone"></i>
</button>
</div>
</div>
</script>
My view has method ring(), which looks like this:
ring: function() {
$("#audio-ringtone").get(0).play();
}
But when this method is invoked audio doesn't play, no errors in JS console, the audio element is chosen correctly. What's the problem?
Well i dont know how your whole view works, if you add it i can modify the code, but i got it to work fairly easy. Again, i need to see your view code to see what youre doing wrong.
View = Backbone.View.extend({
template: _.template($('#webphone-template').html()),
events: {
'click #btn-webphone-answer': 'ring'
},
initialize: function(opt) {
// attach to the main body
$(opt['main']).append(this.el)
this.status = 'paused';
this.render();
},
ring: function() {
$(this.el).find('#audio-ringtone')[0].play()
},
render: function() {
$(this.el).append(this.template({status: this.status}));
return this;
}
});
new View({ main: $('body') });
works: http://jsfiddle.net/hLero5rd/
Finally, there was the following problem: audio-tag is in my view's element, when i call render(), it completely replaces all the HTML inside element, so if there is audio-element it just gets removed and the sound doesn't play anymore.
I'm trying to capture when someone tries to close "edit mode" on a page and tell them to save changes.
What triggers edit mode is a collapse element, first child is "View", second child is "Edit". I also have an event listener attached to the "hide" event of the edit child, so when they click to collapse the edit mode, they will receive a message asking them if they want to continue without saving. Problem is, I ask them, and if they hit continue, everything is fine, but if they hit cancel, I want the edit mode to STOP collapsing, and remain as it is.
I've tried with e.preventDefault, stopImmediatePropagation, return false, nothing works.
HTML:
<div class="accordion" id="my-accordion">
<div class="accordion-group">
<div class="accordion-heading">
<ul class="nav nav-tabs tab-items">
<li id="firstTab" class="active">First</li>
<li id="secondTab">Second</li>
</ul>
</div>
<div id="collapseOne" class="accordion-body collapse in">
</div>
</div>
<div class="accordion-group" id="edit" style="display:none;">
<div class="accordion-heading center">
<a id="edit-toggler" class="accordion-toggle" data-toggle="collapse" data-parent="#my-accordion" href="#collapseTwo">
<i class='icon-arrow-up'></i> Open Edit <i class='icon-arrow-up'></i>
</a>
</div>
<div id="collapseTwo" class="accordion-body collapse">
</div>
</div>
</div>
JavaScript:
$('#batch-edit').on('hide', function () { // THIS IS FOR THE BATCH EDIT BUTTON FUNCTIONALITY
$('#collapseOne').collapse('toggle');
$('#edit-toggler').html("<i class='icon-arrow-up'></i> Open Edit <i class='icon-arrow-up'></i>");
isEdit = false;
if(UserEditing){
if(!confirm('Leave without saving?')) {
return false;
}
}
// DO SOME STUFF
});
Any help would be greatly appreciated.
Unfortunately, there is no native hide/show event you can bind to. This just means we need to trigger these custom events ourselves.
Take a look at this SO answer. Here is the link to the fiddle.
I did something similar to the solution in the answer linked above in order to get beforeHide, afterHide, beforeShow and afterShow events.
The snippet below can demo the events mentioned above.
var $show = $('#show'),
$hide = $('#hide'),
$batchEdit = $('#batch-edit');
$show.hide();
$show.on('click', function()
{
$show.hide();
$hide.show();
$batchEdit.show();
});
$hide.on('click', function()
{
$show.show();
$hide.hide();
$batchEdit.hide();
});
$batchEdit.on('beforeHide', function ()
{
alert('triggered beforeHide');
$show.show();
$hide.hide();
})
.on('afterHide', function ()
{
alert('triggered afterHide');
})
.on('beforeShow', function ()
{
alert('triggered beforeShow');
$show.hide();
$hide.show();
})
.on('afterShow', function ()
{
alert('triggered afterShow');
});
(function($){
$.override ={'show': $.fn.show, 'hide': $.fn.hide};
$.each($.override,function(M,F){
var m=M.replace( /^\w/, function(r){ return r.toUpperCase(); });
$.fn[M] = function(speed, easing, callback) {
var args=[speed||0,easing||'',callback||function(){}];
if( $.isFunction(speed)){
args[2]=speed;
args[0]=0;
}
if( $.isFunction(easing)){
args[2]=easing;
args[1]='';
}
if(!this.selector){
F.apply(this, arguments);
return this;
}
return this.each(function () {
var obj = $(this),
oldCallback = args[args.length-1],
newCallback = function () {
if ($.isFunction(oldCallback)){
oldCallback.apply(obj);
}
obj.trigger('after'+m);
};
obj.trigger('before'+m);
args[args.length-1]=newCallback;
//alert(args);
F.apply(obj,args);
});
}
});
})(jQuery);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<div id="batch-edit">Batch Edit</div>
<input type='button' id="show" value='Show'>
<input type='button' id="hide" value = 'Hide'>
Is it possible to have a custom bootstrap popover?
I mean I want to be able to use
$('#example').popover(options)
So on click of an element #example, I'll pass some text (which would be shown in editable textarea);
I am using bootstrap 2.3.2
I dont think the links in the comments completely answers the question. Here is a 2.3.2 example, working with multiple links / elements, that passes text() from the element to a textarea on the popover, and back to the element upon "submit" :
awesome user
Use popovers template feature to customize the popover (adding buttons), set a <textarea> as content, inject the text of the link / element to the textarea on the shown event :
$("[rel=comments]").popover({
trigger : 'click',
placement : 'top',
html: 'true',
content : '<textarea class="popover-textarea"></textarea>',
template: '<div class="popover"><div class="arrow"></div>'+
'<h3 class="popover-title"></h3><div class="popover-content">'+
'</div><div class="popover-footer"><button type="button" class="btn btn-primary popover-submit">'+
'<i class="icon-ok icon-white"></i></button> '+
'<button type="button" class="btn btn-default popover-cancel">'+
'<i class="icon-remove"></i></button></div></div>'
})
.on('shown', function() {
//hide any visible comment-popover
$("[rel=comments]").not(this).popover('hide');
var $this = $(this);
//attach link text
$('.popover-textarea').val($this.text()).focus();
//close on cancel
$('.popover-cancel').click(function() {
$this.popover('hide');
});
//update link text on submit
$('.popover-submit').click(function() {
$this.text($('.popover-textarea').val());
$this.popover('hide');
});
});
see fiddle -> http://jsfiddle.net/e4zMu/ here with three editable links / elements :
In the event you want to use RAZOR or HTML actually be used as the template for the popover (rather than injecting it through the attribute in JS):
In the following example, we were building a bootstrap Breadcrumb control with a popover that contained a list of values that might be selected to change the value of a breadcrumb.
we were using razor to create the HTML TEMLPLATE for the popover-content.
This is how the popover used HTML for its 'content':
<script type='text/javascript'>
$(function () {
$('a[data-toggle="popover"]').popover({
html: true,
content: function () {
return $($(this).data('contentwrapper')).html();
}
});
});
</script>
What this did was for the content attribute, we used jquery to search for a data-contentwrapper within this breadcrumb.
We used Razor to create each breadcrumb element (using orderlist / listitem) and a div containing the proper id to be used in our data-toggle.
<ol class="breadcrumb">
#foreach (var segment in Model.Segments)
{
var selectedChild = segment.SelectedChild;
var popoverId = segment.Id + "_breadcrumb_popover";
var longCaption = segment.Caption;
var shortCaption = segment.Id;
var childType = segment.ChildType;
if (segment.SelectedChild != null)
{
shortCaption = segment.SelectedChild.Id;
}
else
{
// if the selected child is null, then we want the text to show 'select ' _grandchildType is
shortCaption = string.Format("Select {0} ?", segment.ChildType);
}
var listItemClassString = (segment.Children.Any()) ? "" : "hidden";
<!-- THIS IS THE BREADCRUMB ELEMENT -->
<li class="#listItemClassString">
<small>#childType</small>
<a href="javascript: void(0)" tabindex="0" rel="popover" data-container="body"
data-html="true" data-toggle="popover" data-placement="bottom"
data-animation="true" data-trigger="focus" title="Choose #childType" data-contentwrapper="##popoverId" >#shortCaption</a>
<i class="glyphicon glyphicon-chevron-right"></i>
</li>
<!-- THIS IS THE TEMPLATE DROPDOWNLIST FOR THe above list item -->
<div role="tooltip" title="FROM TEMPLATE" class="popover breadcrumb hidden" id="#popoverId">
<div class="arrow"></div>
#*<h3 class="popover-title"></h3>*#
<div class="popover-content" class='panel clearfix hidden' style='padding-right: 10px;'>
<ul class="list-group">
#foreach (var option in segment.Children)
{
<li class="list-group-item">
#{
var url = new UrlHelper(ViewContext.RequestContext, RouteTable.Routes).RouteUrl("DefaultWithBreadcrumb",
new
{
action = parentRouteData.Values["action"] as String,
controller = parentRouteData.Values["controller"] as String,
breadcrumbPath = option.Url
});
}
#option.Caption
</li>
<!-- class='col-md-3 col-sm-4'-->
}
#{ tabIndex++;}
</ul>
</div>
</div>
}
</ol>
Hope this helps someone who would like to combine serverside MVC with clientside bootstrap popover html content.