I have implemented a custom dropdown which has a close function using bubbling, and I used to have only one dropdown on the page. But now I have two dropdowns and if I open the first dropdown and click on the second dropdown my first dropdown doesn't close. How I can fix it?
My close function using native JS:
add_event(document, 'click', function(event) {
event = event || window.event;
if (!event.keyCode) {
var elem = event.target;
while (elem !== document) {
if (elem && elem.className === 'dropdown') {
return;
}
elem = elem.parentNode;
}
close();
}
}, false);
Link to my dropdown: http://jsfiddle.net/Lv7ou8mp/
First of all, add an event listener to your document for each dropdown. Each dropdown shall close itself when this event is received.
document.addEventListener('closeDropdown', close);
Now create a function to fire this event:
function dispatchCloseEvent() {
var evt = document.createEvent('Event');
evt.initEvent('closeDropdown', true, false);
document.dispatchEvent(evt);
}
Finally, before a dropdown opens, we're going to call this function to make sure other dropdowns in the document will get closed.
The open function now looks like this:
function open() {
dispatchCloseEvent();
dropdown_content.classList.remove('hidden');
dropdown_content.scrollTop = 0;
is_open = true;
}
And this is the updated fiddle: http://jsfiddle.net/Lv7ou8mp/1/
Related
I need to prevent hiding of a modal dialog when user clicks on the dialog, then moves the mouse outside of the dialog, and releases it. The dialog is placed inside outer div on which the click event is registered. Here is the example of the modal dialog and its setup.
So I've done the following:
var pointerDownElement = null;
$('.d1').on('mousedown', function(event) {
// this is how I do it to prevent triggering of click event
pointerDownElement = event.target;
// this is how a browser does it
pointerDownElement = event.currentTarget;
});
$('.d1').on('mouseup', function(event) {
var element = event.target;
if (element === pointerDownElement) {
console.log('triggering click');
}
});
Is this approach correct?
You're definitely on the right track. Slightly modified code:
var pointerDownElement = null;
$('.d1').on('mousedown', function(event) {
event.preventDefault();
pointerDownElement = event.currentTarget;
return false;
});
$('.d1').on('mouseup', function(event) {
if (event.target=== pointerDownElement) {
console.log('triggering click');
}
});
No need to assign a value to a variable if you're only going to use it once. Also, event.preventDefault(); and return false; will guarantee the default behavior of the event doesn't take place (not that there typically is one for mousedown, but I'm assuming you have a reason for including this code).
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();
}
}
}
});
I'm trying to make a gallery of pictures appear when a link is clicked, and disappear when anywhere in the browser window is clicked following that. I can change the function associated with clicking on the "galleryshow" element so that if I click it the gallery is shown and if I click it again the gallery disappears; but if I try to make it so that if the window (or document) is clicked the gallery closes, nothing happens.
This is my code:
function gallerymake() {
document.onclick = function () {gallerytake();};
// document.getElementById("hoverage").onclick = function() {gallerytake();};
document.getElementById("galleryhold").style.visibility="visible";
}
function gallerytake(){
document.getElementById("hoverage").onclick = function () {gallerymake();};
document.getElementById("galleryhold").style.visibility="hidden";
}
Thanks
freejosh's answer works. However, calling e.stopPropagation() may have undesired side effects if there are other handlers using event delegation, since those handlers may not get called.
One of the basics of event handling is that they should not affect or depend on other handlers as much as possible, say if you had two buttons needing to show two different divs. By calling e.stopPropagation(), clicking on one of the popups would not hide the other popup. See document.click keep toggling the menu for an example of where it didn't work since it collided with lightbox event handlers. Therefore, a solution that doesn't affect any other code is to install a document click handler that only does its work if the click didn't come from the button or within your popup.
http://jsfiddle.net/b4PXG/2/
HTML
Here is my web page <button id="show-btn"> show popup</button>
<div id="modal" > I will show over everything Google</div>
JS
var modal = document.getElementById('modal');
var btn = document.getElementById('show-btn');
btn.onclick = function() {
modal.style.display = "block";
};
document.onclick = function (e) {
e = e || window.event;
var target = e.target || e.srcElement;
if (target !== btn && (!target.contains(modal) || target !== modal)) {
modal.style.display = 'none';
}
}
You can abstract this pattern into a function that creates the doc click handlers
/**
* Creates a handler that only gets called if the click is not within any
* of the given nodes
* #param {Function} handler The function to call (with the event object as
* as its parameter)
* #param {HTMLElement} exclude... If the click happens within any of these
* nodes, the handler won't be called
* #return {function} A function that is suitable to be
* bound to the document click handler
*/
function createDocClickHandler(handler /* [,exclude, exclude, ...] */) {
var outerArgs = arguments;
return function (e) {
e = e || window.event;
var target = e.target || e.srcElement;
// Only call the original handler if the click was outside all the excluded nodes
var isWithinExcluded = false;
for (var i=1; i < outerArgs.length; i++) {
var excluded = outerArgs[i];
if (target === excluded || excluded.contains(target)) {
isWithinExcluded = true;
break;
}
}
if (!isWithinExcluded) {
handler.call(this, e);
}
}
}
var modal = document.getElementById('modal');
var btn = document.getElementById('show-btn');
btn.onclick = function() {
modal.style.display = "block";
};
// Assign the handler that will hide the popup if the clicked
// happened outside of modal and btn
document.onclick = createDocClickHandler(function (e) {
modal.style.display = 'none';
}, modal, btn);
Your click event bubbles up to the document every time you click the hoverage element, so gallerymake() and gallerytake() are being called. See this page for an explanation of events.
To prevent this use e.stopPropagation(). See this fiddle for a working example.
I want to detect in my firefox extension if a link has been clicked. So far, for this I add a click event listener to the window
window.addEventListener("click", function(event) { handleWindowClick(event); }, false);
...
handleWindowClick : function(event) {
if ("event.target is a link") {
// do something
}
};
For some links the event.target is simple the URL. However, for some links I get, e.g., a HTMLSpanElement as event.target. Am I on the right track to catch link clicks or are there other ways? If it works this way, how can I ensure the successfully test if the event.targer is a link?
You're adding an event listener to the main window which registers any click. The url's you're having problems with must be wrapped in a <span> tag. What you need is event delegation
Why don't you just put the click event listener to anchors (<a>)?
var hrefs = document.getElementsByTagName('a');
for (i = 0; i < hrefs.length; i++) {
hrefs[i].addEventListener(...)
...
}
or in jQuery:
$('a').click(function () {
...
});
check this out, i hope this is what you are looking for.
window.addEventListener("click", function(event) {
handleWindowClick(event);
}, false);
function handleWindowClick(event){
var origEl = event.target || event.srcElement;
if(origEl.tagName === 'A')
alert("anchor link is clicked");
else if(origEl.parentNode.tagName === 'A')
alert("clicked inside anchor");
else if(origEl.tagName === 'SPAN')
alert("span is clicked");
}
fiddle : http://jsfiddle.net/5zXkN/3/
I extended the answer of dku.rajkumar to support arbitrary constructs within "A"-tags. I'm simply go up the tree until I either find an "A" or I'm at the root (so no link clicked in this case). It seems to do the trick. Thanks to all for your help!
window.addEventListener("click", function(event) { handleWindowClick(event); }, false);
...
isLink : function(element) {
if(element.tagName === 'A')
return true;
else
if (element.parentNode)
return this.isLink(element.parentNode)
else
return false;
},
handleWindowClick : function(event) {
var element = event.target || event.srcElement;
var isLink = this.isLink(element);
if (isLink)
dump("A link has been clicked.\n");
},
$(document).click(function(evt) {
var target = evt.currentTarget;
var inside = $(".menuWraper");
if (target != inside) {
alert("bleep");
}
});
I am trying to figure out how to make it so that if a user clicks outside of a certain div (menuWraper), it triggers an event.. I realized I can just make every click fire an event, then check if the clicked currentTarget is same as the object selected from $(".menuWraper"). However, this doesn't work, currentTarget is HTML object(?) and $(".menuWraper") is Object object? I am very confused.
Just have your menuWraper element call event.stopPropagation() so that its click event doesn't bubble up to the document.
Try it out: http://jsfiddle.net/Py7Mu/
$(document).click(function() {
alert('clicked outside');
});
$(".menuWraper").click(function(event) {
alert('clicked inside');
event.stopPropagation();
});
http://api.jquery.com/event.stopPropagation/
Alternatively, you could return false; instead of using event.stopPropagation();
if you have child elements like dropdown menus
$('html').click(function(e) {
//if clicked element is not your element and parents aren't your div
if (e.target.id != 'your-div-id' && $(e.target).parents('#your-div-id').length == 0) {
//do stuff
}
});
The most common application here is closing on clicking the document but not when it came from within that element, for this you want to stop the bubbling, like this:
$(".menuWrapper").click(function(e) {
e.stopPropagation(); //stops click event from reaching document
});
$(document).click(function() {
$(".menuWrapper").hide(); //click came from somewhere else
});
All were doing here is preventing the click from bubbling up (via event.stopPrpagation()) when it came from within a .menuWrapper element. If this didn't happen, the click came from somewhere else, and will by default make it's way up to document, if it gets there, we hide those .menuWrapper elements.
try these..
$(document).click(function(evt) {
var target = evt.target.className;
var inside = $(".menuWraper");
//alert($(target).html());
if ($.trim(target) != '') {
if ($("." + target) != inside) {
alert("bleep");
}
}
});
$(document).click((e) => {
if ($.contains($(".the-one-you-can-click-and-should-still-open").get(0), e.target)) {
} else {
this.onClose();
}
});
I know that the question has been answered, but I hope my solution helps other people.
stopPropagation caused problems in my case, because I needed the click event for something else. Moreover, not every element should cause the div to be closed when clicked.
My solution:
$(document).click(function(e) {
if (($(e.target).closest("#mydiv").attr("id") != "mydiv") &&
$(e.target).closest("#div-exception").attr("id") != "div-exception") {
alert("Clicked outside!");
}
});
http://jsfiddle.net/NLDu3/
I do not think document fires the click event. Try using the body element to capture the click event. Might need to check on that...
This code will open the menu in question, and will setup a click listener event. When triggered it will loop through the target id's parents until it finds the menu id. If it doesn't, it will hide the menu because the user has clicked outside the menu. I've tested it and it works.
function tog_alerts(){
if($('#Element').css('display') == 'none'){
$('#Element').show();
setTimeout(function () {
document.body.addEventListener('click', Close_Alerts, false);
}, 500);
}
}
function Close_Alerts(e){
var current = e.target;
var check = 0;
while (current.parentNode){
current = current.parentNode
if(current.id == 'Element'){
check = 1;
}
}
if(check == 0){
document.body.removeEventListener('click', Close_Alerts, false);
$('#Element').hide();
}
}
function handler(event) {
var target = $(event.target);
if (!target.is("div.menuWraper")) {
alert("outside");
}
}
$("#myPage").click(handler);
try this one
$(document).click(function(event) {
if(event.target.id === 'xxx' )
return false;
else {
// do some this here
}
});
var visibleNotification = false;
function open_notification() {
if (visibleNotification == false) {
$('.notification-panel').css('visibility', 'visible');
visibleNotification = true;
} else {
$('.notification-panel').css('visibility', 'hidden');
visibleNotification = false;
}
}
$(document).click(function (evt) {
var target = evt.target.className;
if(target!="fa fa-bell-o bell-notification")
{
var inside = $(".fa fa-bell-o bell-notification");
if ($.trim(target) != '') {
if ($("." + target) != inside) {
if (visibleNotification == true) {
$('.notification-panel').css('visibility', 'hidden');
visibleNotification = false;
}
}
}
}
});