How to position a Bootstrap popover? - javascript

I'm trying to position a Bootstrap popover for a trigger that sits on the far top-right corner of a 960px wide web page.
Ideally, I'd position it on the bottom and move the arrow with CSS (so the arrow is on the top-right of the popover). Unfortunately the 'placement':'bottom' positioning is not enough, since it will center it below the trigger.
I'm looking for solution that will place the popover statically below and on the left of the trigger.

This works. Tested.
.popover {
top: 71px !important;
left: 379px !important;
}

Simply add an attribute to your popover! See my JSFiddle if you're in a hurry.
We want to add an ID or a class to a particular popover so that we may customize it the way we want via CSS.
Please note that we don't want to customize all popovers! This is terrible idea.
Here is a simple example - display the popover like this:
// We add the id 'my-popover'
$("#my-button").popover({
html : true,
placement: 'bottom'
}).data('bs.popover').tip().attr('id', 'my-popover');
#my-popover {
left: -169px!important;
}
#my-popover .arrow {
left: 90%
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link href="https://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
<button id="my-button" data-toggle="popover">My Button</button>

I've created a jQuery plugin that provides 4 additonal placements:
topLeft, topRight, bottomLeft, bottomRight
You just include either the minified js or unminified js and have the matching css (minified vs unminified) in the same folder.
https://github.com/dkleehammer/bootstrap-popover-extra-placements

Popover's Viewport (Bootstrap v3)
The best solution that will work for you in all occassions, especially if your website has a fluid width, is to use the viewport option of the Bootstrap Popover.
This will make the popover take width inside a selector you have assigned. So if the trigger button is on the right of that container, the bootstrap arrow will also appear on the right while the popover is inside that area.
See jsfiddle.net
You can also use padding if you want some space from the edge of container. If you want no padding just use viewport: '.container'
$('#popoverButton').popover({
container: 'body',
placement: "bottom",
html: true,
viewport: { selector: '.container', padding: 5 },
content: '<strong>Hello Wooooooooooooooooooooooorld</strong>'
});
in the following html example:
<div class="container">
<button type="button" id="popoverButton">Click Me!</button>
</div>
and with CSS:
.container {
text-align:right;
width: 100px;
padding: 20px;
background: blue;
}
Popover's Boundary (Bootstrap v4)
Similar to viewport, in Bootstrap version 4, popover introduced the new option boundary
https://getbootstrap.com/docs/4.1/components/popovers/#options

