Noob on JS -- why is document.onclick not working properly? - javascript

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.

Related

Correct way to prevent triggering `click` if inner element is not hovered when mouse is released

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).

Close function using bubbling

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/

Function activate after two onclicks

Hey I'm using javascript+html only.
Is there any way to activate a function after the button has been clicked two (or more) times? I want the button to do NOTHING at the first click.
For a "doubleclick", when the user quickly presses the mouse button twice (such as opening a program on the desktop), you can use the event listener dblclick in place of the click event.
https://developer.mozilla.org/en-US/docs/Web/Reference/Events/dblclick
For a quick example, have a look at the below code. http://jsfiddle.net/jzQa9/
This code just creates an event listener for the HTMLElement of "item", which is found by using getElementById.
<div id="item" style="width:15px;height:15px;background-color:black;"></div>
<script>
var item = document.getElementById('item');
item.addEventListener('dblclick',function(e) {
var target = e.target || e.srcElement;
target.style.backgroundColor = 'red';
},false);
</script>
As for wanting the user to click an element X times for it to finally perform an action, you can do the following. http://jsfiddle.net/5xbPG/
This below code works by adding a click tracker to the HTMLElement and incrementing the click count every time it's clicked. I opted to save the clicks to the HTMLElement instead of a variable, but either way is fine.
<div id="item" style="width:15px;height:15px;background-color:black;"></div>
<script>
var item = document.getElementById('item');
item.addEventListener('click',function(e) {
var target = e.target || e.srcElement;
var clicks = 0;
if(target.clicks)
clicks = target.clicks;
else
target.clicks = 0;
if(clicks >= 4) {
target.style.backgroundColor = 'red';
}
target.clicks += 1;
},false);
</script>
== UPDATE ==
Since you recently posted a comment that you want two different buttons to be clicked for an action to happen, you would want to do something like this... http://jsfiddle.net/9GJez/
The way this code works is by setting two variables (or more) to track if an element has been clicked. We change these variables when that item has been clicked. For each event listener at the end of changing the boolean values of the click state, we run the function checkClick which will make sure all buttons were clicked. If they were clicked, we then run our code. This code could be cleaned up and made to be more portable and expandable, but this should hopefully get you started.
<input type="button" id="button1">
<input type="button" id="button2">
<div id="result" style="width:15px;height:15px;background-color:black;"></div>
<script>
var result = document.getElementById('result');
var button1 = document.getElementById('button1');
var button2 = document.getElementById('button2');
var button1Clicked = false;
var button2Clicked = false;
button1.addEventListener('click',function(e) {
button1Clicked = true;
checkClick();
},false);
button2.addEventListener('click',function(e) {
button2Clicked = true;
checkClick();
},false);
function checkClick() {
if(button1Clicked && button2Clicked) {
result.style.backgroundColor = 'red';
}
}
</script>
Two ways you can do this, one would be to have a data attribute within the html button that identifies whether the click has been done.
<button id="btn">Click Me!</button>
<script>
var clickedAlready = false;
document.getElementById('btn').onclick = function() {
if (clickedAlready) {
//do something...
}
else
clickedAlready = true;
}
</script>
While global variables aren't the best way to handle it, this gives you an idea. Another option would be to store the value in a hidden input, and modify that value to identify if it's the first click or not.
Maybe something like this?
var numberTimesClicked = 0;
function clickHandler() {
if (numberTimesClicked > 0) {
// do something...
}
numberTimesClicked++;
}
document.getElementById("myBtn").addEventListener("click", clickHandler);

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

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();
}
}
}
});

OpenLayers steals click events with Popups

Why does FramedCloud popup steal click events inside the popup?
current_popup = new OpenLayers.Popup.FramedCloud(
"featurePopup",
f.geometry.getBounds().getCenterLonLat(),
new OpenLayers.Size(0,0),
"<b>Наблюдения</b><br/>" + $.map(features, function(fe) { return fe.attributes.description; }).join('<br/>'),
null, false, null);
map.addPopup(current_popup, true);
$('#map').on('click', function() { console.log('test'); return false; });
Captures click events always except when I click a link inside a popup. The popup and the anchors are descendants of #map.
Click the map => callback is fired
Click a marker => callback is fired, popup is shown
click inside popup (not on a link) => callback is not fired
click a link inside a popup => same way, nothing happens
The code in that part of OL is quite obscure.
Why does it catch clicks inside the popup? How do I take them back?
edit: debugging deeper in OL: this function is fired:
bindAsEventListener: function(func, object) {
return function(event) {
return func.call(object, event || window.event);
};
event.target is the anchor, exactly what I expect:
<a class="edit-card-link" href="/form/?id=806">...</a>
func is:
handleBrowserEvent: function(evt) {
var type = evt.type, listeners = this.listeners[type];
if (!listeners || listeners.length == 0) {
return;
}
var touches = evt.touches;
if (touches && touches[0]) {
var x = 0;
var y = 0;
var num = touches.length;
var touch;
for (var i = 0; i < num; ++i) {
touch = touches[i];
x += touch.clientX;
y += touch.clientY;
}
evt.clientX = x / num;
evt.clientY = y / num;
}
if (this.includeXY) {
evt.xy = this.getMousePosition(evt);
}
this.triggerEvent(type, evt);
}
this is OpenLayers.Event class instance, evt.target is still that anchor, listeners contains 1 listener:
function (evt){OpenLayers.Event.stop(evt,true);}
Is this the reason? How do I take it out?
If you want to stop the popup from stealing a mouse event then in your CSS you could, as suggested here, set the pointer-events: none; for the id corresponding to the popup id given at its creation. Thus in your case it would be:
#featurePopup{
pointer-events: none;
}
It worked like a charm for me when I wanted to avoid flickering of a popup which I showed on mouseover.
I did it another way. I let OpenLayers capture the event, but before that I trigger another one.
$('a', current_popup.contentDiv).on('click', function(evt) {
var jtarget = $(evt.target);
hide_popup(); // hides OpenLayers popup
$(document).trigger('edit_link_clicked', {
feature: features[jtarget.parent().find('a').index(jtarget)],
cluster: f,
url: jtarget.attr('href')
});
return false;
});

Categories

Resources