probably event bubbled on wrong element in custom directive - javascript

My goal is to flip a simple element on page on mouse click. It works but not on every click. I must click several times in different places across the tile and then the tile flip.
Code is executed as I see in the console, but it looks like click on wrong elements is fired.
I have custom directive with link method:
link: function (scope, element, attrs) {
scope.hideClicked = true;
scope.showClicked = false;
scope.backClicked = function () {
console.log('show front')
scope.hideClicked = false;
scope.showClicked = true;
}
scope.frontClicked = function () {
console.log('hide front');
scope.hideClicked = true;
scope.showClicked = false;
}
console.log(scope, element, attrs);
}
and in the template
<div class="tile" ng-class="{'hide-elem': hideClicked,'show-elem': showClicked }">
<span class="front" ng-click="frontClicked()">{{itemData.value}}</span>
<span class="back" ng-click="backClicked();"></span>
</div>
Full code to reproduct is here: https://codepen.io/lkurylo/pen/Laprjx

Both child-span-Elements use absolute positioning, so it seems as if the browser decides that the second element is displayed "on top" of the first. As a result, the click only registers on the back-Element, which happens to be the top-element.
I suggest you display only one element at any given time, e.g. by adding ng-show or ng-if to both elements.
<span class="front" ng-if="showClicked" ng-click="..."></span>
<span class="back" ng-if="hideClicked" ng-click="..."></span>

Related

perform function once element loaded - angular

