Select specific div and get outerHeight with Angular's jqLite - javascript

I am trying to rid my Webapp of jQuery and only use Angular's jqLite, since there isn't that much I need to do with jQuery anyway and I've read several times that using jQuery with AngularJS is not pretty.
My problem is that I need to select divs with specific id's, but jqLite only supports search by tag names as far as I can see. Furthermore, I need .outerHeight(), which also seems to be absent. So naturally, my current code doesn't work:
app.directive('contentMargin', function($document, $window) {
return {
restrict: 'A',
link: function(scope, elem, attrs) {
$document.ready(function() {
var topMargin = elem.find('#header').outerHeight(true);
var bottomMargin = elem.find('#nav').outerHeight(true);
elem.find('#content').css({'margin-top':topMargin, 'margin-bottom':bottomMargin});
});
}
};
});
Here is where I use the directive:
<div id="wrapper" content-margin>
<!---Header--->
<div id="header">
<div id="logo" class="fullwidth">
<img src="images/logo.jpg"/>
</div>
</div>
<!---Content--->
<div id="content" class="container-fluid">
<div ng-view></div>
</div>
<!---Footer Nav--->
<div id="nav" class="fullwidth" ng-controller="MainController">
<ul class="list-unstyled">
[...]
</ul>
</div>
</div>
Is there any way to achieve this without full jQuery?

mate, I suggest you use Vanilla JS, it's powerful than you thought...
the equivlent code in Vanilla Js is below:
function Dimension(elmID) {
var elmHeight, elmMargin, elm = document.getElementById(elmID);
if(document.all) {// IE
elmHeight = elm.currentStyle.height;
elmMargin = parseInt(elm.currentStyle.marginTop, 10) + parseInt(elm.currentStyle.marginBottom, 10) + "px";
} else {// Mozilla
elmHeight = document.defaultView.getComputedStyle(elm, '').getPropertyValue('height');
elmMargin = parseInt(document.defaultView.getComputedStyle(elm, '').getPropertyValue('margin-top')) + parseInt(document.defaultView.getComputedStyle(elm, '').getPropertyValue('margin-bottom')) + "px";
}
return (elmHeight+elmMargin);
}
In your case, your code looks like:
var topMargin = Dimension('header');
var bottomMargin = Dimension('nav');
I've read most of Jquery/Jquery UI source code, actually, they are just plain code, and sometimes you can do better than them. lol, happy coding bro!

Related

How to write jquery toggle function for icon(which is generated from css) in angularjs?

I have a scenario,where I need to collapse the footer in mobile device,I tried with css media queries and jquery toggle function.But,I need to use angularjs.I tried it with using ng-show/ng-hide.But here,the '+' or '-' is getting from css.So, how I need to give click action for those signs.Click function must be given dynamically.
Script:
$( ".widget h2" ).click(
function() {
$(this).parent().toggleClass('active');
}
);
I need above code in angularjs.
Here is my Plunker
Thanks in Advance.
You can use window.resize event
$(window).resize(function() {
$windowWidth = $(window).width();
if ($windowWidth <= 479) {
$('.widget').addClass('active');
}
});
Here is your updated plunkr
More improved version:
$(window).resize(function() {
CheckWindowWidth();
});
$('.widget h2').click(function() {
$(this)
.parent()
.toggleClass('active');
});
function CheckWindowWidth() {
$windowWidth = $(window).width();
if ($windowWidth <= 479) {
$('.widget').addClass('active');
}
}
CheckWindowWidth();
Edit:
As per angular version you can use this
In your html change
<div class="col-md-5 widget" ng-class="{'active': setSignInData }">
<h2 ng-click="setSignInData = !setSignInData">sign in data</h2>
<article class="widget_content">
<ul ng-class="autoScroll?'widget':'active widget'">
<li>Get a quote</li>
<li>Send quote</li>
</ul>
</article>
</div>
In your controller
$scope.setSignInData = false;
This will change the scope variable and toggle active class
You could probably use ng-class to toggle css classes based on your conditions.
I would personally suggest to avoid using jquery in angularjs projects.
For ways on how you can use the ng-class,
you can go through the following blog

In Angularjs, how do I review the DOM after a route change?

