I'm currently using jQuery to make a div clickable and in this div I also have anchors. The problem I'm running into is that when I click on an anchor both click events are firing (for the div and the anchor). How do I prevent the div's onclick event from firing when an anchor is clicked?
Here's the broken code:
JavaScript
var url = $("#clickable a").attr("href");
$("#clickable").click(function() {
window.location = url;
return true;
})
HTML
<div id="clickable">
<!-- Other content. -->
I don't want #clickable to handle this click event.
</div>
Events bubble to the highest point in the DOM at which a click event has been attached. So in your example, even if you didn't have any other explicitly clickable elements in the div, every child element of the div would bubble their click event up the DOM to until the DIV's click event handler catches it.
There are two solutions to this is to check to see who actually originated the event. jQuery passes an eventargs object along with the event:
$("#clickable").click(function(e) {
var senderElement = e.target;
// Check if sender is the <div> element e.g.
// if($(e.target).is("div")) {
window.location = url;
return true;
});
You can also attach a click event handler to your links which tell them to stop event bubbling after their own handler executes:
$("#clickable a").click(function(e) {
// Do something
e.stopPropagation();
});
Use stopPropagation method, see an example:
$("#clickable a").click(function(e) {
e.stopPropagation();
});
As said by jQuery Docs:
stopPropagation method prevents the event from bubbling up the DOM
tree, preventing any parent handlers from being notified of the event.
Keep in mind that it does not prevent others listeners to handle this event(ex. more than one click handler for a button), if it is not the desired effect, you must use stopImmediatePropagation instead.
Here my solution for everyone out there looking for a non-jQuery code (pure javascript)
document.getElementById("clickable").addEventListener("click", function(e) {
e = window.event || e;
if(this === e.target) {
// put your code here
}
});
Your code wont be executed if clicked on parent's children
If you do not intend to interact with the inner element/s in any case, then a CSS solution might be useful for you.
Just set the inner element/s to pointer-events: none
in your case:
.clickable > a {
pointer-events: none;
}
or to target all inner elements generally:
.clickable * {
pointer-events: none;
}
This easy hack saved me a lot of time while developing with ReactJS
Browser support could be found here: http://caniuse.com/#feat=pointer-events
Inline Alternative:
<div>
<!-- Other content. -->
<a onclick='event.stopPropagation();' href="http://foo.example">I don't want #clickable to handle this click event.</a>
</div>
You can also try this
$("#clickable").click(function(event) {
var senderElementName = event.target.tagName.toLowerCase();
if(senderElementName === 'div') {
// Do something here
} else {
// Do something with <a> tag
}
});
Writing if anyone needs (worked for me):
event.stopImmediatePropagation()
From this solution.
Using return false; or e.stopPropogation(); will not allow further code to execute. It will stop flow at this point itself.
If you have multiple elements in the clickable div, you should do this:
$('#clickable *').click(function(e){ e.stopPropagation(); });
I compare to ev.currentTarget when this is not available (React, etc).
$("#clickable").click(function(e) {
if (e.target === e.currentTarget) {
window.location = url;
return true;
}
})
Here's an example using Angular 2+
For example, if you wanted to close a Modal Component if the user clicks outside of it:
// Close the modal if the document is clicked.
#HostListener('document:click', ['$event'])
public onDocumentClick(event: MouseEvent): void {
this.closeModal();
}
// Don't close the modal if the modal itself is clicked.
#HostListener('click', ['$event'])
public onClick(event: MouseEvent): void {
event.stopPropagation();
}
If it is in inline context, in HTML try this:
onclick="functionCall();event.stopPropagation();
e.stopPropagation() is a correct solution, but in case you don't want to attach any event handler to your inner anchor, you can simply attach this handler to your outer div:
e => { e.target === e.currentTarget && window.location = URL; }
var inner = document.querySelector("#inner");
var outer = document.querySelector("#outer");
inner.addEventListener('click',innerFunction);
outer.addEventListener('click',outerFunction);
function innerFunction(event){
event.stopPropagation();
console.log("Inner Functiuon");
}
function outerFunction(event){
console.log("Outer Functiuon");
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Pramod Kharade-Event with Outer and Inner Progration</title>
</head>
<body>
<div id="outer" style="width:100px;height:100px;background-color:green;">
<div id="inner" style="width:35px;height:35px;background-color:yellow;"></div>
</div>
</body>
</html>
You need to stop the event from reaching (bubbling to) the parent (the div).
See the part about bubbling here, and jQuery-specific API info here.
To specify some sub element as unclickable write the css hierarchy as in the example below.
In this example I stop propagation to any elements (*) inside td inside tr inside a table with the class ".subtable"
$(document).ready(function()
{
$(".subtable tr td *").click(function (event)
{
event.stopPropagation();
});
});
You can check whether the target is not your div-element and then issue another click event on the parent after which you will "return" from the handle.
$('clickable').click(function (event) {
let div = $(event.target);
if (! div.is('div')) {
div.parent().click();
return;
}
// Then Implement your logic here
}
Here is a non jQuery solution that worked for me.
<div style="background:cyan; width:100px; height:100px;" onclick="if (event.srcElement==this) {console.log('outer');}">
<a style="background:red" onclick="console.log('inner');">Click me</a>
</div>
for those that are not using jQuery
document.querySelector('.clickable').addEventListener('click', (e) =>{
if(!e.target.classList.contains('clickable')) return
// place code here
})
In case someone had this issue using React, this is how I solved it.
scss:
#loginBackdrop {
position: absolute;
width: 100% !important;
height: 100% !important;
top:0px;
left:0px;
z-index: 9; }
#loginFrame {
width: $iFrameWidth;
height: $iFrameHeight;
background-color: $mainColor;
position: fixed;
z-index: 10;
top: 50%;
left: 50%;
margin-top: calc(-1 * #{$iFrameHeight} / 2);
margin-left: calc(-1 * #{$iFrameWidth} / 2);
border: solid 1px grey;
border-radius: 20px;
box-shadow: 0px 0px 90px #545454; }
Component's render():
render() {
...
return (
<div id='loginBackdrop' onClick={this.props.closeLogin}>
<div id='loginFrame' onClick={(e)=>{e.preventDefault();e.stopPropagation()}}>
... [modal content] ...
</div>
</div>
)
}
By a adding an onClick function for the child modal (content div) mouse click events are prevented to reach the 'closeLogin' function of the parent element.
This did the trick for me and I was able to create a modal effect with 2 simple divs.
If a child element is clicked, then the event bubbles up to the parent and event.target !== event.currentTarget.
So in your function, you can check this and return early, i.e.:
var url = $("#clickable a").attr("href");
$("#clickable").click(function(event) {
if ( event.target !== event.currentTarget ){
// user clicked on a child and we ignore that
return;
}
window.location = url;
return true;
})
This is what you are looking for
mousedown event. this works on every DOM elements to prevent javascript focus handler like this:
$('.no-focus').mousedown(function (e) {
e.prevenDefault()
// do stuff
}
in vue.js framework, you can use modifier like this:
<span #mousedown.prevent> no focus </span>
Note that using on the input will prevent text selection handler
add a as follows:
....
or return false; from click handler for #clickable like:
$("#clickable").click(function() {
var url = $("#clickable a").attr("href");
window.location = url;
return false;
});
All solution are complicated and of jscript. Here is the simplest version:
var IsChildWindow=false;
function ParentClick()
{
if(IsChildWindow==true)
{
IsChildWindow==false;
return;
}
//do ur work here
}
function ChildClick()
{
IsChildWindow=true;
//Do ur work here
}
<a onclick="return false;" href="http://foo.example">I want to ignore my parent's onclick event.</a>
For my Angular js grid work, I am using ui-grid(v3.1.1) to implement the grid table. When I change the pagination size, I am having trouble to scroll down using mousewheel. When I hover over the mouse on scrollbar, then scroll up and down both are working fine. Strangely, the scroll up using mousewheel is working fine too. Its just the scroll down that doesn't seem to work. I fail to understand the reason for it.
I have disabled the scrolling when not required using:
.ui-grid-render-container-body .ui-grid-viewport {
overflow: auto !important;
}
Other than this I have not changed any of the default settings. Why is it that the mousewheel scroll down won't work when hovered over ui-grid? Please help
I commented out event.preventDefault() on line 2859 of v4.0.2 (in gridUtil.on.mousewheel function).
Can't say what other impact this may have, but worked for my usage.
Experienced this mousewheel/trackpad drag issue in v4.0.2 & v4.0.3 in a grid with pagination & filtering enabled and with CSS modifications to enable cell word-wrap.
.ui-grid-viewport .ui-grid-cell-contents {
word-wrap: break-word;
white-space: normal !important;
overflow:visible;
}
.ui-grid-cell {
display : table-cell;
height: auto !important;
overflow:visible;
position: static;
}
.ui-grid-row {
height: auto !important;
display : table-row;
position: static;
}
.ui-grid-row div[role=row] {
display: flex ;
align-content: stretch;
}
Tried Paul's fix and it worked but requires change to core code. Also, our requirements called for showing the entire table on the page based on the data rather than a fixed height table with scroll. So we couldn't use.
Instead, I worked around the issue by implementing a dynamic grid height solution.
Added ng-style to the grid as follows:
<div ui-grid="gridOptions" style="float:left" ui-grid-selection ui-grid-pagination ng-style="gridHeight()" class="myGrid"></div>
Disabled grid scrolling:
$scope.gridOptions.enableHorizontalScrollbar = 0;
$scope.gridOptions.enableVerticalScrollbar = 0;
Wrote function gridHeight as follows (uses jQuery):
$scope.gridHeight = function() {
var outerHeight = 50; //buffer for things like pagination
$('.ui-grid-row').each(function() {
outerHeight += $(this).outerHeight();
});
$('.ui-grid-header').each(function() {
outerHeight += $(this).outerHeight();
});
return { height: outerHeight+"px"};
};
This satisfied our requirements.
Another way to solve this - if the grid has no scroll at least - is to redefine the prototype function atTop & atBottom on uigrid's ScrollEvent. This will not need changes to uigrid's code, one just has to inject ScrollEvent in the controller or app.run().
The grid swallows any scroll event if its possible to scroll, but does not handle it if there are no scrollbars present.
The original code also fails an isNaN check on this.y.percentage - i think it should be as it is in the comment below.
ScrollEvent.prototype.atTop = function(scrollTop) {
return true;
//return (this.y && (this.y.percentage === 0 || isNaN(this.y.percentage) || this.verticalScrollLength < 0) && scrollTop === 0);
};
ScrollEvent.prototype.atBottom = function(scrollTop) {
return true;
//return (this.y && (this.y.percentage === 1 || isNaN(this.y.percentage) || this.verticalScrollLength === 0) && scrollTop > 0);
};
I have an app with <ons-sliding-menu> and a page with <ons-toolbar> and a horizontal <ons-carousel> covering the remaining space.
For the <ons-sliding-menu> the parameter swipe-target-width="50px" is set.
Is there a way to tell the <ons-carousel> to ignore events originating from the most left 50px and let these go to the menu?
Currently there is no option to make the carousel ignore events on one side, but perhaps you can make a trick. You can put a div at the same level than the carousel and let it take the clicks instead of the carousel in the area you need:
<div class="cover"></div>
<ons-carousel>
...
</ons-carousel>
You can change these values to fit your case:
.cover {
position: absolute;
left: 0;
height: 100%;
width: 200px;
z-index: 1;
}
Check it out here: http://codepen.io/frankdiox/pen/YqKOJE
Hope it helps!
After some experimentation, I came to the solution to inject the necessary functionality directly in the drag event handlers of the OnsCarouselElement. For this purpose I have introduced the attribute swipe-ignore-left for the <ons-carousel>. The other sites could easily be added when needed.In order to inject the functionality, load this JS-Code after loading onsenui.js:
(function () {
'use strict';
/****************************************************************
Checks the current event against the attribute swipe-ignore-left.
****************************************************************/
window.OnsCarouselElement.prototype._ignoreDrag = function (event) {
var attr = this.getAttribute('swipe-ignore-left');
if (attr === undefined) return false;
var left = parseInt(attr, 10);
if (left === undefined || left < 1) return false;
var startX = event.gesture.center.clientX - event.gesture.deltaX;
return startX < left;
};
/****************************************************************
Save the original drag-event-handlers
****************************************************************/
var originalCarouselOnDrag = window.OnsCarouselElement.prototype._onDrag;
var originalCarouselOnDragEnd = window.OnsCarouselElement.prototype._onDragEnd;
/****************************************************************
Override: OnsCarouselElement.prototype._onDrag
****************************************************************/
window.OnsCarouselElement.prototype._onDrag = function (event) {
if (this._ignoreDrag(event)) return;
originalCarouselOnDrag.apply(this, arguments);
};
/****************************************************************
Override: OnsCarouselElement.prototype._onDragEnd
****************************************************************/
window.OnsCarouselElement.prototype._onDragEnd = function (event) {
if (this._ignoreDrag(event)) return;
originalCarouselOnDragEnd.apply(this, arguments);
};
})();
To preserve for example the left 20 pixel for the <ons-sliding-menu>, this HTML is to provide:
<ons-sliding-menu ... side="left" swipeable swipe-target-width="20px" />
...
<ons-carousel ... swipeable swipe-ignore-left="20px" />
I faced with strange behaviour of uib-collapse.
Let's assume I have a list of elements and i want each of them to be collapsed. Also i want to refresh its content periodically depend on something.
For example: i have some items and each of them have description which consists of some sections. I can pick item and description sections should be populated with item's description content. The problem is that each time i refresh its content, some sections are collapsing (despite the fact i set uib-collapse to false)
My controller:
var i = 0;
$scope.sections = [0,1,2];
$scope.next = function(nextOffset) {
i+=nextOffset;
$scope.sections = [i, i+1, i+2]
}
My template:
<button ng-click="next(1)" style="margin-bottom: 10px">Next item</button>
<button ng-click="next(2)" style="margin-bottom: 10px">Next next item</button>
<button ng-click="next(3)" style="margin-bottom: 10px">Next next next item</button>
<div ng-repeat="section in sections">
<div uib-collapse="false">
<div class="well well-lg">{{ section }}</div>
</div>
</div>
So when i click first button, only one section does transition. When i click second, 2 section do transition and click to third button leads to all section transition.
See plunkr
Any ideas?
UPD: if $scope.sections is array of object, not of primitives, then all sections have transition in each of 3 cases. It is so ugly...
You are not refreshing the existing content, you are adding new arrays each time, which will make ng-repeat remove the old DOM elements and insert new ones.
If you try with track by $index you will see the difference:
<div ng-repeat="section in primitiveSections track by $index">
Demo: http://plnkr.co/edit/hTsVBrRLa8nWXhaqfhVK?p=preview
Note that track by $index might not be the solution you want in your real application, I just used it for demonstration purposes.
What you probably need is to just modify the existing objects in the array.
For example:
$scope.nextObject = function(nextOffset) {
j += nextOffset;
$scope.objectSections.forEach(function (o, i) {
o.content = j + i;
});
};
Demo: http://plnkr.co/edit/STxy1lAUGnyxmKL7jYJH?p=preview
Update
From the collapse source code:
scope.$watch(attrs.uibCollapse, function(shouldCollapse) {
if (shouldCollapse) {
collapse();
} else {
expand();
}
});
When a new item is added the watch listener will execute, shouldCollapse will always be false in your case so it will execute the expand function.
The expand function will always perform the animation:
function expand() {
element.removeClass('collapse')
.addClass('collapsing')
.attr('aria-expanded', true)
.attr('aria-hidden', false);
if ($animateCss) {
$animateCss(element, {
addClass: 'in',
easing: 'ease',
to: {
height: element[0].scrollHeight + 'px'
}
}).start().finally(expandDone);
} else {
$animate.addClass(element, 'in', {
to: {
height: element[0].scrollHeight + 'px'
}
}).then(expandDone);
}
}
If this is the intended behavior or not I don't know, but this is the reason why it happens.
this is a comment on the original ui-bootstrap library: (and the new uib prefixed directive doesn't comply this comment.)
// IMPORTANT: The height must be set before adding "collapsing" class.
Otherwise, the browser attempts to animate from height 0 (in
collapsing class) to the given height here.
use the deprecated "collapse" directive instead of new "uib-collapse" until it gets fixed.
I'm having an issue working with AngularJS and CSS3 animations.
I currently have a ng-repeat of items that are styled depending on their index. I have the appropriate CSS3 animation properties in place, but I can't seem to get the items to animate when the $index of each item changes (through orderBy).
Here's my ng-repeat list:
<li ng-repeat="video in videos | orderObjectBy:'order'" ng-style="{left: (($index * 160) + ($index * 15) + 'px')}">
{{ video.title }}
</li>
My CSS3:
ul li {
-webkit-transition: all 3s ease !important;
-moz-transition: all 3s ease !important;
transition: all 3s ease !important;
}
My orderObjectBy filter:
.filter('orderObjectBy', function(){
return function(input, attribute) {
if (!angular.isObject(input)) return input;
var array = [];
for(var objectKey in input) {
array.push(input[objectKey]);
}
array.sort(function(a, b){
a = parseInt(a[attribute]);
b = parseInt(b[attribute]);
if (!a && b) return 1;
else if (a && !b) return -1;
else if (a === b) return 0;
else return (a > b) ? 1 : (b > a ? -1 : 0)
});
return array;
}
});
And here's a JSFiddle: http://jsfiddle.net/jakemulley/Y8L5L/
As you can see in the JSFiddle, when you click on a video, it "jumps" to the front, because its index is changed. This is what I want to animate - instead of it jumping.
Any ideas?
Thanks guys!
You must configure your animation CSS code before, as animations guide says, and you're using ngRepeat, so, you should read this: https://docs.angularjs.org/api/ng/directive/ngRepeat#usage_animations.
See a Plunker: http://plnkr.co/edit/VXg7x0PXDyNw5YEbbDLI. On my Plunker I am using a ngRepeat too, so you should see the example after read.