I want to have an angular function fire after an element has displayed on the page.
The element is part of a SPA where what is displayed is controlled by a whole bunch of events (so doing things on page load won't work). The display of this element's parent (and therefore the element itself) is controlled by an ng-if which calls a separate function. As the parent doesn't display until that function has returned, there's no logical place to include the logic to alter this child element within that function, and since it's called by angular because of the ng-if, there's no parent function to place the code in once the previous function has returned.
I am currently achieving this by putting the function with my logic in it that always returns true within an ng-if on a child element of the element that has the proper, useful ng-if on it, as this will run as soon as the element has the option of being displayed. While this does work, I feel it's a very dodgy solution to the problem. Is there a more "proper" method of achieving this?
Snippet of the HTML (function names changed for the sake of the question):
<div data-ng-if="shouldTheButtonDisplay()">
<a class="btn"
data-ng-click="irrelevantToQuestion()"
data-ng-if="functionToPerformOnceElementLoaded()"
href="#">
button text
</a>
</div>
Snippet of JS (details changed because irrelevant to question):
$scope.shouldTheButtonDisplay() {
return booleanThatIsRelevantInContext;
}
$scope.functionToPerformOnceElementLoaded = function() {
// Edit state of button (technically an anchor)
var firstRowButton = document.querySelector("a.btn");
firstRowButton.style.background = "green";
return true;
}
I would have done so:
angular
.module('yourModuleName', [])
.controller('yourControllerName', yourControllerNameCtrl)
.directive('yourDirectiveName', yourDirectiveNameDirective);
yourControllerNameCtrl.$inject = ['$scope'];
function yourControllerNameCtrl($scope)
{
$scope.message = 'In controller';
$scope.irrelevantToQuestion = function() {};
}
function yourDirectiveNameDirective()
{
function doSomethingElse(scope)
{
scope.message = 'In directive';
}
return {
restrict: 'A',
scope: 'falsy',
link: function(scope, element)
{
element.find('a').addClass('green');
doSomethingElse(scope);
}
};
}
.btn {
background-color: red;
}
.btn.green {
background-color: green;
}
<html ng-app="yourModuleName">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.js"></script>
</head>
<body ng-controller="yourControllerName">
<div>{{ message }}</div>
<div your-directive-name>
<a class="btn"
data-ng-click="irrelevantToQuestion()"
href="#">
button text
</a>
</div>
</body>
</html>

find <div> in a reloaded HTML page and apply function to it

I have an HTML page that lays out div components and every div component call a function and pass this as an object. The div looks like this:
<div id="pull-requests-view">
<div class="green-header">This is the 1st title</div>
# this is the div that will flash on double click
<div class="green" ondblclick="flashAndMessage(this)" onclick="unblink(this)" id="tile0">
<img class="profile-photo" src="someImageURL">
<div class="pill-green">
<a class="hypLink" href="UNIQUE_LINK_FOR_EACH_DIV" target="_blank">tag information</a>
</div>
</div>
</div>
What's really happening here is that there are multiple divs like that with different id. When I click double click on the div its calls the flashAndMessage() with argument this which results in my div to blink and flash using JQuery fadeIn() and fadeOut().
Details aside, the actual problem is that I have polling implemented that re-renders the HTML including all the divs. So basically the flashing and blinking is lost once the page updates.
I have tried global variables that store the this object before the page reloads and tries to pass the variables to jQuery find() function so that flashAndMessage() could be re-applied.
However it is not working for me. Here are my functions:
function unblink(selector) {
$(selector).stop()
$(selector).fadeIn({opacity: 1})
}
function blink(selector) {
$(selector).fadeOut('slow', function () {
$(this).fadeIn('slow', function () {
blink(this);
});
});
}
function flashAndMessage(selector) {
var pullReqURL = $(selector).find('.hypLink').attr('href')
blinkList.push(selector) # this is where the `div` is stored before the page reloads
blink(selector)
}
Note: The blinkList is a global array that is defined on top of everything else. I have a loop that runs after the divs are reloaded and I do see those divs that I stored in the list. However, when I iterate through that list and pass the obj to the flashAndMessage(), it does not work. It feels like the object is not the same when re-drawn or it loses some identity.
Code snippet not work because of restrictions, but code is tested and functional.
function unblink(selector) {
$(selector).stop();
$(selector).fadeIn({opacity: 1});
sessionStorage.href = "";
}
function blink(selector) {
$(selector).fadeOut('slow', function () {
$(this).fadeIn('slow', function () {
blink(this);
});
});
}
function flashAndMessage(selector) {
sessionStorage.href = $(selector).find('.hypLink').attr('href');
blink(selector);
}
if (sessionStorage.href) {
flashAndMessage($("a[href='"+sessionStorage.href+"']").parent().parent());
}
<div id="pull-requests-view">
<div class="green-header">This is the 1st title</div>
<div class="green" ondblclick="flashAndMessage(this)" onclick="unblink(this)" id="tile0">
<img class="profile-photo" src="someImageURL">
<div class="pill-green">
<a class="hypLink" href="UNIQUE_LINK_FOR_EACH_DIV" target="_blank">tag information</a>
</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Assuming you have a parent to <div id="pull-requests-view"> that is not getting reloaded with your polling code, you should use event delegation (and not inline event handlers).
<div id="parent-id-that-isnt-updated">
<div id="pull-requests-view">
...
</div>
</div>
You can attach your event listeners to the parent that doesn't update and have it only trigger when the clicked element matches the selector:
var $staticParent = $('#parent-id-that-isnt-updated');
$staticParent.on('dblclick', '.green', function() {
flashAndMessage(this);
});
$staticParent.on('click', '.green', function() {
unblink(this);
});

How to make a simple menu navigation in angularjs

I'm not really satisfied with what I can find. I just want a simple example of a menu system in Angularjs with hover effect and selection effect.
I understand that 'hover' effects can be done in CSS, but this is more of an exercise for me to understand angularjs better.
What I am trying to do is pretty basic stuff.
Basically I have some HTML which has some DIVs (or menu items) :
<div NavCtrl id="header">
<div class="item" ng-click="click(1)" ng-mouseenter="hoverIn()" ng-mouseleave="hoverOut()">
1
</div>
<div class="item" ng-click="click(2)" ng-mouseenter="hoverIn()" ng-mouseleave="hoverOut()">
2
</div>
<div class="item" ng-click="click(3)" ng-mouseenter="hoverIn()" ng-mouseleave="hoverOut()">
3
</div>
</div>
I want to do two things.
listen on click
listen on mouseIn and mouseOut
I understand..
It is probably nicer to do the hover effect in CSS
It is possible to do some inline logic in the HTML with angularjs
...because I want to have flow on effects from these events. A hover event needs to pull information out related to that menu item, and a click event should also be able to perform some action related to that menu item. Cheap CSS tricks are not going to solve this!
For my hover logic, I thought this would do the trick :
$scope.hoverIn = function($event){
angular.element($event.srcElement).addClass('hover')
};
$scope.hoverOut = function($event){
angular.element($event.srcElement).removeClass('hover')
};
However $event is undefined :( . How do I get to the element object from a mouseover event?
My click logic looks like this :
$scope.click = function(position, $event) {
elem = angular.element($event.srcElement);
if (elem.hasClass("clicked")) {
elem.removeClass("clicked")
}else {
elem.addClass("clicked")
}
// if (position == 1) //do something etc...
};
Same problem : $event is undefined. I also want to pass in the index, so that I can do something special for certain menu items.
My Fiddle is here :
https://jsfiddle.net/zxjg3tpo/5/
ng-mouseenter="hoverIn($event)"
How it works: ng-mouseenter is kinda clever and it has $event in its scope in addition to what you have (i.e. you have hoverIn). So when it parse provided expression, it launches hoverIn with event.
All work with elements, like addClass should be done in directives where you have direct access to html element. Sometimes you may need angular.element(...) but in most cases you are happy with current element. (In directive link : function(scope, element, attrs))
In angularjs you can get the event by using $event in your html code
<div class="item" ng-click="click(1,$event)" ng-mouseenter="hoverIn($event)" ng-mouseleave="hoverOut($event)">
Hover Logic
$scope.hoverIn = function($event){
angular.element($event.target).addClass('hover')
};
$scope.hoverOut = function($event){
angular.element($event.target).removeClass('hover')
};
Click logic
$scope.click = function(position, $event) {
elem = angular.element($event.target);
if (elem.hasClass("clicked")) {
elem.removeClass("clicked")
}else {
elem.addClass("clicked")
}
// if (position == 1) //do something etc...
};
Updated Fiddle : https://jsfiddle.net/zxjg3tpo/6/
Here is another updated Fiddle where the siblings have their class removed (to make the click work correct)
https://jsfiddle.net/zxjg3tpo/9/
You missed to pass $event from html and the srcElement was wrong.
Please try the following:
HTML
<body ng-app="navTest" ng-controller="NavTestCtrl">
<div id="header">
<div class="item" ng-click="click(1, $event)" ng-mouseenter="hoverIn($event)" ng-mouseleave="hoverOut($event)">
1
</div>
<div class="item" ng-click="click(2, $event)" ng-mouseenter="hoverIn($event)" ng-mouseleave="hoverOut($event)">
2
</div>
<div class="item" ng-click="click(3, $event)" ng-mouseenter="hoverIn($event)" ng-mouseleave="hoverOut($event)">
3
</div>
</div>
</body>
JS Code:
var app = angular.module('navTest', [
]);
app.controller('NavTestCtrl', function ($scope, $location, $http) {
$scope.click = function(position, $event) {
elem = angular.element($event.target);
if (elem.hasClass("clicked")) {
elem.removeClass("clicked")
}else {
elem.addClass("clicked")
}
// if (position == 1) //do something etc...
};
$scope.hoverIn = function($event){
angular.element($event.target).addClass('hover')
};
$scope.hoverOut = function($event){
angular.element($event.target).removeClass('hover')
};
});

Can Bootstrap popovers not be destroyed?

Is it possible to have a bootstrap (v3) popovers to have it's div loaded right at the start of pageload and not be destroyed when it is being toggled?
I have a popover content in a div:
<div id="popoverContent">
<h1>Stuff</h1>
<p>I'm in a popover!</p>
</div>
And a button that toggles the popover:
<a id="floating_tab" data-toggle="popover" data-placement="left">Button</a>
Here is my Javascript code that handles the button pushes:
<script>
var x = false;
$('[data-toggle=popover]').popover({
content: $('#popoverContent').html(),
html: true
}).click(function() {
if (x) {
$(this).popover('hide');
x = false;
}
else {
$(this).popover('show');
x = true;
}
});
</script>
The thing is, that when $(this).popover('show'); is called, a div is created. Something like this shows up in the inspect element (chrome):
<div class="popover fade left in" role="tooltip" id="popover460185" style="top: 430.5px; left: 2234px; display: block;"><div class="arrow" style="top: 50%;"></div><h3 class="popover-title" style="display: none;"></h3><div class="popover-content">
<h1>Stuff</h1>
<p>I'm in a popover!</p>
</div></div>
But when the button is clicked again, the whole div itself is removed and obliterated from the page.
Is it possible to have the popover div to be created during pageload (hidden though) and can be toggleable without having the div to be deleted?
As stated in the comments, it is not presently possible with Bootstrap 3. The Popover (which is an extension of the Tooltip) is dynamically created on show and detached (using jQuery.detach) from the DOM on hide.
It is probably best to roll your own JavaScript and simply utilize Bootstrap's CSS. However, you could easily patch the functionality using the Popover's event API -the following can be used as a starting place:
$(function () {
var content = $('#popover-content'), // Pre defined popover content.
popover = $('#popover-anchor').popover();
popover.on('inserted.bs.popover', function () {
var instance = $(this).data('bs.popover');
// Replace the popover's content element with the 'content' element
instance.$tip = content;
});
popover.on('shown.bs.popover', function () {
var instance = $(this).data('bs.popover');
// Remove the reference to 'content', so that it is not detached on hide
instance.$tip = null;
});
popover.on('hide.bs.popover', function () {
// Manually hide the popover, since we removed the reference to 'content'
content.removeClass('in');
content.addClass('out');
});
});
Codepen

ng-blur on a DIV

I am trying to hide a DIV on blur (the focus has been removed from the DIV). I am using angular and bootstrap. So far I have tried setting "focus" on the DIV when it is shown and then an ng-blur function when the user click anywhere else on the screen. This is not working.
Basically the problem is I cannot set focus on my "#lockerBox" through JS, my "hideLocker" function works no problem when focus is given to my DIV with clicking it.
<div class="lock-icon" ng-click="showLocker(result); $event.stopPropagation();"></div>
<div ng-show="result.displayLocker" id="lockerBox" ng-click="$event.stopPropagation();" ng-blur="hideLocker(result)" tabindex="1">
$scope.displayLocker = false;
$scope.showLocker = function ( result ) {
$scope.displayLocker = !$scope.displayLocker;
node.displayLocker = $scope.displayLocker;
function setFocus() {
angular.element( document.querySelector( '#lockerBox' ) ).addClass('focus');
}
$timeout(setFocus, 100);
};
$scope.hideLocker = function ( node ) {
$scope.displayLocker = false;
node.displayLocker = $scope.displayLocker;
};
Just add tabindex="-1" attr to the div and it gives it the same behave like an input -
<div tabindex="-1" ng-blur="onBlur()"></div>
https://jsfiddle.net/ast12nLf/1/
(Inspired from here - https://stackoverflow.com/a/17042452/831294)
Even I had the same problem - but was able to resolve it using 'mouseup' event on the entire document in a custom directive following is the code,
where I am toggling an accordion contained in a div as pop-up using ng-show()="variable"
now this variable is the key here across menu toggle button HTML Element, div pop up HTML Element, and the custom directive to handle mouse up!
var dummyDirective = angular.module(dummyDirective, []);
dummyDirective.directive('hideOnMouseUpElsewhere', function() {
return {
restrict: 'A',
link: function(scope, element, attr) {
$(document).mouseup(function(e) {
var container = $(element);
if (!container.is(e.target) // if the target of the click isn't the container...
&& container.has(e.target).length === 0 && scope.status.menuVisible // ... nor a descendant of the container
&& !(e.target.id === attr.excludeClick)) // do not execute when clicked on this
{
scope.status.menuVisible = false;
scope.$apply();
}
});
}
}
})
where container will have the DOM element you applied 'hideOnMouseUpElsewhere' directive on along with added attribute 'excludeClick' and e.target is the one DOM element where you click
Following is the HTML code for a accrodian bootstrap angular popup menu:
<a id="MenuAnchor" href="" data-toggle="dropdown" class="dropdown-toggle" ng-click="status.menuVisible = !status.menuVisible;">
<i id= "MenuIcon" class="glyphicon glyphicon-cog" style="font-size:20px; color: #428bca; cursor:pointer;"></i>
</a>
<div id ="MenuPane" ng-blur="status.menuVisible=false;" hide-on-mouse-up-elsewhere exclude-click='MenuIcon'>
<div class="btn-group favoritesMenu" style="width: 300px;" ng-show="status.menuVisible" >
<accordion close-others="true">
<accordion-group >
<accordion-heading>....</accordion-heading>
<div ng-click="status.menuVisible=false;">Data</div>
</accordion-group>
</accordion>
I ended up just setting the blur event on the element that has the onclick.

Categories

Resources