I solved this (partially) by adding some lines of code to the bootstrap css library. You will have to modify tooltip.js, tooltip.less, popover.js, and popover.less
in tooltip.js, add this case in the switch statement there
case 'bottom-right':
tp = {top: pos.top + pos.height, left: pos.left + pos.width}
break
in tooltip.less, add these two lines in .tooltip{}
&.bottom-right { margin-top: -2px; }
&.bottom-right .tooltip-arrow { #popoverArrow > .bottom(); }
do the same in popover.js and popover.less. Basically, wherever you find code where other positions are mentioned, add your desired position accordingly.
As I mentioned earlier, this solved the problem partially. My problem now is that the little arrow of the popover does not appear.
note: if you want to have the popover in top-left, use top attribute of '.top' and left attribute of '.left'

To bootstrap 3.0.0:
.popover{ right:0!important; }
And modify too
.popover { max-width:WWWpx!important; }
where WWW is your correct max-width to show your popover content.

I had to make the following changes for the popover to position below with some overlap and to show the arrow correctly.
js
case 'bottom-right':
tp = {top: pos.top + pos.height + 10, left: pos.left + pos.width - 40}
break
css
.popover.bottom-right .arrow {
left: 20px; /* MODIFIED */
margin-left: -11px;
border-top-width: 0;
border-bottom-color: #999;
border-bottom-color: rgba(0, 0, 0, 0.25);
top: -11px;
}
.popover.bottom-right .arrow:after {
top: 1px;
margin-left: -10px;
border-top-width: 0;
border-bottom-color: #ffffff;
}
This can be extended for arrow locations elsewhere .. enjoy!

If you take a look at bootstrap source codes, you will notice that position can be modified using margin.
So, first you should change popover template to add own css class to not get in conflict with other popovers:
$(".trigger").popover({
html: true,
placement: 'bottom',
trigger: 'click',
template: '<div class="popover popover--topright" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
});
Then using css you can easily shift popover position:
.popover.popover--topright {
/* margin-top: 0px; // Use to change vertical position */
margin-right: 40px; /* Use to change horizontal position */
}
.popover.popover--topright .arrow {
left: 88% !important; /* fix arrow position */
}
This solution would not influence other popovers you have. Same solution can be used on tooltips as well because popover class inherit from tooltip class.
Here's a simple jsFiddle

Sure you can. Fortunately there is a clean way to do that and it is in the Bootstrap popover / tooltip documentation as well.
let mySpecialTooltip = $('#mySpecialTooltip);
mySpecialTooltip.tooltip({
container: 'body',
placement: 'bottom',
html: true,
template: '<div class="tooltip your-custom-class" role="tooltip"><div class="arrow"></div><div class="tooltip-inner"></div></div>'
});
in your css file:-
.your-custom-class {
bottom: your value;
}
Make sure to add the template in bootstrap's tooltip documentation and add your custom class name and style it using css
And, that's it. You can find more about this on https://getbootstrap.com/docs/4.0/components/tooltips/

Maybe you don't need this logic for a responsive behavior.
E.g.
placement: 'auto left'
Bootstrap 3 has auto value for placement. Bootstrap doc:
When "auto" is specified, it will dynamically reorient the tooltip. For example, if placement is "auto left", the tooltip will display to the left when possible, otherwise it will display right.

Related

Bootstrap navbar overlapping content when using affix

I want
A site banner with a navbar below
The banner to disappear when scrolling down, but the navbar to
remain fixed
I found this jsfiddle which provides the above solution:
http://jsfiddle.net/DTcHh/541/
Two main points of code:
//js
$('#topnavbar').affix({
offset: {
top: $('#banner').height()
}
});
//css
#topnavbar.affix {
position: fixed;
top: 0;
width: 100%;
}
My problem now is when you scroll down to the point where the 'affix' happens. If you look carefully at that point it kinda jumps, and now the navbar is overlapping the first 4 lines in the paragraph
Any ideas how to get rid of that overlap?
You need to displace the fixed .navbar element by adding padding-top to the body element equal to the height of the fixed element.
You can listen to the affix.bs.affix/affix-top.bs.affix events and then determine whether the padding should be equal to the element's height or removed:
Updated Example - the jump you were seeing no longer occurs.
$('#topnavbar').on('affix.bs.affix affix-top.bs.affix', function (e) {
var padding = e.type === 'affix' ? $(this).height() : '';
$('body').css('padding-top', padding);
});
Add "z-index:10;" to your topnavbar.affix class in css.
position: fixed;
top: 0;
width: 100%;
z-index:10;

How to add text to Fancy Box Loader

When clicking on a link I need to load a huge pdf on FancyBox overlay. Until the pdf is loaded I'm displaying a FancyBox loader. The problem is I need to add a text like "Please Wait...etc" in the FancyBox loader. Can any one help?
This is My Code:
<p>
<a class="fancypdf" href="hugepdf.pdf">Click
Here To View The PDF</a>
</p>
<script type="text/javascript">
$(document).ready(function() {
$(".fancypdf").click(function(event) {
$.fancybox.open(this.href, {
type : "iframe"
});
$.fancybox.showLoading();
$("iframe.fancybox-iframe").load(function() {
$.fancybox.hideLoading();
content: {
text: 'Loading...',}
});
event.preventDefault();
});
});
</script>
P.S.
You can modify following fiddle.
DEMO
Please have a look at below modifications:
Updated Fiddle Link: http://jsfiddle.net/PudLq/619/
1) added CSS class as:
#fancybox-loading{
background-repeat: no-repeat;
background-position: center -108px;
text-align: center;
}
#fancybox-loading div{
margin: auto;
}
.overrideLoading{
background: none !important;
color: white;
width: 92px !important;
}
2) after showing loading animation; altering the loading div HTML as per our need as follows:
$.fancybox.showLoading();
$('#fancybox-loading').append("<div class='overrideLoading'>Please Wait...</div>");
3) On hiding the animation; As suggested by "rockmandew" there is absolutely no need of reverting our HTML/CSS changes. On calling $.fancybox.showLoading() again directly; default loading animation will be shown to user. I have tested it and added one more link in fiddle to show default loading animation. Please click on "Show Default loading" to see that effect.
I hope this will help you.
I didn't have a chance to tweak the resulting positioning being a little off-center, but this may be a more simple solution:
http://jsfiddle.net/PudLq/621/
Simply add your text to an :after pseudo element with a content: rule and modify the styles of the loading wrapper to accomodate.
here's the CSS I added:
#fancybox-loading {
background: #000;
padding: 5px;
border-radius: 6px;}
#fancybox-loading:after {
content:"Please wait...";
display:inline-block;
color:#fff;}
#fancybox-loading div {margin:auto;}
Here is a forked version of your Fiddle.
I've basically span with the text "Please Wait". Then I've applied some CSS to that to position it as you did with #fancybox-loading .
Here is the new javascript code -
$(".on").click(function () {
var target = $('#target');
var overlay = $('#overlay');
overlay.width(target.width()).height(target.height()).css({
'left': target.position().left,
'top': target.position().top
}).fadeIn(200);
$.fancybox.showLoading();
$('#fancybox-loading').css({
'left': (target.width() - $('#fancybox-loading').width()) / 2,
'top': (target.height() - $('#fancybox-loading').height()) / 2,
'margin': 0
});
var labelWidth = 80;
$('body').append($('<span>', {
'class': 'waitText'
}).text("Please Wait").css({
'width': labelWidth,
'left': (target.width() - labelWidth) / 2,
'top': ((target.height() - $('#fancybox-loading').height()) / 2) + $('#fancybox-loading').height()
}));
});
$(".off").click(function () {
$('#overlay').fadeOut(200);
$.fancybox.hideLoading();
$('.waitText').remove();
});
And my new CSS -
.waitText {
position: fixed;
margin: auto;
color: white;
text-align: center;
}
Following vijayP's answers:
JsFiddle: http://jsfiddle.net/rockmandew/kmfeppec/
I modified his CSS class of "overrideLoading":
.overrideLoading{
background: none !important;
color: white;
position: absolute;
top: 42px;
}
As you can see I added a "position:absolute" and a "top" position - you can modify this to however you need it to appear.
Next I altered his jQuery, which I modified to actually append a new element:
$('#fancybox-loading div').append("<div class='overrideLoading'>Please Wait...</div>");
As you can see, that reduced your required jQuery to one line.
Finally, I removed the last part of the function, which was removing the class. Since this is no longer required, you can just keep the FancyBox "hideLoading" call.
For learning purposes, I removed the following from the last function:
$('#fancybox-loading div').removeClass("overrideLoading");
$('#fancybox-loading div').text("");
Again, here is the JsFiddle: http://jsfiddle.net/rockmandew/kmfeppec/
First Update:
I saw that the first user to answer, updated his answer and while works, I would suggest shying away from "!important" tags as much as possible. I too refined my answer and developed a solution that didn't use any !important tags.
What was originally: $('#fancybox-loading div').append("Please Wait..."); was now changed to:
$('#target ~ #overlay').append("<div class='overrideLoading'>Please Wait...</div>");
I noticed an earlier comment from you, which specified that you wanted to target specific loading overlays - what this function does is it: Selects every '#overlay' element that is preceded by a '#target' element - you can insert whatever target you want.
I removed all instances of the "!important" tag - this is just best/standard practice.
.overrideLoading{
color: white;
position: absolute;
top: 86px;
left: 16px;
}
Updated JsFiddle: https://jsfiddle.net/rockmandew/kmfeppec/7/