In my app I use ng-view to switch out my views. I need to manipulate elements such as change width and position. When I try to do it with on my controller I'm getting, as expected, Unable to get property 'setAttribute' of undefined or null reference.
I know this is happening since JS doesn't know the DOM has changed.
Since I'm running some heavy plugins to work with SQLite in Windows 8 I can't post too much code.
This is my template being loaded into the ng-view
<div id="productslist" class="container">
<div class="product" ng-repeat="product in products">
<div class="details">
<img src="img/detail-list-back.png" class="texture" />
<div class="heading">
<p class="title">{{product.name}}</p>
<img src="img/products/title-shadow.png" class="title-shadow"/>
</div>
<div class="text">
<div class="text-container">
<p class="intro" ng-hide="product.intro == null">{{product.intro}}</p>
<p class="title">Curves</p>
{{product.prodText}}
</div>
</div>
</div>
<div class="image" style="background-image:url('img/products/nanoslim-bkg.jpg')"></div>
</div>
</div>
some of my angular. the for loop is breaking since it doesn't know the sections exist:
var totalProd = res.rows.length;
var windowW = window.innerWidth;
var sections = document.querySelectorAll('#productslist .product');
var textArea = document.querySelectorAll('#productslist .text');
document.getElementById('productslist').setAttribute('style', 'width:' + (windowW * totalProd) + 'px');
for (var i = 0; i < sections.length; i++) {
sections[i].setAttribute('style', 'width:' + windowW + 'px');
}
Everything else works fine. I'm trying to bypass this by using ng-style but having issues there.
In angular, DOM manipulations should be done in directives where possible. Here's an example of setting the width to the window width:
.directive('fullWidth', function() {
function link(scope, element, attrs) {
var windowW = window.innerWidth;
element.css({width: windowW + 'px'});
}
return {
link: link
};
});
Now in your view
<div class="product" full-width ng-repeat="product in products">
Assuming that you have a different controller for each distinct view, there are possibilities that your DOM takes more time to load then your controller, leading to the issue Unable to get property 'setAttribute' of undefined or null reference.
You can instead put your DOM selector statement in a $timeout statement, with a delay of 1 or 2 seconds, that will give DOM enough time to load and prevent any such null reference issue. Something like this :
myapp.controller("mycontroller"), function($scope, $timeout) {
$timeout(function() {
$("your-dom-selector").setAttribute("height", 500px);
}, 2000);
});
Alternatively a better approach would be to use a broadcast model, in which you can track the route change event, and as soon as the route change event succeeds, you can broadcast an event, with necessary details, that can be captured by the respective controller.
Former approach is easy to use, latter one is standard and guaranteed to be error-free.
More details upon $timeout here

How to change attribute of html in angular js?

