I have the code below to change the height of a div to equal that of its parent.
$('#infodiv').css("height",$("#infocol").outerHeight());
The problem is that the height of the child element #infocol, is no longer dynamic if i load new content inside of it. Is there a way to make the child element dynamic again after i have set the height with the above code?
I have tried to reconfigure its height after the content is loaded with the same code, but so far that hasn't worked.
There is a way you can solve this issue using ResizeObserver
However, note that it's not supported in some browsers, check the page I've linked for further details.
Here is a working example:
$(function () {
$("#add-content").click(function () {
$(".first-col").append("<p>More dynamic content...</p>");
});
// keep the second-col same size as first
$(".second-col").css('height', $(".first-col").outerHeight());
const resizeObserver = new ResizeObserver(function (entries) {
for (let entry of entries) {
// once one of the entry changes we want to update the other size too!
$(".second-col").css('height', $(entry.target).outerHeight());
}
});
// We need to pass the actual DOM node, hence the [0]
resizeObserver.observe($(".first-col")[0]);
});
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.container {
width: 23.5rem;
margin: 1rem auto;
}
.first-col {
background: cornflowerblue;
}
.second-col {
background: crimson;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div class="first-col">
<p>Some content</p>
<p>Some content</p>
<button id="add-content">Add content</button>
</div>
<div class="second-col"></div>
</div>
Though, I suggest before implementing it like that, that you look into how flex works or simply even min-height might be the proper tool for the issue here. If you're out of options, feel free to use ResizeObserver, but it's considered an exotic solution!
Related
I'm pretty sure this is currently infeasable.
I have an animation that involves an element moving from an absolute position to an inline one. For reasons, I can not know how the container is sized, nor how the element I'm animating is sized.
What I need to know is what the size of the HTML Element will be after the transformation, without any jittery drawing.
This makes the problem very difficult (likely undoable) because I have no way to know if adding the element will resize the parent, or resize the element itself.
What I need is a means of looking into the future.
const byId = (id) => document.getElementById(id);
#container {
height: 3em;
min-width: 50%;
background: teal;
}
#mystery {
background: purple;
}
<div id="container">
<div id="mystery">Some Text</div>
</div>
<button onClick='byId("mystery").style.position = "relative"'>Position Relative</button>
<button onClick='byId("mystery").style.position = "absolute"'>Position Absolute</button>
Currently, these are the only solutions I can imagine (they're all absurd):
Clone the entire webpage HTML, make the clone have opacity: 0; pointer-events: none and render what the future will be secretly.
Capture the paint data of the current page (basically screenshot), overlay that while secretly modifying the page, get my future, revert, and remove the screenshot overlay.
Similar to number 2, is there a way to ❄️freeze❄️ rendering of a page for 3-4 frames?
I remember seeing a "sizing worker" something-or-rather a long time ago. Couldn't find any information on it now, but it seems like it might be useful?
You can simply change the property, measure the sizes you want and then change the property back. JS is fast enough to do it all between renderings, as long as you keep it all in the same thread. Have you tried that at all?
Asker Edit:
Here's the code to prove it works.
function byId(id){ return document.getElementById(id); }
const tweenyEl = byId("tweeny");
function appendTweeny() {
tweenyEl.style.opacity = "1";
const startingWidth = tweenyEl.clientWidth + "px"
tweenyEl.style.position = "relative";
const targetWidth = tweenyEl.clientWidth + "px";
console.log(startingWidth, targetWidth);
tweenyEl.style.width = startingWidth;
requestAnimationFrame(() =>
requestAnimationFrame(() =>
tweenyEl.style.width = targetWidth
)
);
}
function resetTweeny() {
tweenyEl.style.position = "";
tweenyEl.style.width = "";
tweenyEl.style.opacity = "0.1";
}
#container {
display: inline-block;
height: 3em;
min-width: 150px;
background: teal;
}
#tweeny {
font-family: arial;
color: white;
position: absolute;
background: purple;
transition: all 0.5s ease;
opacity: 0.1;
}
<div id="container">
<div id="tweeny">I'm Tweeny</div>
</div>
<br>
<button onClick='appendTweeny()'>Append Tweeny</button>
<button onClick='resetTweeny()'>Reset Tweeny</button>
I would suggest cloning the page into an iframe and then positioning the iframe off the screen.
<iframe style="width:100vw;height:100vh;left:-101vw;positionabsolute"><iframe>
Also bear in mind that the user can zoom in-and-out at will! Different browsers might render the same thing in different ways. You really don't know how big an element will be until it does so.
I don't know if you can get anywhere by specifying display: none; ... whether or not the browser would bother to make these calculations for an object that isn't visible.
You can clone on the fly an element with same transformation with delay 0 and then calculate it's width and height, then do what you want with your actual element it's still animating
I have an html file(a webpage). I want when I press a button on it, the page should be replaced by another html file (with its own css, javascript functions etc) without being redirected to some other link.
For example, if link in first case is abc.com/def it should be same after too.
Using this code, I am able to change webpage look, but not getting how to change look (and also manage to load css and js functions) from another file.
<script type="text/javascript">
document.body.addEventListener('click',function(){
document.write("THIS IS NEW TEXT")
},
false);
</script>
You need to look into frameworks like AngularJS, Specially Routing of Angular. They provide such features built-in for web applications. However, you can do it the hard way, using javascript, like you are doing it right now. Add CSS and change whole body HTML using javascript if you don't want to learn any new framework or libraries.
You want to use PJAX,
Here's a link for an example.
As discuss by others, you should use a Framework to do this..
But this is a complete solution you can inspire of:
let layouts = {}
let current = null
// Display the new page by deleting current, and replacing by the good one
let displayLayout = (layout_id) => {
let parentNode = current.parentNode
parentNode.removeChild(current)
current = layouts[layout_id]
parentNode.appendChild(current)
loadEvents(current)
}
// Load event for HTML DOM you just created
let loadEvents = (layout_el) => {
Array.from(layout_el.getElementsByClassName('go-to-layout')).forEach(el => {
el.addEventListener('click', e => {
e.preventDefault()
displayLayout(e.currentTarget.dataset.layout)
})
})
}
// On init I get all the existing layout, but you can build you own dictionary an other way.
Array.from(document.getElementsByClassName('layout')).forEach(l => {
layouts[l.id] = l
if (l.classList.contains('active')) {
loadEvents(l)
current = l
}
else {
l.parentNode.removeChild(l);
}
})
/* Global CSS */
body, html, .layout {
height: 100%;
margin: 0;
}
* {
color: #FFF
}
.layout {
display: flex;
}
.nav, .page {
}
.nav {
width: 150px;
background: #555;
}
/* Special CSS for one layout */
#layout1 {
background: red;
}
#layout2 {
background: blue;
}
<div id="layout1" class="layout active">
<div class="nav">
Page 2
</div>
<div class="page">
This is page 1
</div>
</div>
<div id="layout2" class="layout">
<div class="nav">
Page 1
</div>
<div class="page">
This is page 2
</div>
<style>.page { font-size: 2em }</style>
</div>
I wanted to scroll to the bottom of the page using vanilla JS, but I encountered a problem. The code below is supposed to scroll to the bottom of the page:
window.scrollTo(0,document.body.scrollHeight);
Whereas all it does is logs "undefined" in the console. Inputting
document.body.scrollHeight
returns an integer 736. Other than that, it doesn't matter what I input into the function's parameters, nothing happens. What more, it only happens on one website. What may matter (not sure) is that the website hides its vertical scrolling bar, even thought it has a really long list of content.
The problem might be that the actuall scroll that you have on the website is not the scroll of the body but a scroll of another element inside that body.
Here is an example:
$('#btn1').click(function() {
window.scrollTo(0,document.body.scrollHeight);
});
$('#btn2').click(function() {
el = $('.a')[0];
el.scrollTop = el.scrollHeight;
});
body {
overflow: hidden;
margin: 0;
padding: 0;
}
div.a {
height: 100vh;
overflow: auto;
}
div.b {
height: 1500px;
position: relative;
}
div.c {
position: absolute;
bottom: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="a">
<div class="b">
<button id="btn1">Scroll body - doesn't work</button><br />
<button id="btn2">Scroll element - will work</button>
<div class="c">This is at bottom of page</div>
</div>
</div>
Note - the usage of jquery is only to make the example shorter.
Put some content in your page o style the body heigth = 1500px for example, then try to execute same code.
Solved. This is what had to be done:
document.getElementsByTagName('body')[0].style.display = "block";
For whatever reason, changing the display to "block" enabled the scrolling using the given code:
window.scrollTo(0,document.body.scrollHeight);
If you will try to type in browser's console like a var a = 5 you also will get undefined. It happens that your example and my did not return anything.
I'm looking for simple way to detect, if child element of parent with overflow:hidden is visible within parent (it's not hidden by overflow).
I found something like this:
http://www.useallfive.com/thoughts/javascript-tool-detect-if-a-dom-element-is-truly-visible/
but i wonder maybe there is simpler solution.
Thanks in advance!
Assuming you want a vanilla js solution, try this:
function isVisible (parent, child) {
return !(
(child.offsetLeft - parent.offsetLeft > parent.offsetWidth) ||
(child.offsetTop - parent.offsetTop > parent.offsetHeight)
)
}
Basically "if the difference between the start of the parent element and the start of the child element is greater than the actual width or height of the parent, it's considered not visible"
Run the following snippet for an example:
var parent = document.getElementById('parent');
[].slice.call(document.querySelectorAll('.child')).forEach(function (child, i) {
console.log(i + ' is visible?', isVisible(parent, child));
});
function isVisible(parent, child) {
return !(
(child.offsetLeft - parent.offsetLeft > parent.offsetWidth) ||
(child.offsetTop - parent.offsetLeft > parent.offsetHeight)
)
}
* {
box-sizing: border-box;
}
#parent {
width: 200px;
height: 100px;
white-space: nowrap;
background: lightblue;
}
.child {
display: inline-block;
width: 75px;
height: 100%;
border: 1px solid green;
}
<div id="parent">
<div class="child">0</div>
<div class="child">1</div>
<div class="child">2</div>
<div class="child">3</div>
<div class="child">4</div>
</div>
You can do the following things:
check if the element has height and width that are bigger than 0px
check if the element display CSS property is not "none", preferably "block"
check if the element positioning is valid and if so, check if it´s "top" property is not bigger than the parent´s height
Same thing for left - if it is not bigger than parent´s width
When checking "width", check offsetWidth and clientWidth, those will show actual numbers as displayed to the client.
I had a similar requirement, but mine was a bit more complicated because the overflow: hidden element wasn't the first parent, it was like 5 or 6 elements away.
Just spend a whole day trying to do it with solutions from the internet(I've tried the repo you mentioned as well), but nothing worked.
So I've made this repo by myself (only JS, 2kb sized) https://github.com/LuizAsFight/is-element-visible.
It might help you, basically I just get the target element and climb the tree searching if any parent has a overflow:hidden, once I found it I get the parent's rect size, and check if the target element rect is inside the parent (visually, pixels)
for using it you just need to
import isVisible from 'is-element-visible';
const el = document.getElementById('id');
isVisible(el);
I hope it helps you, Best.
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>