Auto height grid

I have a product grid on my e-shop (http://shop.rukahore.sk/). Every product div is 222px x 222px, but I want it to have auto height.
I tried something like this - http://patterntap.com/code/stacking-columns-layout-masonry ,
but I had to add min-height and it didn't look good because some of the images were smaller or bigger and wrapping div was still 222px, which I don't want to happen due to hover effect, etc.
Can someone provide advice regarding this?
Well, for using the display shown in that page you need to use the plugin
<!-- Requires Masonry | visit http://masonry.desandro.com/ to download -->
If you don't want to add more plugins... Well, what makes you lose the height is the float css property. You should use other thing to make the grid, see for example how they do it in www.camarasdecolores.com.
To add the Masonry plugin:
Add an id to your container:
<div id="masonryContainer" class="hp-products allposts" style="position: relative; height: 2008px;">
Add the init js code in a script
$(window).load(function(){
$('#masonryContainer').masonry({
itemSelector: '.hp-product',
columnWidth: 60
});
});
change some css:
#masonryContainer { width: 0 auto; }
.hp-product {
width: 180px; float: left;
}
.hp-product-img {
}
.hp-product-img img {
max-width: 100%;
height: auto;
}
//Void the following ones
.hp-products {
}
.allposts {
}
.allposts .hp-product {
}

Loss of CSS class after style update

I am applying a class (rShift) to a DIV that acts as a menu tab. The class gives it a :hover behaviour. On clicking the DIV, I bring in a menu on to the screen. On collapsing the menu, I loose the class and the :hover behaviour too.
I am using jQuery UI and have even tried .addClass('') to apply the lost class, but it did not work.
See it at: http://pastebin.com/hdb8Y2Ke | http://bharath.lohray.com/ftree/
When the page is initially loaded, you can see a tab at the top left corner of the page, just under the search box. On hovering the mouse it jumps out a few pixels. On clicking, the menu appears. On clicking the tab again, the menu collapses and the jump out effect is lost :-(.
What am I doing wrong?
The class is being added, but on click, you are applying an inline style to the leftmenutab directly via jquery. This style (left) overrides any styles you have in your style sheets.
I would remove the inline styles you are applying via jquery and add the styles you want to your css.
Create styles like this:
.leftMenuTab[data-state="expanded"] { left: 100px; }
.leftMenuTab[data-state="collapsed"] { left: 0; }
and remove these lines from your javascript:
$(".leftMenuTab").css("left", "+=100px");
$(".leftMenuTab").css("left", "-=100px");
Alternatively, add and remove classes from your leftMenuTab and leftMenu on click and style them through CSS. Something like this:
HTML:
<div class="leftMenu">Hello Menu</div>
<div class="leftMenuTab" data-state="collapsed">
<span class="charIcon"></span>
</div>
JS:
$('.leftMenuTab').click(function(e) {
$('.leftMenuTab,.leftMenu').toggleClass('expanded');
}
CSS:
.leftMenuTab .charIcon:after{
content:'>>';
}
.leftMenuTab.expanded .charIcon:after{
content:'<<';
}
.leftMenuTab {
background-color: #FFFFFF;
border-color: #000000;
cursor: pointer;
float: left;
padding-right: 5px;
position: absolute;
text-align: right;
top: 45px;
width: 30px;
z-index: 2;
left: -10px;
}
.leftMenuTab:hover {left:0;}
.leftMenuTab.expanded { left:100px;}
After you modify the style of the element, the style left: 0px; is left in the DIV, this neglated the effect of the hover.
This is the relevant code:
$('.leftMenuTab').click(function(e) {
temp = $(this);
if ($('.leftMenuTab').attr('data-state') == "collapsed") {
$(".charIcon", this).html("«");
$('.leftMenuTab').attr('data-state', 'expanded');
$(".leftMenu").css("left", "+=110px");
$(".leftMenuTab").css("left", "+=100px");
} else {
$(".charIcon", this).html("»");
$('.leftMenuTab').attr('data-state', 'collapsed');
$(".leftMenu").css("left", "-=110px");
$(".leftMenuTab").css("left", "-=100px");
$(this).addClass("rShift");
}
});
The quickest fix is to erase the left style instead of modyfing it (and you don't need to add the class again):
$('.leftMenuTab').click(function(e) {
temp = $(this);
if ($('.leftMenuTab').attr('data-state') == "collapsed") {
$(".charIcon", this).html("«");
$('.leftMenuTab').attr('data-state', 'expanded');
$(".leftMenu").css("left", "+=110px");
$(".leftMenuTab").css("left", "+=100px");
} else {
$(".charIcon", this).html("»");
$('.leftMenuTab').attr('data-state', 'collapsed');
$(".leftMenu").css("left", ""); //set to empty string
$(".leftMenuTab").css("left", ""); //set to empty string
//$(this).addClass("rShift"); //Not needed
}
});
Note: this was tested with a local copy from your web.

How do I include an element's margin in the hot-spot for jQuery's hover() event?

jQuery(".my_container").hover(function(){
//do code
}, function(){
//do code
});
.my_container { width: 100px; height: 100px; margin: 50px; }
The code above doesn't react to mouse over of margin (margin isn't a part of element?) - how can I change that?
You could use a 50px transparent border instead - the margin isn't really supposed to be mouseable...
Include a pseudo element, e.g.
.my_container:before {
content:'';
position:absolute;
top:-50px;
bottom:-50px;
left:-50px;
right:-50px;
}
This adds an extra 50px to the existing element's clickable area.
If you only want to add this on touch screen devices, you could do this:
.touchevents .my_container:before {
...
}
This requires something like Modernizer to insert the appropriate feature-based CSS class.
Update
As per #Jaladh's comments, you may also need to apply position:relative to the container element, since position:absolute above will be relative to the first ancestor with a position attribute:
.my_container {
position:relative;
}
Perhaps use a 2nd wrapper element with padding on the outer element and existing background and padding styles on the inner element:
<div class="my_container">
<div class="my_container_inner">
<!-- etc. -->
</div>
</div>
jQuery(".my_container").hover(function(){
//do code
}, function(){
//do code
});
.my_container { padding: 50px; }
.my_container_inner { width: 100px; height: 100px; /* etc. */ }
Building upon #Dunc's solution, you can alternatively use pseudo element to mimic your container and let actual container behave like margins. This will look like:
.my_container {
width: calc(100px + (2 * 50px));
height: calc(100px + (2* 50px));
position: relative;
}
.my_container::before {
content: '';
position: absolute;
top: 50px;
bottom: 50px;
left: 50px;
right: 50px;
}
Also make sure to move all other properties (like background color, border, etc.) you had in my_container to my_container::before because before is acting like our container here.
This is essentially helpful if your containers are grid items and you want gaps in-between them to be hoverable, because otherwise using psuedo element to add margins won't work appropriately in that case.
Change the margin to padding and it'll be hoverable.

Categories