I want to change attribute of a div in angularjs. I know how to do in jquery but not in angular.
html :
<div ng-app="test">
<div ng-controller="cont">
<button ng-click="updateStatus()">TOGGLE ATTRIBUTE </button>
<div id="test" {{status}}>TEXT </div>
</div>
</div>
js :
var angApp = angular.module('test',[]);
angApp.controller('cont', ['$scope', function($scope){
$scope.status = 'someattr';
$scope.updateStatus = function() {
if( $scope.status == 'someattr'){
$scope.status = '';
}else{
$scope.status = 'someattr';
}
};
}])
Here is jsfiddle to work with.
In jquery :
var div = $('#test');
$('button').on('click',function(){
if( div.attr('someattr'){
div.removeAttr('someattr');
}else{
div.attr('someattr',true);
}
})
I want to achive same in angularjs.
NOTE : I AM NOT TRYING TO ADD DISABLED STATE TO DIV. I JUST WANT TO TOGGLE AN ATTRIBUTE.
In your specific case (add disabled attribute), you have to use ng-disabled in order to bind its value to a $scope variable.
It makes no sense to use it on a div, I'll use a button instead to give you an example:
<button ng-click="updateStatus()">TOGGLE ATTRIBUTE </button>
<button id="test" ng-disabled='status'>TEXT</button>
see a working example HERE
UPDATE
To toggle an attribute, yo can use attr() and removeAttr():
el.attr("disabled", "true");
el.removeAttr("disabled");
See a complete example HERE
NOTE (thanks to jme11): as reported on Angular Dev Guide
Do not use controllers to:
Manipulate DOM — Controllers should contain only business logic. Putting any presentation logic into Controllers significantly affects its testability. Angular has databinding for most cases and directives to encapsulate manual DOM manipulation.
you should avoid to manipulate the DOM inside the controller.
Make a directive which uses .attr and .removeAttr in a $watch handler. Here's a modified version of your fiddle: http://jsfiddle.net/0eqz1qo1/1/
The directive:
.directive('addAttr', function() {
return function(scope, elem, attr) {
scope.$watch(attr.addAttr, function(val, prev) {
if(val)
elem.attr(val, "");
if(prev && prev !== val)
elem.removeAttr(prev);
});
}
})
Usage:
$scope.myVar = 'hello';
...
<div add-attr="myVar"></div>
becomes:
<div add-attr="myVar" hello></div>
You can not implement disable property for any div.But you can hide or show the div using Angular.js.Check the code below.
<div ng-app="test">
<div ng-controller="cont">
<button ng-click="updateStatus()">TOGGLE ATTRIBUTE </button>
<div id="test" ng-hide="hide-div" >TEXT </div>
</div>
</div>
JS:
var angApp = angular.module('test',[]);
angApp.controller('cont', ['$scope', function($scope){
$scope.hide-div = true;
$scope.updateStatus = function() {
if( $scope.hide-div == true){
//do something here
}else{
$scope.hide-div = true;
}
};
}])
Other option is you can also use ng-class in div and inside those class you can declare display:none,display:block
You can't add an attribute by this way with angularJS. If you inspect your code, you can see that the attribute that you're trying to pass in div is {{status}}(your expression), use existing attributes rather than create your own! For example: ng-disabled, ng-show, ng-hide.
It's not really right thing to do. I guess, cannot inject attribute with angularJS. {{status}} is an expression, it's like expression and will evaluate by angularjs while rendering to html. about expression: https://docs.angularjs.org/guide/expression
Replace your line :
<div id="test" {{status}}>TEXT </div>
with these :
<div id="test" someattr="" ng-if="status=='someattr'" >TEXT</div>
<div id="test" ng-if="status==''" >TEXT</div>

Better (more performant) way to get specific DIVs?

I want to add a specific DIV to other DIVs of a defined class. Because the page changes regularly I do this on every DOM-Change. This happens quite often and there are a lot of DIVs (up to a few 1000) that meet the criteria.
(This is an extension so I cannot modifiy the source)
I do it this way:
$('.Qg').each(function() {
if ($(this).parent().find('.quickShare').length === 0)
{
$(this).before('<div class="quickShare">(some more html)<br/></div>');
}
});
That works but does not seem to be very performant, mainly because of the "each" - Loop
Is there a more elegant (and especially performant) way to get only those DIVs which's parent do not contain my DIV (something like $('.Qg').parent().without('quickShare').each(function(){}); (pseudocode)?
Update: To make it clearer a DOM-Example:
<div class="anOuterDiv>
<div class="Qg">something here</div>
</div>
<div class="anotherOuterDiv">
<div class="quickShare">already added</div>
<div class="Qg">something here</div>
</div>
I want to Add the "quickShare" div before the "Qg", but only if it does not exist. (So I want to get the upper Qg, but not the lower Qg)
Give all the parents of .Qg the class QgContainer, then do:
$(".QgContainer:not(:has(.quickShare)) > .Qg").each(function() {
...
});
Since you can't change the site, try:
$(".Qg").filter(function() {
return $(this).siblings(".quickShare").length == 0);
}).each(function() {
...
});
As you wanted better(more perfomant) then you could consider using pure Javascript.
HTML
<div class="anOuterDiv1">
<div class="Qg">something here</div>
</div>
<div class="anOuterDiv2">
<div class="quickShare">already added</div>
<div class="Qg">something here</div>
</div>
<div class="anOuterDiv3">
<div class="Qg">something here</div>
</div>
<div class="anOuterDiv4">
<div class="quickShare">already added</div>
<div class="Qg">something here</div>
</div>
Javascript
Array.prototype.forEach.call(document.getElementsByClassName('Qg'), function (Qg) {
var parentNode = Qg.parentNode,
quickShares = parentNode.getElementsByClassName('quickShare'),
newQuickShare;
if(!quickShares.length) {
newQuickShare = document.createElement('div');
newQuickShare.className = 'quickShare';
newQuickShare.textContent = 'Newly added';
parentNode.insertBefore(newQuickShare, Qg);
}
});
On jsFiddle
Next we should actually compare it against some jQuery, so we will use the accepted answer.
$(".Qg").filter(function() {
return $(this).siblings(".quickShare").length == 0;
}).each(function() {
$(this).before('<div class="quickShare">Newly added</div>');
});
On jsFiddle
And now lets see how they perform on jsPerf
You can filter each .Qg that's not preceded by a .quickShare sibling and then apply .before() on that:
$('.Qg')
.filter(function() {
var node = this.previousSibling; // start with previous sibling
while (node) {
if (node.className == 'quickShare') {
return false; // we found one
}
node = node.previousSibling; // keep trying with previous sibling
}
return true;
})
.before('<div class="quickShare">(some more html)<br/></div>');
This time it will definitely work:
$('div:only-child.Qg').each(function(){
$(this).before('<div class="quickShare">(some more html)<br/></div>');
});
Try this. This is very easy and readable and small and performant.
jsFiddle Demo http://jsfiddle.net/VS6mG/

