Close all Angular JS Bootstrap popovers with click anywhere on screen? - javascript

I am using the Angular directives for bootstrap.
I have a popover as in their example:
<button popover="Hello, World!" popover-title="Title" class="btn btn-default ng-scope">Dynamic Popover</button>
It closes when you click on the button again. I'd like to close it -- and any other open popovers -- when the user clicks anywhere.
I don't see a built-in way to do this.

angular.element(document.body).bind('click', function (e) {
var popups = document.querySelectorAll('.popover');
if(popups) {
for(var i=0; i<popups.length; i++) {
var popup = popups[i];
var popupElement = angular.element(popup);
if(popupElement[0].previousSibling!=e.target){
popupElement.scope().$parent.isOpen=false;
popupElement.remove();
}
}
}
});

This feature request is being tracked (https://github.com/angular-ui/bootstrap/issues/618). Similar to aet's answer, you can do what is recommended in the feature request as a work-around:
$('body').on('click', function (e) {
$('*[popover]').each(function () {
//Only do this for all popovers other than the current one that cause this event
if (!($(this).is(e.target) || $(this).has(e.target).length > 0)
&& $(this).siblings('.popover').length !== 0
&& $(this).siblings('.popover').has(e.target).length === 0)
{
//Remove the popover element from the DOM
$(this).siblings('.popover').remove();
//Set the state of the popover in the scope to reflect this
angular.element(this).scope().tt_isOpen = false;
}
});
});
(source: vchatterji's comment in feature request mentioned above)
The feature request also has a non-jQuery solution as well as this plnkr: http://plnkr.co/edit/fhsy4V

angular.element(document.body).bind('click', function (e) {
var popups = document.querySelectorAll('.popover');
if (popups) {
for (var i = 0; i < popups.length; i++) {
var popup = popups[i];
var popupElement = angular.element(popup);
console.log(2);
if (popupElement[0].previousSibling != e.target) {
popupElement.scope().$parent.isOpen = false;
popupElement.scope().$parent.$apply();
}
}
}
});

What you say it's a default settings of the popover, but you can control it with the triggers function, by putting blur in the second argument of the trigger like this popover-trigger="{mouseenter:blur}"

One idea is you can change the trigger to use mouse enter and exit, which would ensure that only one popover shows at once. The following is an example of that:
<button popover="I appeared on mouse enter!"
popover-trigger="mouseenter" class="btn btn-default"
popover-placement="bottom" >Hello World</button>
You can see this working in this plunker. You can find the entire list of tooltip triggers on the angular bootstrap site (tooltips and popovers have the same trigger options). Best of luck!

Had the same requirement, and this is how we did it:
First, we modified bootstrap, in the link function of the tooltip:
if (prefix === "popover") {
element.addClass('popover-link');
}
Then, we run a click handler on the body like so:
$('body').on('click', function(e) {
var clickedOutside = true;
// popover-link comes from our modified ui-bootstrap-tpls
$('.popover-link').each(function() {
if ($(this).is(e.target) || $(this).has(e.target).length) {
clickedOutside = false;
return false;
}
});
if ($('.popover').has(e.target).length) {
clickedOutside = false;
}
if (clickedOutside) {
$('.popover').prev().click();
}
});

I am using below code for same
angular.element(document.body).popover({
selector: '[rel=popover]',
trigger: "click"
}).on("show.bs.popover", function(e){
angular.element("[rel=popover]").not(e.target).popover("destroy");
angular.element(".popover").remove();
});

Thank you Lauren Campregher, this is worked.
Your code is the only one that also runs the state change on the scope.
Only configured so that if you click on the popover, the latter closes.
I've mixed your code, and now also it works if you click inside the popover.
Whether the system, whether done through popover-template,
To make it recognizable pop up done with popover-template, I used classes popover- body and popover-title, corresponding to the header and the body of the popover made with the template, and making sure it is pointing directly at them place in the code:
angular.element(document.body).bind('click', function (e) {
var popups = document.querySelectorAll('.popover');
if(popups) {
for(var i=0; i<popups.length; i++) {
var popup = popups[i];
var popupElement = angular.element(popup);
var content;
var arrow;
if(popupElement.next()) {
//The following is the content child in the popovers first sibling
// For the classic popover with Angularjs Ui Bootstrap
content = popupElement[0].querySelector('.popover-content');
// For the templating popover (popover-template attrib) with Angularjs Ui Bootstrap
bodytempl = popupElement[0].querySelector('.popover-body');
headertempl= popupElement[0].querySelector('.popover-title');
//The following is the arrow child in the popovers first sibling
// For both cases.
arrow = popupElement[0].querySelector('.arrow');
}
if(popupElement[0].previousSibling!=e.target && e.target != content && e.target != arrow && e.target != bodytempl && e.target != headertempl){
popupElement.scope().$parent.isOpen=false;
popupElement.remove();
}
}
}
});
Have ever a good day, thank you Lauren, thank you AngularJS, Thank You So Much Stack Family!
Updated:
I updated all adding extra control.
The elements within the popover were excluded from the control (for example, a picture inserted into the body of the popover.). Then clicking on the same closed.
I used to solve the command of API Node.contains, integrated in a function that returns true or false.
Now with any element placed inside, run the control, and keeps the popover open if you click inside :
// function for checkparent with Node.contains
function check(parentNode, childNode) { if('contains' in parentNode) { return parentNode.contains(childNode); } else { return parentNode.compareDocumentPosition(childNode) % 16; }}
angular.element(document.body).bind('click', function (e) {
var popups = document.querySelectorAll('.popover');
if(popups) {
for(var i=0; i<popups.length; i++) {
var popup = popups[i];
var popupElement = angular.element(popup);
var content;
var arrow;
if(popupElement.next()) {
//The following is the content child in the popovers first sibling
// For the classic popover with Angularjs Ui Bootstrap
content = popupElement[0].querySelector('.popover-content');
// For the templating popover (popover-template attrib) with Angularjs Ui Bootstrap
bodytempl = popupElement[0].querySelector('.popover-body');
headertempl= popupElement[0].querySelector('.popover-title');
//The following is the arrow child in the popovers first sibling
// For both cases.
arrow = popupElement[0].querySelector('.arrow');
}
var checkel= check(content,e.target);
if(popupElement[0].previousSibling!=e.target && e.target != content && e.target != arrow && e.target != bodytempl && e.target != headertempl&& checkel == false){
popupElement.scope().$parent.isOpen=false;
popupElement.remove();
}
}
}
});

Related

Attach the event on click to his father with pure javascript

Newby to pure JS.
I'm creating a menu that has to work with mobile.
I'm trying to create with pure .js, instead of using jQuery so, that's an experiment and it has been challenging.
Here's my code:
JS:
(function() {
var menu = document.querySelector('.mobile-menu');
var subMenu = {
downToggle: document.getElementsByClassName('sub-menu'),
downToggleTitle: document.getElementsByClassName('sub-menu-title'),
subMenuItems: document.getElementsByClassName('sub-menu-item-mobile'),
searchBar: document.getElementById('mobile-search'),
onclickimg: document.querySelectorAll('.sub-menu-arrow'),
};
function listen() {
for(var i=0; i<subMenu.downToggleTitle.length; i++) {
subMenu.downToggleTitle.item(i).addEventListener('click', function(e) {
// if there is a menu that's already open and it's not the element that's been clicked, close it before opening the selected menu
for(var i=0; i<subMenu.downToggleTitle.length; i++) {
if (subMenu.downToggleTitle.item(i).classList.contains('expanded') && subMenu.downToggleTitle.item(i) !== e.target) {
subMenu.downToggleTitle.item(i).classList.toggle('expanded');
}
}
// inside each sub-menu is a third-level-sub-menu. So inside each sub-menu we
// check if it's already open, then close it
for(var i=0; i<subMenu.subMenuItems.length; i++) {
// console.log("test test")
if(subMenu.subMenuItems.item(i).classList.contains('expanded') && subMenu.subMenuItems.item(i) !== e.target) {
subMenu.subMenuItems.item(i).classList.toggle('expanded');
}
}
this.classList.toggle('expanded');
});
}
for(var i=0; i<subMenu.subMenuItems.length; i++) {
subMenu.subMenuItems.item(i).addEventListener('click', function(e) {
for(var i=0; i<subMenu.subMenuItems.length; i++) {
if(subMenu.subMenuItems[i].classList.contains('expanded') && subMenu.subMenuItems[i] !== e.target) {
subMenu.subMenuItems[i].classList.toggle('expanded');
console.log("hello Aug 20");
}
}
this.classList.toggle('expanded');
});
}
} listen();
}());
The behavior that I want to change is the following:
In the first version, if the client press the .sub-menu-title class (the variable downToggleTitle), which is a li item, the very element will toggle the class expanded. Now I want something a little bit different.
I added the class sub-menu-arrow, which is the variable onclickimg to an img at the very end of my list element, so if the client will click on the arrow, all the class element sub-menu-title ( var = downToggleTitle ) will toggle the class expanded.
This is not happening because for some reason if I change the code in this way:
subMenu.onclickimg.item(i).addEventListener('click', function(e) {
for(var i=0; i<subMenu.downToggleTitle.length; i++) {
if (subMenu.downToggleTitle.item(i).classList.contains('expanded') && subMenu.downToggleTitle.item(i) !== e.target) {
subMenu.downToggleTitle.item(i).classList.toggle('expanded');
}
}
the class expanded will be toggled to the sub-menu-arrow elements (like I said, some images with animations).
Any suggestion on how to target the parent element in this case?
Also, is it possible to exclude the anchor element with the class mobile-toplevel-link from the click event?
The <a> element is the other children of the sub-menu-title class
This is really just a comment. You can greatly simplify your code using the iterator functionality of modern NodeLists. I don't see the point of the subMenu object, it just makes references longer.
Also, I've replaced getElementsByClassName as it produces a live NodeList, whereas querySelectorAll returns a static list. Not much difference here, but can be significant in other cases.
The following is a simple refactoring, it should work exactly as your current code is supposed to. Note that for arrow functions, this is adopted from the enclosing execution context.
(function() {
let menu = document.querySelector('.mobile-menu');
let downToggle = document.querySelectorAll('.sub-menu'),
downToggleTitle = document.querySelectorAll('.sub-menu-title'),
subMenuItems = document.querySelectorAll('.sub-menu-item-mobile'),
searchBar = document.getElementById('mobile-search'),
onclickimg = document.querySelectorAll('.sub-menu-arrow');
function listen() {
downToggleTitle.forEach(dtTitle => {
dtTitle.addEventListener('click', function(e) {
// If there is a menu that's already open
// and it's not the element that's been clicked,
// close it before opening the selected menu
downToggleTitle.forEach(node => {
if (node.classList.contains('expanded') && node !== this) {
node.classList.toggle('expanded');
}
});
// inside each sub-menu is a third-level-sub-menu. So inside each sub-menu
// If it's already open, close it
subMenuItems.forEach(item => {
// console.log("test test")
if (item.classList.contains('expanded') && item !== this) {
item.classList.toggle('expanded');
}
});
this.classList.toggle('expanded');
});
});
subMenuItems.forEach(item => {
item.addEventListener('click', function(e) {
subMenuItems.forEach(item => {
if (items.classList.contains('expanded') && item !== this) {
item.classList.toggle('expanded');
console.log("hello Aug 20");
}
});
this.classList.toggle('expanded');
});
});
}
listen();
}());
If you want to get the parent element of the clicked target, then you can exploit your current eventListener and use the e.target.parentNode to get it. This will return you an element, which you can add/remove CSS classes from and do pretty much everything you like. Keep in mind, you can use the .parentNode multiple times and for example, if you wanted to get the "grandparent" of an element (2 levels up) you could write e.target.parentNode.parentNode and so on.

Close a bootstrap warning when clicking anywhere on page

Bootstrap Warnings Image I have two different types of bootstraps alerts (warning and danger). Danger alerts are always suppose to be on the page no matter what. Warning alerts happen when user clicks on the dropdown list carriers it displays a bootstrap warning notification. User has to click on 'x' for it to close. I need it to work when user click anywhere on the page or by clicking on the 'x'.
HomeController.cs
case "Carrier":
var carrierid = (from foo in db.Carriers
where foo.ID == warningid
select foo.WarningID).Single();
if (carrierid != null)
{
warning = (from warnings in db.Warnings
where warnings.IsActive == true && warnings.Id == carrierid
select warnings.WarningBody).SingleOrDefault();
if (warning != null)
{
warning = ("<div class=\"alert alert-warning alert-dismissible\" id=\"myWarning\" role=\"alert\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-label=\"Close\"><span aria-hidden=\"true\">×</span></button><strong>" +
warning + "</strong></div>");
}
else
{
warning = "";
}
}
else
{
warning = "";
}
return Json(warning, JsonRequestBehavior.AllowGet);
default:
break;
warningwriter.js
//// warning display script takes a value of warningid and warningcaller
$(document).ready(function () {
var warningid = 0;
var warningcaller = "Universal";
loadWarnings(warningid, warningcaller);
});
$('#Phones').change(function () {
var warningid = $(this).val();
var warningcaller = "Phone";
loadWarnings(warningid, warningcaller);})
$('#Carriers').change(function () {
var warningid = $(this).val();
var warningcaller = "Carrier";
loadWarnings(warningid, warningcaller);})
function loadWarnings(warningid, warningcaller) {
$.getJSON("../Home/LoadWarnings", { warningID: warningid, warningCaller: warningcaller },
function (warning) {
var select = $('#warnings');
select.append(warning);
});
};
As Martin suggested, it's something you need to do in javascript. I haven't tested this, but it would be something like:
$(document).click(function (event) {
$(".alert").hide();
});
This is basically, clicking anywhere on the page will hide any displayed alert.
Since you have two different types of bootstraps alerts (danger and warning). You have to use ".alert-warning" because that is the one you want to get rid of when user did a mouse click anywhere on page. ".alert" is all of the bootstraps alerts, however, if you need to get rid of a certain type you can call the contextual classes(e.g., .alert-success, .alert-info, .alert-warning, and/or .alert-danger. https://v4-alpha.getbootstrap.com/components/alerts/
$(document).click(function (event) {
$(".alert-warning").hide();
});
$(document).ready(function () {
$("#myWarning").click(function () {
$(".alert").alert("close");
});
});
By doing this, u are making two things wrong:
You are binding the click event to an element, that possibly
doesnt exist when the page is loaded.
You are binding the click
event to a restricted element. This means that the alert wont be
closed when u click anywhere on the page. In this case, only clicks on #myWarning will close the alert.
Finally, you should use what #Bryan already posted :)
Edit:
Assuming that u have a set of alerts that u always want to close on page load, add to this elements a way to identify them, for example a class "close-on-screenclick"
$(document).click(function () {
$(".close-on-screenclick.alert").alert("close");
});
.This should close those elements whenever a click is made on the screen

How to detect a user clicked on an internal or external link

I need to know if users clicked on an internal or external link to alert them.
I have many internal and external links on my site.
My internal links are like this:
about
draw graph
I need to alert only when external links are clicked.
(I've included two methods here: One method uses jQuery, and the other doesn't use jQuery. Skip down to the bold heading if you don't want to use jQuery)
One way you could do this is by adding a class to each external link, and then attaching an event handler to everything in that class which raises an alert when you click the link. That's tedious, though, as you have to add the class to every external link, and it won't for user generated content.
What you can do is use jQuery, along with the CSS selector a[href^="http"], to select all the external links, and then attach an event handler that raises your alert when they're clicked:
$('a[href^="http"]').click(function() {
alert();
});
a[href^="http"] means "an a tag which has a link, and that link has to start with 'http'." So here we select all the elements which start with http - that is, every external link - and then set it so that when you click on them, an alert pops up.
Non-jQuery method
If you want to do this without jQuery, you'll want to use document.querySelectorAll('a[href^="http"]') and bind the click event of each element in the array that that function returns. That looks something like this:
var externalLinks = document.querySelectorAll('a[href^="http"]');
for (var i = externalLinks.length-1; i >= 0; i--) {
externalLinks[i].addEventListener("click", function() { alert(); }, false);
}
I had to do this from scratch on my own site so I'll just copy + paste it here for you. It came from inside one of my objects so if I left some this keywords you can remove them.
function leaving() {
var links = document.anchors || document.links || document.getElementsByTagName('a');
for (var i = 0; i < links.length; i++) {
if ((links[i].getAttribute('href').indexOf('http') === 0 && links[i].getAttribute('href').indexOf('fleeceitout') < 0) && (links[i].getAttribute('href').indexOf('/') !== 0 && links[i].getAttribute('href').indexOf('#') !== 0) && links[i].className.indexOf('colorbox') < 0) {
addEvent(links[i], 'click', this.action);
}
}
}
function action(evt) {
var e = evt || window.event,
link = (e.currentTarget) ? e.currentTarget : e.srcElement;
if (e.preventDefault) {
e.preventDefault();
} else {
window.location.href = link.href;
return;
}
var leave = confirm("You are now leaving the _______ website. If you want to stay, click cancel.");
if (leave) {
window.location.href = link.href;
return;
} else {
return leave;
}
}
var addEvent = function (element, myEvent, fnc) {
return ((element.attachEvent) ? element.attachEvent('on' + myEvent, fnc) : element.addEventListener(myEvent, fnc, false));
};
Replace instances of 'fleeceitout' with your sites domain name (microsoft.com, etc) and you're set.
The easiest ways are with jQuery, using a special class for external links, or by checking for "http://" in the URL.
Like this, if using a special class:
$("a.external").on("click", function() {
//de Do something special here, before going to the link.
//de URL is: $(this).attr("href")
});
And then in HTML:
<a href="http://external.link" class='external'>external link</a>
Or, you can check for http:// in the URL! Then you don't need a special class.
$('a[href=^"http://"]').on("click", function() {
//de Do something special here, before going to the link.
//de URL is: $(this).attr("href")
});
Cite: My original method of testing for "http://" was a bit slower, actually doing an indexOf test on .attr("href") so I used #Matthew's selector choice instead. Forgot about the caret route! Props to #Matthew on that, and for the non-jQuery alternative.
$(document).ready(function() {
$('a').click(function() {
var returnType= true;
var link = $(this).attr('href');
if ( link.indexOf('http') >= 0 ) {
returnType=confirm('You are browsing to an external link.');
}
return returnType;
});
});`

Adding links to jQuery tabs

I'm working on a project for my JavaScript class, and I don't know how to edit this jQuery where when you select a tab, it will bring you to a new page. I try adding "a href" to the body, but it doesn't look right. Is there a piece of code I have to enter in the jQuery so when you choose "About" that it will bring you to the actual page? Here's the code:
jQuery
function handleEvent(e) {
var el = $(e.target);
if (e.type == "mouseover" || e.type == "mouseout") {
if (el.hasClass("tabStrip-tab") && !el.hasClass("tabStrip-tab-click")) {
el.toggleClass("tabStrip-tab-hover");
}
}
if (e.type == "click") {
if (el.hasClass("tabStrip-tab-hover")) {
var id = e.target.id;
var num = id.substr(id.lastIndexOf("-") + 1);
if (currentNum != num) {
deactivateTab();
el.toggleClass("tabStrip-tab-hover")
.toggleClass("tabStrip-tab-click");
showDescription(num);
currentNum = num;
}
}
}
}
function deactivateTab() {
var descEl = $("#tabStrip-desc-" + currentNum);
if (descEl.length > 0) {
descEl.remove();
$("#tabStrip-tab-" + currentNum).toggleClass("tabStrip-tab-click");
}
}
$(document).bind("click mouseover mouseout", handleEvent);
HTML
<div class="tabStrip">
<div id="tabStrip-tab-1" class="tabStrip-tab">Home</div>
<div id="tabStrip-tab-2" class="tabStrip-tab">About</div>
<div id="tabStrip-tab-3" class="tabStrip-tab">Contact</div>
<div id="tabStrip-tab-3" class="tabStrip-tab">Gallery</div>
</div>
add this to your handler if you need a new page..
window.open('url', 'window name', 'window settings');
or this if you want to redirect the actual view
window.location.href('url');
furthermore this should be a better choice:
$('div[id^=tabStrip-tab]').bind("click mouseover mouseout", handleEvent);
now only the 'tabStrip-*' id´s will trigger the events/handler
The best solution for your problem is to put hidden div with content for every tab you have.
All you have to do is display the current div depending which tag is selected. The another solution is using ajax and then you have a template for the content and you fill the template with the data you have received.

Javascript Detect click event outside of div [duplicate]

This question already has answers here:
How do I detect a click outside an element?
(91 answers)
Closed 1 year ago.
The community reviewed whether to reopen this question last year and left it closed:
Original close reason(s) were not resolved
I have a div with id="content-area", when a user clicks outside of this div, I would like to alert them to the fact that they clicked outside of it. How would I use JavaScript to solve this issue?
<div id = "outer-container">
<div id = "content-area">
Display Conents
</div>
</div>
In pure Javascript
Check out this fiddle and see if that's what you're after!
document.getElementById('outer-container').onclick = function(e) {
if(e.target != document.getElementById('content-area')) {
document.getElementById('content-area').innerHTML = 'You clicked outside.';
} else {
document.getElementById('content-area').innerHTML = 'Display Contents';
}
}
http://jsfiddle.net/DUhP6/2/
The Node.contains() method returns a Boolean value indicating whether a node is a descendant of a given node or not
You can catch events using
document.addEventListener("click", clickOutside, false);
function clickOutside(e) {
const inside = document.getElementById('content-area').contains(e.target);
}
Remember to remove the event listened in the right place
document.removeEventListener("click", clickOutside, false)
Bind the onClick-Event to an element that is outside your content area, e.g. the body. Then, inside the event, check whether the target is the content area or a direct or indirect child of the content area. If not, then alert.
I made a function that checks whether it's a child or not. It returns true if the parent of a node is the searched parent. If not, then it checks whether it actually has a parent. If not, then it returns false. If it has a parent, but it's not the searched one, that it checks whether the parent's parent is the searched parent.
function isChildOf(child, parent) {
if (child.parentNode === parent) {
return true;
} else if (child.parentNode === null) {
return false;
} else {
return isChildOf(child.parentNode, parent);
}
}
Also check out the Live Example (content-area = gray)!
I made a simple and small js library to do this for you:
It hijacks the native addEventListener, to create a outclick event and also has a setter on the prototype for .onoutclick
Basic Usage
Using outclick you can register event listeners on DOM elements to detect whether another element that was that element or another element inside it was clicked. The most common use of this is in menus.
var menu = document.getElementById('menu')
menu.onoutclick = function () {
hide(menu)
}
this can also be done using the addEventListener method
var menu = document.getElementById('menu')
menu.addEventListener('outclick', function (e) {
hide(menu)
})
Alternatively, you can also use the html attribute outclick to trigger an event. This does not handle dynamic HTML, and we have no plans to add that, yet
<div outclick="someFunc()"></div>
Have fun!
Use document.activeElement to see which of your html elements is active.
Here is a reference:
document.activeElement in MDN
$('#outer-container').on('click', function (e) {
if (e.target === this) {
alert('clicked outside');
}
});
This is for the case that you click inside the outer-container but outside of the content-area.
Here is the fiddle : http://jsfiddle.net/uQAMm/1/
$('#outercontainer:not(#contentarea)').on('click', function(event){df(event)} );
function df(evenement)
{
var xstart = $('#contentarea').offset().left;
var xend = $('#contentarea').offset().left + $('#contentarea').width();
var ystart = $('#contentarea').offset().top;
var yend = $('#contentarea').offset().top + $('#contentarea').height();
var xx = evenement.clientX;
var yy = evenement.clientY;
if ( !( ( xx >= xstart && xx <= xend ) && ( yy >= ystart && yy <= yend )) )
{
alert('out');
}
}
use jquery as its best for DOM access
$(document).click(function(e){
if($(e.target).is("#content-area") || $(e.target).closest("#content-area").length)
alert("inside content area");
else alert("you clicked out side content area");
});
Put this into your document:
<script>
document.onclick = function(e) {
if(e.target.id != 'content-area') alert('you clicked outside of content area');
}
</script>
Here is a simple eventListener that checks all parent elements if any contain the id of the element. Otherwise, the click was outside the element
html
<div id="element-id"></div>
js
const handleMouseDown = (ev) => {
let clickOutside = true
let el = ev.target
while (el.parentElement) {
if (el.id === "element-id") clickOutside = false
el = el.parentElement
}
if (clickOutside) {
// do whatever you wanna do if clicking outside
}
}
document.addEventListener("mousedown", handleMouseDown)

Categories

Resources