I am making a directive to set the height of a div equal to the height of the browser if the height of the div is less than the height of the browser.
I have created the following directive:
(function() {
var directive = function ($log, $window) {
function evaluate(element) {
var height = element.prop("offsetHeight");
var pageHeight = $window.innerHeight;
if (height < pageHeight)
{
$log.info("element height is less than page height, changing element height.");
element.css("height", pageHeight);
}
}
return {
restrict: "A",
link: function (scope, element, attrs)
{
//call it one time on load to handle first display
evaluate(element);
$window.addEventListener("resize",
function() {
$log.info("Resize directive called: evaluating window size.");
//window size changed, check if we need to adjust the height
evaluate(element);
});
//add a watch to trigger when the inside of the div changes
scope.$watch(function() { return element[0].childNodes.length }, function(values) {
evaluate(element);
});
}
}
};
angular.module('bootstrapApp')
.directive('fullSize', ["$log", "$window", directive]);
})();
and it is used in the following way:
<body>
<fullsize-background-image-1></fullsize-background-image-1>
<navbar-1></navbar-1>
<div class="container-fluid">
<div class="row">
<left-sidebar-1></left-sidebar-1>
<right-sidebar-1></right-sidebar-1>
<div class="col-xs-12 main" id="main" full-size>
<div ng-view="" autoscroll="true"></div>
<footer-1></footer-1>
</div>
</div>
</div>
....
</body>
My problem is, on page load, thing was working perfectly (without the scope.$watch statement). However, when angular routed to a different page, that content replaced what is in the div#main element. Sometimes the height of the new page is greater than the height of the previous page, causing my hidden <left-sidebar-1> to show.
What I need to do is, on initial page load have my full-size directive evaluate what the height of main should be. Then, when the page changes, have it re-evaluate it and remove the property if the content is bigger.
thanks.
Related
I have an iframe that has a variable height which is not known in advance.
Currently, if the section in which the iframe loads is too small, the iframe loads with internal scroll bars. If the iframe happens to be a shorter iframe, there is empty space below the iframe before the footer begins.
Is there a solution available to this type of problem?
Well, adding simple JS code to get the iframe content height and setting the container height will do, as suggested by #relief.melone.
Other simple solution that can be of help, as an alternative :
https://github.com/davidjbradshaw/iframe-resizer
In reference to my comment. The first thing you have to solve is your cross origins problem. Most browsers will block requests to other websites if the response does not include the current host in their cross origins allow header. So in your case the header from your request to the iframe contents needs to include the header
Access-Control-Allow-Origin: http://159.89.229.184
and
Access-Control-Allow-Mehtods: GET
Also see https://de.wikipedia.org/wiki/Cross-Origin_Resource_Sharing for more info on this.
Now to the actual solution.
You need to determine the height of your iframes contents and then set the height accordingly. You can do this by adding a javascript function. In your head section add
<script>
const setHeight = (frame) => {
frame.style.height = `${frame.contentWindow.document.body.scrollHeight}px`
}
</script>
and your iframe needs to include the onload event
<iframe ... scrolling="no" onload="setHeight(this)" />
This should solve your problem. But as I mentioned just if you allow the cross origin access. Otherwise you access to the document of frame.contentWindow will get rejected with an error like
VM747:1 Uncaught DOMException: Blocked a frame with origin "http://159.89.229.184" from accessing a cross-origin frame.
I also made an example on glitch to demonstrate how it works (Click on Show to see it in action)
https://glitch.com/edit/#!/iframe-varialbe-height
I had s situation where the height of the iFrame content changed dynamically, and I told the parent frame (containing the iFrame) to change it's height accordingly using postMessage: like this
Parent window:
<section class="module">
<div class="row">
<!-- Content column start -->
<div class="col-sm-12" id="web-version-container">
<iframe id="web-version-frame" src="index.html" allowfullscreen=false frameborder="0" style="width:100%; height:100%;min-height:800px;">
</iframe>
</div>
</div> <!-- .row -->
</section>
<script>
window.addEventListener('message', function(event) {
// IMPORTANT: Check the origin of the data!
if (~event.origin.indexOf('https://yourdomain.com')) {
// The data has been sent from your site
if (event.data.messageName) {
switch (event.data.messageName) {
case "documentHeight":
if (event.data.messageValue && parseInt(event.data.messageValue) > 500);
var newHeight = parseInt(event.data.messageValue) + 50;
$("#web-version-frame").css("height",newHeight.toString() + "px");
break;
}
}
// The data sent with postMessage is stored in event.data
} else {
// The data hasn't been sent from your site!
// Be careful! Do not use it.
return;
}
});
</script>
Child window:
if (window.parent) {
window.parent.postMessage(
{
messageName: "documentHeight",
messageValue: $(".content-active").height()
},
"*"
);
}
https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
In the iframe that you have added in second example has the css property min-height as 1600px. Use a percentage for min-height or height to fix the issue. Added min-height: 275vh; and it fixed the issue.
.job-detail-iframe iframe{
width: 100%;
min-height: 275vh;
}
Check this out also as a reference.
I have successfully designed page anchors which will on first click compensate for the fixed header height, code below:
<p id="anchorid" style="padding-top: 287px; margin-top: -287px; width: 20px;">
FILLER TEXT
</p>
The issue I have is that my header shrinks when you scroll, so once you click onto a page via an anchor, the next time you click an anchor -for- that page while -on- that page, the padding over-compensates (as it is set to the initial header height) and the section is brought to the middle of the page rather than the top. What I hope to do is have the anchor padding set dynamically to the height of the header, so that it always brings the section to the top, but I am woefully lost as to how to do this.
Is there a way to use the anchor (the id="filler") to have the browser scroll to a certain point, depending on the height of the header, using CSS?
Similar problem solved here, but their header doesn't change size: HTML position:fixed page header and in-page anchors
EDITED ANSWER :
Here is the code I'm using for a fixed header who takes care of the offset when scrolling to anchor with a smouth scroll using jQuery.
Here is an illustration : https://jsfiddle.net/mmb5k7xb/1/
To see how the script react with a different height, just change the value of the menu height in the html part.
Hope it helps.
<script type="text/javascript">
var $ = jQuery.noConflict();
$(document).ready(function($) {
var menu_height = $('.menu').height(); // calulate the height of the menu
(function(document, history, location) {
var HISTORY_SUPPORT = !!(history && history.pushState);
var anchorScrolls = {
ANCHOR_REGEX: /^#[^ ]+$/,
OFFSET_HEIGHT_PX: menu_height, // Set the offset with the dynamic value
/**
* Establish events, and fix initial scroll position if a hash is provided.
*/
init: function() {
this.scrollIfAnchor(location.hash);
$('body').on('click', 'a', $.proxy(this, 'delegateAnchors'));
},
/**
* Return the offset amount to deduct from the normal scroll position.
* Modify as appropriate to allow for dynamic calculations
*/
getFixedOffset: function() {
return this.OFFSET_HEIGHT_PX;
},
/**
* If the provided href is an anchor which resolves to an element on the
* page, scroll to it.
* #param {String} href
* #return {Boolean} - Was the href an anchor.
*/
scrollIfAnchor: function(href, pushToHistory) {
var match, anchorOffset;
if(!this.ANCHOR_REGEX.test(href)) {
return false;
}
match = document.getElementById(href.slice(1));
if(match) {
anchorOffset = $(match).offset().top - this.getFixedOffset();
$('html, body').animate({ scrollTop: anchorOffset});
// Add the state to history as-per normal anchor links
if(HISTORY_SUPPORT && pushToHistory) {
history.pushState({}, document.title, location.pathname + href);
}
}
return !!match;
},
/**
* If the click event's target was an anchor, fix the scroll position.
*/
delegateAnchors: function(e) {
var elem = e.target;
if(this.scrollIfAnchor(elem.getAttribute('href'), true)) {
e.preventDefault();
}
}
};
$(document).ready($.proxy(anchorScrolls, 'init'));
})(window.document, window.history, window.location);
});
</script>
When using ng-hide or ng-show directives a .ng-class is added or removed so DOM elements are visible or not.
However they kinda get positional "removed" as for example, hiding or showing two continous div elements one on top of the other.
<div ng-show="condition1">First div</div>
<div ng-show="condition2">Second div</div>
So, if condition1 evaluates to false first div will be hidden BUT second div will take the position which the just hidden div took.
How can I avoid that? I only want DOM elements to be invisible but not to get somehow removed.
First workaround.
I tried to overried .ng-hide class and getting a secondary class, only-hide, for elements on which I wanted this effect:
.ng-hide.only-hide {
visibility: hidden !important;
}
But didn't get results so far.
I achieved it with this second class approach by setting:
.ng-hide.only-hide {
visibility: hidden !important;
display: block !important;
}
As Angular sets .ng-hide with display:none, I make it invisible but present setting display:block.
To preserve and maintain the space occuped by the div you can't use directly ng-hide or ng-show.
You can use the ng-style directive as following:
<div ng-style="conditionHide1">First div</div>
<div ng-style="conditionHide2">Second div</div>
then your conditionHide1 and conditionHide2 should be like
if (condition1)
$scope.conditionHide1= {'visibility': 'hidden'}; // then div1 will hidden.
else
$scope.conditionHide1= {'visibility': 'visible'}; // then div1 will visible.
if (condition2)
$scope.conditionHide2= {'visibility': 'hidden'}; // then div2 will hidden.
else
$scope.conditionHide2= {'visibility': 'visible'}; // then div2 will visible.
You can change the visibility of the button by changing the $scope.conditionHide1 and $scope.conditionHide2 according to your conditions.
Solution2 by using a custom directive:
Create a new directive named condition and relative to an Attribute. Set-up a watch to watch the value of the attribute and, based on the value, set to the element (in this case the div) an appropriate css style. The value is mapped to the variable showDiv which change his value by clicking on the button. Clicking on the button, the value showDiv became the opposite !showDiv and the watch change the visibility from visible to hidden and vice-versa.
angular.module('MyModule', [])
.directive('condition', function() {
return {
restrict: 'A',
link: function(scope, element, attributes) {
scope.$watch(attributes.condition, function(value){
element.css('visibility', value ? 'visible' : 'hidden');
});
}
};
})
.controller('MyController', function($scope) {
$scope.showDiv = true;
});
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='MyModule' ng-controller='MyController'>
<div condition='showDiv'>Div visible/invisible</div>
<button ng-click='showDiv = !showDiv'>Hide div or show it</button>
</div>
I am trying to set the height of a div based on its width, and then applying a multiplication factor. I am using angularjs in my code, and I need to use the class to base the directive on.
my html is as follows:
<div ng-class="(box.banner) ? 'bannerbox col-xs-12 col-sm-12 col-md-6 col-lg-6 col-xl-4' : 'cardbox col-xs-12 col-sm-12 col-md-6 col-lg-6 col-xl-4'" ng-repeat="box in boxes">
If the div is a bannerbox (ie has the bannerbox class) then I need the height of this div to be 1.08571 * the width. I understand I need to do this using a directive, but not sure where I am going wrong. My directive code is as follows:
app.directive('bannerbox', function () {
return {
restrict: "C",
link: function (scope, element) {
element.height(element.width * 1.08571);
}
}
});
Any help would be much appreciated!
You need to watch for the width of the element because the width's value is zero initially.
When the link function is called, the width of the element is zero.
Assuming you're using angularJs and jQuery:
app.directive('bannerbox', function () {
return {
restrict: "A",
link: function (scope, element) {
scope.getWidth = function () {
return $(element).width();
};
scope.$watch(scope.getWidth, function (width) {
// Do your work with the element width.
// Be careful not to change the width of the element or you will create an infinite loop.
// Set the height of the element.
});
}
}
});
Working JsFiddle example here : http://jsfiddle.net/ZtQ2p/
I have a directive that compiles another directive and attaches it to the body with the same scope passed. This will not be the same location as the "parent" directive.
When the parent directive gets destroyed is there some way to have the child directive and scope destroy as well? I ask because after inspecting the DOM the child directive is still there.
Currently I hook into the parents $destroy event but was curious if it could be handled automatically.
jsFiddle: http://jsfiddle.net/FPx4G/1/
The child stays there as you toggle the parent, but i'd like to to be destroyed. What would be the best method to do that?
html:
<div ng-app="app">
<div ng-controller="ParentCtrl">
<button data-ng-click="toggleParent()">Toggle Parent</button>
<div data-ng-switch data-on="displayDirective">
<div data-ng-switch-when="true">
<div class="parentDirective">Parent Directive</div>
</div>
</div>
</div>
</div>
javascript:
angular.module('app', [])
.directive("parentDirective", function($compile){
return {
restrict: 'C',
link: function(scope, element){
var secondDirective = angular.element(document.createElement("div"));
secondDirective.addClass("childDirective");
document.body.appendChild(secondDirective[0]);
$compile(secondDirective)(scope);
}
}
})
.directive("childDirective", function(){
return {
restrict: 'C',
template: '<div>Child Directive</div>',
link: function(scope, element){
scope.$on("destroy", function(){
alert(1);
});
}
}
});
function ParentCtrl($scope){
$scope.displayDirective = true;
$scope.toggleParent = function(){
$scope.displayDirective = !$scope.displayDirective;
}
}
Normally, I'd just have the sub element within the original directive's template so that it's positioned correctly. The issue really comes down to dealing with z-index. The parent element is in a container that can be scrolled, so the child (in one case a custom dropdown) would be hidden/cut off if it was larger then the container. To combat this I instead create the actual child in the document body and position it relative to the parent. It would also listen in on scroll events to reposition itself. I have that all working and is just fine. It's what happens when I need to delete the parent... the child is still there.
directive("childDirective", function(){
return {
restrict: 'C',
template: '<div >Child Directive</div>',
link: function(scope, element){
scope.$on("$destroy",function() {
element.remove();
});
}
}
});
updated fiddle : http://jsfiddle.net/C8hs6/