I'm having a bit of trouble handling multiple, nestled, angularjs mousebutton directives.
I have a template that looks like this:
<div class="project-wrap">
<div class="workspace-wrap">
<div id="workspace" class="workspace"
oncontextmenu="return false;"
ng-mousedown="project.checkBtnDown($event)"
ng-mouseup="project.checkBtnUp($event)"
ng-mousemove="project.mouseMoved($event)">
</div>
</div>
<button class="logoutBtn" ng-click="project.logout()">
Logout
</button>
</div>
What checkBtnDown() does, is simply check which of the mousebuttons was pressed and then processes it.
The problem I'm having is, when the left mousebutton is pushed down on the "workspace" (within my ProjectCtrl's template), it places a SVG element inside the "workspace" div. This SVG element is bound with an custom angular directive, which has a ng-click on it's template.
So what's happening is, I create the SVG element as planned but, when I click on the portion of the SVG element that I want to call a function on scope. It's still calling checkBtnDown(), because the new SVG element is inside the project template.
How can I get the SVG element ng-click to "peek through" and not fire checkBtnDown() simultaneously?
Hi, i don't know if you meaning this or not..hope this helps you.
in fact you just need to detect if your mouse clicked or not, for that we need to something to detect this for second time.
var app = angular.module("app", []);
app.controller("ctrl", function ($scope) {
$scope.alreadyClicked = false;
$scope.action = function() {
console.log("action");
}
$scope.mousedown = function (event) {
if ($scope.alreadyClicked) {
$scope.action();
} else {
console.log("mousedown");
$scope.alreadyClicked = true;
}
}
});
.box {
position: relative;
background: #eee;
border: solid 1px #ccc;
float: left;
width: 100px;
height: 100px;
cursor:pointer
}
<!DOCTYPE html>
<html ng-app="app" ng-controller="ctrl">
<head>
<title></title>
</head>
<body>
<div class="box" ng-mousedown="mousedown($event)">
{{alreadyClicked ? "click to call action":"click to call mousedown"}}
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</body>
</html>
What I decided to do using your suggestion Mayer,
"In fact you just need to detect if your mouse clicked or not, for that we need to [do] something to detect this for second time."
was to measure the maximum time between the first and second mouse-events, then use a setTimeout().
Here's some pseudo-code to illustrate my idea.
// First mouse-down event
if (firstEvent) {
var timerID = setTimeout(function() {
// handle first mouse-event
}, dTimeBetween);
}
// Second mouse-down event
if (secondEvent) {
clearTimeout(timerID);
function() {
// handle second mouse-event
}
}
Related
const findClosestParent = (startElement, fn) => {
const parent = startElement.parentElement;
if (!parent) return undefined;
return fn(parent) ? parent : findClosestParent(parent, fn);
};
$document.on('click', event => {
let target = event.target;
if (findClosestParent(target, parent => parent.classList.contains('.calendar')) {
// do some things
}
});
I'm trying to find if an element is within another element. I can't do that with closest in Internet Explorer 11. So I went the route of creating a recursive searching up the DOM tree function. However, I also cannot simply grab the parent element of the targeted event element in Internet Explorer 11 in an angular js controller using jQuery's event.target property. This does snippet not work because startElement.parentElement in findClosestParent is always null no matter what I click. Also, startElement.parentNode is null. event.currentTarget.parentNode is null. I have tried so many things. Nothing works. Another thing I tried, I cannot simply add an id to the divs I am trying to select and use a pure vanilla js getElementById query selector because these elements are abstracted away in a 3rd party library. I would very much so like to be able to use event target or something similar to get me the dang element in IE11.
I think it boils down to the fact that, according to https://developer.mozilla.org/en-US/docs/Web/API/Node/parentElement, for IE, parentElement is "Only supported on Element". Thats what it says... what that means is that I think internet explorer doesn't think event.target is an "Element".
Here is a simple solution using angularjs and jQuery Core.
Try clicking somewhere inside and outside the element with the calendar class.
(function () {
'use strict';
angular.module('app', []);
angular.
module('app')
.controller('myController', ['$scope', function ($scope) {
$(document).on('click', function ($event) {
var nativeElement = $event.target;
if ($(nativeElement).closest('.calendar').length) {
console.log('Inside a calendar');
}
else {
console.log('Outside a calendar');
}
});
}])
})();
.calendar {
margin: 20px;
padding: 20px;
border: 2px solid blue;
}
.widget {
margin: 20px;
padding: 20px;
border: 2px solid green;
}
<!DOCTYPE html>
<html lang="en" ng-app="app">
<head>
<meta charset="utf-8" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.min.js"></script>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script src="module.js"></script>
<link rel="stylesheet" href="style.css" />
</head>
<body ng-controller="myController">
<div class="calendar">
<div class="ancestor-2">
<div class="ancestor-1">
Calendar
</div>
</div>
</div>
<div class="widget">
<div class="ancestor-2">
<div class="ancestor-1">
Not a calendar
</div>
</div>
</div>
</body>
</html>
JSFiddle demo
Languages involved: HTML, CSS, JS
Context: I'm relatively new to web development. I have two elements overlapping each other. One is a slider, one is a div. The slider is on top of the div.
Code snippets:
<div id="myDiv">
<input id="mySlider" type="range" min=1 max=100 step=1>
</div>
and
initListeners() {
document.getElementById("myDiv").addEventListener("click", divFunction);
document.getElementById("mySlider").addEventListener("input", sliderFunction);
}
I need to make it that when you click the slider, it doesn't click the div. How would I go about doing that? I've tried z-index, but that doesn't seem to change anything.
Thanks in advance!
As I'm sure you've figured out by now, events in JavaScript by default bubble up from a child to a parent. You need to stop that from happening at the child level, also known as preventing propagation.
Using the stopPropagation function, you can handle this as follows:
function sliderFunction(e) {
e.stopPropagation();
}
Simple. That event will no longer reach the parent.
EDIT
While stop propagation is the correct method to use, event listeners must also match in type. Therefore, both the slider and the parent DIV must have click event listeners (instead of input and click). stopPropagation stops propagation of a specific type of event.
function divFunction() {
console.log('DIV clicked!');
}
function sliderFunction(event) {
event.stopPropagation();
console.log('Slider clicked!');
}
function initListeners() {
document.getElementById('myDiv').addEventListener('click', divFunction);
document.getElementById('mySlider').addEventListener('click', sliderFunction);
}
initListeners();
/* unnecessary visual aides */
body *:not(label) {
padding: 2rem;
outline: 1px solid red;
position: relative;
}
label {
display: inline-block;
position: absolute;
background: #222;
color: #fff;
top: 0; left: 0;
}
<div id="myDiv">
<label>#myDiv</label>
<div id="tools">
<label>#tools</label>
<input type="range" id="mySlider">
</div>
</div>
You can also check the target once you fire that click event. I've used this approach before:
JSFiddle: http://jsfiddle.net/L4ck7ygo/1/
function divFunction(e) {
if (e.target !== this) {
return;
} else {
console.log('hit');
}
}
When the fiddle first loads, click the slider and you'll see the console log out some text. To see it work, remove the line that is being pointed to and rerun the fiddle. Now when you click the slider, you won't see anything logged in the console, but if you click on the div and not the slider, it will log to the console.
function initListeners() {
document.getElementById("myDiv").addEventListener("click", divFunction);
document.getElementById("mySlider").addEventListener("input", sliderFunction);
}
initListeners();
function divFunction(e) {
console.log('Firing...') // <-- This will log on any click
if (e.target !== this) {
return;
} else {
console.log('hit'); // <-- This will NOT log except for div click
}
}
function sliderFunction() {
console.log('Doing stuffs...');
}
<div id="myDiv">
<input id="mySlider" type="range" min=1 max=100 step=1>
</div>
UPDATE: Stupidity on my part. I had the ordering wrong for the elements which caused propagation to not act as intended.
I am having a div inside a div. And I want to call a function on the click of outer div and another function on the click of inner div. Is it possible to do so?
<div onclick="function1()">
<div onclick=function2()></div>
</div>
Yes, this is very much possible. And the code you have will get the job done.
NOTE: You need to add event.stopPropagation() in case you want to prevent the bubbling of the event from the inner function.
Try this out:
function function1() {
console.log("From outer div");
}
function function2(event) {
console.log("From inner div");
event.stopPropagation();
}
#outer-div {
width: 100px;
height: 100px;
background: yellow;
}
#inner-div {
width: 50px;
height: 50px;
background: red;
position: relative;
top: 25px;
left: 25px;
}
<div id="outer-div" onclick="function1()">
<div id="inner-div" onclick="function2(event)"></div>
</div>
Yes, it is; one way to do it is the way you've done it in your question, except:
You need quotes around the inner onclick attribute value, just as you have around the outer onclick attribute value.
You probably want to pass event into at least the inner one:
<div onclick="function2(event)"></div>
and then have it call stopPropagation on that:
function function2(event) {
event.stopPropagation();
}
so that the click event isn't propagated to the parent (doesn't bubble any further). If the click bubbles, function1 will be called as well.
Example:
function function1() {
console.log("function1 called");
}
function function2(event) {
event.stopPropagation();
console.log("function2 called");
}
<div onclick="function1()">
<div onclick="function2(event)">this div fires function2</div>
clicking here will fire function1
</div>
You might also consider modern event handling rather than onxyz-attribute-style event handlers; search for examples of addEventListener for details; my answer here also has a useful workaround for obsolete browsers.
I may just be thinking about this wrong because I'm doing it in Angular and over complicating, but what I'm trying to do is setup my click event so it only triggers when an element is clicked, but not it's child. I'm trying to setup a modal, where if you click the background overlay it closes, but obviously I don't want it closing if the user interacts with the modal.
<div class="overlay">
<div class="modal">
</div>
</div>
So far, I've created this:
#HostListener('document:click', ['$event.target']) public onClick(targetElement: HTMLElement) {
const clickedInside = this._elementRef.nativeElement.contains(targetElement);
if (!clickedInside && targetElement.class.indexOf('overlay') && targetElement.parentElement.tagName === 'GP-MODAL') {
//do stuff
}
}
Where _elementRef is the Angular ElementRef. The problem is it feels like an inefficient way of doing it: trigger on any click, only continue on certain elements. It feels more ideal to trigger a click on .overlay and then somehow not have it go off in .wrapper, but I can't think of how to do it. Any advice?
I think what you are looking for is the stopPropagation method, which resides in the event object
(function () {
var app = angular.module('app', []);
app.controller('MainCtrl', MainController);
MainController.$inject = [];
function MainController () {
var vm = this;
vm.onClick = function (event) {
event.stopPropagation();
alert('Child clicked');
};
vm.parentOnClick = function (event) {
alert('Parent clicked');
};
}
}) ();
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular.min.js"></script>
<body ng-app="app" ng-controller="MainCtrl as vm">
<div ng-click="vm.parentOnClick($event)" style="height: 400px; width: 400px; background: red;">
<div ng-click="vm.onClick($event)" style="position: absolute; top: 100px; left: 100px; background: blue; height: 200px; width: 200px;">
</div>
</div>
</body>
I came up with two possible methods, depending on selection criteria.
The first is just Javascript:
If you know a specific attribute of the clicked element you can check on, create a click event on the element you want to track, bound to:
clickedOverlay(event) {
if (event.target.parentElement.tagName === 'GP-MODAL') {
// Do stuff
}
}
A more Angular focused answer:
#ViewChild('target') targetRef: ElementRef;
clickedOverlay(event: MouseEvent) {
if (this.targetRef.nativeElement === event.target) {
// Do stuff
}
}
It's more explicit, and less prone to false positives, but more verbose.
Not suggesting these are the best answers, but my preference right now.
I was wondering if there was a way to have a centered item shift smoothly when its width changes?
In my case, I have a piece of text on the left that stays the same, and the piece of text on the right will change depending on what page you are on.
<div id="title-container">
<h1 class="inline-header">example.</h1>
<h1 id="title-category" class="inline-header">start</h1>
</div>
The total width of this will change as a result, and it will shift abruptly.
Here is a jsfiddle demonstrating the problem.
https://jsfiddle.net/sm3j26aa/3/
I've currently worked around it by just fixing the left side using relative positioning and translates, but if I can get the smooth transition, I would rather do that.
Thanks for any help!
Instead of fading just the right portion in and out, you'll need to fade the entire line.
Also, there is no need for individual functions for each word change. Just have one function that accepts the new word as a parameter.
Lastly, don't use inline HTML event attributes to set up event handlers. It:
creates spaghetti code that is more difficult to read
creates anonymous wrapper functions that alter the this binding
within the function
doesn't follow W3C DOM Even Standards
Instead set up your event handlers in JavaScript.
var $titleContainer = $('#title-container');
var $titleCategory = $('#title-category');
$("button").click(function(){ change(this.textContent); })
function change(text) {
$titleContainer.fadeOut(300, function() {
$titleCategory.text(text);
$titleContainer.fadeIn(600);
})
}
#title-container, #button-container { text-align: center; }
.inline-header { display: inline-block; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="title-container">
<h1 class="inline-header left">example.</h1>
<h1 id="title-category" class="inline-header">start</h1>
</div>
<div id="button-container">
<button>Sample</button>
<button>Hello</button>
<button>SampleX2</button>
</div>
var $titleCategory = $('#title-category');
function changeToSample() {
$titleCategory.fadeOut(300, function() {
$titleCategory.text('sample');
$titleCategory.fadeIn(600);
document.getElementById('title-container').style.marginLeft = `calc(50% - 14em/2)`;
})
}
function changeToHello() {
$titleCategory.fadeOut(300, function() {
$titleCategory.text('hello');
$titleCategory.fadeIn(600);
document.getElementById('title-container').style.marginLeft = `calc(50% - 12em/2)`;
})
}
function changeToDoubleSample() {
$titleCategory.fadeOut(300, function() {
$titleCategory.text('samplesample');
$titleCategory.fadeIn(600);
document.getElementById('title-container').style.marginLeft = `calc(50% - 20em/2)`;
})
}
#title-container {
margin-left: calc(50% - 12em/2);
transition: .2s;
}
#button-container {
text-align: center;
}
.inline-header {
display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="title-container">
<h1 class="inline-header">example.</h1>
<h1 id="title-category" class="inline-header">start</h1>
</div>
<div id="button-container">
<button onclick="changeToSample();">Sample</button>
<button onclick="changeToHello();">Hello</button>
<button onclick="changeToDoubleSample();">SampleX2</button>
</div>