dynamically populate into navigation bar from javascript

This is my jquery mobile page header section i want to populate my dynamically populated data from javascript to navigation bar in this code.
<div data-role="page" data-theme="a" id="homePage" >
<div data-theme="a" data-role="header" data-position="fixed">
<h3 class="mblHeading">Client</h3>
<a data-rel="dialog" data-position-to="window" data-transition="slidedown" data-theme="a" data-mini="true" href="#AboutDialog" class="ui-btn-right"> About </a>
<div data-role="navbar" >
<ul id="navid">
<li>One</li>
<li>Two</li>
</ul>
</div>
</div>
My java script code for dynamically populating the content.Here querytableid is navid.navigationList is an array
function populateQueryList4(queryTableID)
{
var listParent = jq(queryTableID);
listEntry = document.createElement("LI");
aNode = document.createElement("A");
aNode.innerHTML=navigationList[k-1];
listEntry.appendChild(aNode);
aNode.onclick = function() {
//displayArtifactContent(artifactAreaInfoMap[wiTitle]);
};
listParent.append(listEntry);
jq("#navid").navbar('refresh');
}
Unfortunately you cant populate navbar just like that. Functions navbar() and navbar('refresh') are not going to help you here, not trigger('create') or trigger('pagecreate'). For some reason, when you dynamically add additional navbar item it will not be styled as it should and this is an error.
There are 2 alternative ways how it can be done.
Dynamically populate navbar during the pagecreate or pagebeforecreate page venet.
Basically during those 2 events page is style not styled according to jQuery Mobile styles. So any added content at this point will be enhanced automatically.
Here's a working jsFiddle example for you: http://jsfiddle.net/Gajotres/SJG8W/
$(document).on('pagebeforecreate', '#index', function(){
$('[data-role="navbar"]').html('<ul>' +
'<li>By Brand</li>' +
'<li>By Flavor</li>' +
'<li>Zero Nicotine</li>' +
'</ul>');
});
Manually enhance dynamically added navbar items
Other solution is to do it by yourself. It is not complicated as you will see in a working example: http://jsfiddle.net/Gajotres/V6nHp/
$('#index').live('pagebeforeshow',function(e,data){
navbarHandler.addNewNavBarElement('navbar-test','el4','Page Four');
});
var navbarHandler = {
addNewNavBarElement:function(navBarID, newElementID, newElementText) {
var navbar = $("#" + navBarID);
var li = $("<li></li>");
var a = $("<a></a>");
a.attr("id", newElementID).text(newElementText);
li.append(a);
navbar = navbarHandler.clearNavBarStyle(navbar);
navbar.navbar("destroy");
li.appendTo($("#" + navBarID + " ul"));
navbar.navbar();
},
clearNavBarStyle:function(navbar){
navbar.find("*").andSelf().each(function(){
$(this).removeClass(function(i, cn){
var matches = cn.match (/ui-[\w\-]+/g) || [];
return (matches.join (' '));
});
if ($(this).attr("class") == "") {
$(this).removeAttr("class");
}
});
return navbar;
}
}
Comments
As you can see for this method to work you must understand how jQuery Mobile page events work, to find out more take a look at my other answer: jQuery Mobile: document ready vs page events.
Also take a look at my other answer regarding enhancement of jQuery Mobile pages markup: jQuery Mobile: Markup Enhancement of dynamically added content

Categories