How to get the circumscribed area of all DOM elements? - javascript

I have a lot of elements on the page. I need to draw a circumscribed rectangle that contains all DOM elements.
For that I iterate DOM elements and get rectangle:
Array.from(firstChild.children).forEach((child: Element) => {
const rect = child.getBoundingClientRect();
});
Which props I need from rect to do that?
Logically I need to get minimal x,y of left-top corner and max bottom-right corner. But how?

To apply a border you need to set 4 values: top, left, width and height.
Getting top and left can be easily done by finding the topmost and leftmost elements. To get the other values you should do some calculation:
width = rightmost - leftmost and height = bottommost - topmost
To get rightmost and bottommost you have to loop through all elements and get their rightmost/bottommost points with the same formula, but you need to rearrange them. Here you should keep the biggest values.
//select all elements that should be inside the rectangle
const myElements = document.querySelectorAll("div#container *")
//set initial values, I made sure they are always smaller/bigger than they will be
let Top=Infinity, Left=Infinity, Bottom=-Infinity, Right=-Infinity;
for(const i of myElements){
//loop through the elements
const data = i.getBoundingClientRect()
Top = Math.min(Top, data.top)
Bottom = Math.max(Bottom, data.top+data.height)
Left = Math.min(Left, data.left)
Right = Math.max(Right, data.left+data.width)
}
console.log(Top, Left, Bottom, Right) // print out the coordinates
//set the border
//I subtract 1px bacuse of the border width
const myBorder = document.querySelector("#border")
myBorder.style.top=Top-1+"px"
myBorder.style.left=Left-1+"px"
myBorder.style.width=Right-Left+"px"
myBorder.style.height=Bottom-Top+"px"
#t1{
position: absolute;
top: 20px;
left: 40px;
width: 60px;
height: 40px;
background-color: #000;
}
#t2{
position: absolute;
top: 30px;
left: 80px;
width: 40px;
height: 70px;
background-color: #111;
}
#t3{
position: absolute;
top: 50px;
left: -40px;
width: 60px;
height: 40px;
background-color: #800;
}
#t4{
position: absolute;
top: 35px;
left: 110px;
width: 30px;
height: 40px;
background-color: #444;
}
#border{
border: solid red 1px;
position: absolute;
}
<div id="container">
<div id="t1"></div>
<div id="t2">
<div id="t3"></div>
</div>
<div id="t4"></div>
</div>
<div id="border"></div>

Related

How to Swap Two Divs With Animation

I have a project where I want a div to appear as a large box and three more to appear underneath as smaller boxes and when you click a smaller box, it switches sizes and places with the large box using css transitions to make the movement and size change smooth. Right now I'm attempting to use jQuery and the positioning is not working at all. Here's an example of what I have so far:
https://jsfiddle.net/v3pmhawj/1/
$(function () {
let { left: x1, top: y1 } = $('.full-size-card').offset()
$('.inactive-sheets .card').on('click', function() {
let { left: x2, top: y2 } = $(this).offset()
let curr = $('.full-size-card')
let diffX = x2 - x1
let diffY = y2 - y1
$(this).css({
left: -diffX,
top: -diffY
})
$(this).addClass('full-size-card')
curr.css({
left: diffX,
top: diffY
})
curr.removeClass('full-size-card')
})
})
If anyone has suggestions on ways that involve other libraries or other techniques, I'm all ears. I'd like to be able to move the divs around in the DOM as well but as far as I can tell, you can't css-transition them if you do that since the only way (I know of) is to delete and re-add a copy of the element where you want it in the DOM.
You can create animation effect using transitions only. To achieve this you will have to define width and height of your containers as well as top and left position of bottom elements.
On click, you just have to exchange classes of element that will become small and of element that will become large.
Here is fiddle of an example:
https://jsfiddle.net/fkd3ybwx/210/
HTML
<div class="card-container">
<div class="card large">A</div>
<div class="card small">B</div>
<div class="card small">C</div>
<div class="card small">D</div>
</div>
CSS
.card-container {
position: relative;
}
.card {
transition: all ease 1s;
position: absolute;
font-size: 24px;
border: white 4px solid;
box-sizing: border-box;
cursor: pointer;
}
.small {
width: 100px;
height: 100px;
background: blue;
left: 0;
top: 300px;
}
.small ~ .small {
left: 100px;
background: green;
}
.small ~ .small ~ .small {
left: 200px;
background: yellow;
}
.large {
width: 300px;
height: 300px;
background: red;
left: 0px;
top: 0px;
}
JavaScript
const smallCards = document.querySelectorAll('.card');
smallCards.forEach((smallCard) => {
smallCard.addEventListener('click', (event) => {
const largeCard = document.querySelector('.large');
largeCard.className = "card small";
event.target.className = "card large";
});
});

showing a map marker at the exact location in css

So I have a database with x and y quadrants and I have a 350x350px map. I have positioned the map as such:
background-image: url(/storage/maps/surface.png);
background-repeat: no-repeat;
height: 350px;
background-position: center center;
margin: 0px auto;
background-size: cover;
The width seems to be 429px, not sure why. Im sure it has to do with the cover.
On top of this image I have a marker:
font-size: 32px;
color: #f9e4b4;
z-index: 5;
position: absolute;
top: 78px;
left: 349px;
The top represents the Y position and the X position represents the left.
These values (top and left) come from the database and are set in React JS.
This, as it stands creates three divs:
<div class="location-map mb-3">
<div style="background-image: url("/storage/maps/surface.png"); background-repeat: no-repeat; height: 350px; background-position: center center; margin: 0px auto; background-size: cover;">
<i class="fas fa-map-marker-alt player-icon" style="top: 78px; left: 349px;"></i>
</div>
</div>
The issue I am having is:
As you an see, I am trying to position this marker at a pixel perfect position on the map.
now as you see the location states 349, 78 and while this might be right css wise, the marker should be at the edge of the map (on the right) if it was truly 349px's to the left.
So my question is, is the image too small? did I position the image properly? Why is the marker where it is and not where I want it to be?
There are two main items to solve:
The size of the map element should be an exact 350px square
Using top and left should place the base of the pin at the exact coordinates.
Let's start with the map. If you know the exact image dimensions of your image this should suffice:
.map {
background: url(http://placehold.it/350x350);
height: 350px;
width: 350px;
}
<div class="map">
</div>
Now lets create the element we want to position using top and left to the exact pixel, allowing any decoration to fall where it may by using a pseudo element:
.map {
background: url(http://placehold.it/350x350);
height: 350px;
position: relative;
width: 350px;
}
.map-x-pin {
background-color: black; /* So we can see where the pixel is */
height: 1px;
position: absolute;
width: 1px;
}
.map-x-pin::after {
background-color: red; /* Decorative, this could be an image too */
bottom: 100%;
content: '';
display: block;
left: -10px; /* Half of the width, give or take a pixel */
position: absolute;
height: 40px;
width: 20px;
}
<div class="map">
<div class="map-x-pin" style="top: 78px; left: 349px;"></div>
</div>

Calculate div width/height when inside a zoom/scale transform

I have a div inside another div with transform scale applied.
I need to get the width of this div after the scale has been applied. The result of .width() is the original width of the element.
Please see this codepen:
https://codepen.io/anon/pen/ZMpBMP
Image of problem:
Hope this is clear enough, thank you. Code below:
HTML
<div class="outer">
<div class="inner">
</div>
</div>
CSS
.outer {
height: 250px;
width: 250px;
background-color: red;
position: relative;
overflow: hidden;
}
.inner {
background-color: green;
height: 10px;
width: 10px;
transform: translate(-50%);
position: absolute;
top: 50%;
left: 50%;
transform: scale(13.0);
}
JS
$(function() {
var width = $('.inner').width();
// I expect 130px but it returns 10px
//
// I.e. It ignores the zoom/scale
// when considering the width
console.log( width );
});
Use getBoundingClientRect()
$(function() {
var width = $('.inner')[0].getBoundingClientRect();
// I expect 130px but it returns 10px
//
// I.e. It ignores the zoom/scale
// when considering the width
console.log(width.width);
});
https://jsfiddle.net/3aezfvup/3/
i achieved your 130 by this
var x = document. getElementsByClassName('inner');
var v = x.getBoundingClientRect();
width = v.width;
You need the calculated value. This can be done in CSS.
Use calc() to calculate the width of a <div> element which could be any elements:
#div1 {
position: absolute;
left: 50px;
width: calc(100% - 100px);
border: 1px solid black;
background-color: yellow;
padding: 5px;
text-align: center;
}
I found this about this topic.
Can i use Calc inside Transform:Scale function in CSS?
For JS:
How do I retrieve an HTML element's actual width and height?

div with a large z-index is under the div with a smaller z-index

I had a problem:
Element overlapped element because it has a higher z-index (10 vs 5). Element has the child elements #sub-block-1 & #sub-block-2, and #sub-block-1 had child text #text1 with z-index = 20. But #text1 with z-index = 20 still under with z-index = 10. Why?
Tell me, how can I solve this problem?
The must have a predefined z-index (number, not 'auto', and less than z-index , for example, 5 vs 10).
My example:
https://jsfiddle.net/nynsjv3L/1/
P.S.
The above example is not displayed correctly in jsfiddle - the position of the elements (#sub-block-1 & #sub-block-2) does not work.
body {
background: #00ff00;
}
.screen {
position: absolute;
left: 0px;
top: 0px;
width: 100vw;
height: 100vh;
background: #ffffff;
opacity: 0.75;
z-index: 10;
}
#main-block {
position: absolute;
left: 500px;
top: 200px;
width: 700px;
height: 400px;
border: 2px solid #bb0000;
background: #ff0000;
z-index: 5;
}
.sub-block {
position: absolute;
width: 300px;
height: 150px;
background: #ffffff;
border: 4px solid #000000;
}
#sub-block-1 {
left: 100;
top: 100;
z-index: auto;
}
#sub-block-2 {
left: 275;
top: 175;
z-index: 11;
}
#text1 {
position: absolute;
left: 65px;
top: 55px;
color: #000000;
background: #ffff00;
font-size: 30px;
font-weight: 700;
z-index: 20;
}
#text2 {
position: absolute;
left: 65px;
top: 55px;
color: #00bbbb;
font-size: 30px;
font-weight: 700;
}
<div id='main-block'>
<div class='sub-block' id='sub-block-1'>
<div id='text1'>TEST TEXT</div>
</div>
<div class='sub-block' id='sub-block-2'>
<div id='text2'>NEW TEXT</div>
</div>
</div>
<div class='screen'></div>
The #text is still inside your block, that has a lower index than its sibling. The #text part is still in the same context block of its parent. You could check out the inner workings of z-indexes in this page.
You need to create a new stacking context for the text to appear above the other block. One of the possible ways is to change the opacity of the text to .99, or using the transform attribute. I do not recommend you to do it, though, as it will cause more troubles in the future.
Z-indexes should not be used very often. It is better to organize this inside the HTML itself. You should rethink your work.
#main-block is on the same level as .screen and has a lower z-index. So child elements of #main-block will always be overlapped by .screen and its children.
Whenever you set an element to be position: relative or position: absolute you open up a new z-index-stack for all of its children.
You either have to move the child elements of #main-block some levels up to make them independent or give the #main-block a higher z-index which will also result in overlapping .screen.
Hint: Negative z-index is also possible. And clean up your markup (keyword: single quotes). ;-)

How to prevent get over other divs?

I have a problem...In the following example i don't want that the div who is fixed get over the div with the background red.
Here is the example:
http://jsfiddle.net/HFjU6/3645/
#fixedContainer
{
background-color:#ddd;
position: fixed;
width: 200px;
height: 100px;
left: 50%;
top: 0%;
margin-left: -100px; /*half the width*/
}
Alright, I think I get what the OP wants. He wanted a container that stays fixed on the top of the viewport, but remains confined by a parent. This behaviour is known as a conditional sticky behaviour, and is actually implemented in both Firefox (without vendor prefix) and macOS/iOS Safari (with -webkit- prefix): see position: sticky.
Therefore the easiest (but also the least cross-browser compatible) way is simply to modify your markup, such that the sticky element stays within a parent, and you declare position: sticky on it:
* {
margin: 0;
padding: 0;
}
#fixedContainer {
background-color: #ddd;
position: -webkit-sticky;
position: sticky;
width: 200px;
height: 100px;
left: 50%;
top: 0%;
transform: translate(-50%, 0); /* Negative left margins do not work with sticky */
}
#div1 {
height: 200px;
background-color: #bbb;
}
#div1 .content {
position: relative;
top: -100px; /* Top offset must be manually calculated */
}
#div2 {
height: 500px;
background-color: red;
}
<div id="div1">
<div id="fixedContainer">I am a sticky container that stays within the sticky parent</div>
<div class="content">Sticky parent</div></div>
<div id="div2">Just another element</div>
An alternative would be to use a JS-based solution. In this case, you do not actually have to modify your markup. I have changed the IDs for easier identification of the elements, however.
The gist of the logic is this:
When the scroll position does not exceed the bottom of the parent minus the outer height of the sticky content, then we do not do anything.
When the scroll position exceeds the bottom of the parent minus the outer height of the sticky content, we dynamically calculate the top position of the sticky content so that it remains visually in the parent.
$(function() {
$(window).scroll(function() {
var $c = $('#sticky-container'),
$s = $('#sticky-content'),
$t = $(this); // Short reference to window object
if ($t.scrollTop() > $c.outerHeight() - $s.outerHeight()) {
$s.css('top', $c.offset().top + $c.outerHeight() - $t.scrollTop() - $s.outerHeight());
} else {
$s.css('top', 0);
}
});
});
* {
margin: 0;
padding: 0;
}
div {
height: 500px;
background-color: red;
}
#sticky-container {
background-color: #bbb;
height: 200px;
}
#sticky-content {
background-color: #ddd;
position: fixed;
width: 200px;
height: 100px;
margin-left: -100px;
left: 50%;
top: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="sticky-content">Sticky content that stays within the bounds of #div1</div>
<div id="sticky-container">Sticky confinement area</div>
<div>Other content</div>
Old answer before OP clarified the question appropriately:
Just give them the appropriate z-index values. In this case, you want to:
Do not use static positioning. This can be done by using position: relative for the large elements, in conjunction with the originally position: fixed element.
Assign the appropriate stacking order. The grey <div> element to have the lowest z-index, followed by the position fixed element, and then by the red element.
There are some catchalls to stacking though: the stacking context is reset when you traverse up or down the node tree. For example, the example will not work if the elements are not siblings.
Here is a proof-of-concept example, modified from your fiddle so that inline CSS is removed.
* {
margin: 0;
padding: 0;
}
#fixedContainer {
background-color: #ddd;
position: fixed;
width: 200px;
height: 100px;
left: 50%;
top: 0%;
margin-left: -100px;
z-index: 2;
}
#div1 {
height: 200px;
background-color: #bbb;
position: relative;
z-index: 1;
}
#div2 {
height: 500px;
background-color: red;
position: relative;
z-index: 3;
}
<div id="fixedContainer">z-index: 2</div>
<div id="div1">z-index: 1</div>
<div id="div2">z-index: 3</div>
Just give the z-index.
Hope it helps...
http://jsfiddle.net/HFjU6/1/#run
#fixedContainer {
background-color:#ddd;
position: fixed;
width: 200px;
height: 100px;
left: 50%;
top: 0%;
margin-left: -100px; /*half the width*/
z-index: 2;
}
.div-red {
position: relative;
z-index: 5;
}
<div id="fixedContainer"></div>
<div style="height:200px;background-color:#bbb;"></div>
<div style="height:500px;background-color:red;" class="div-red"></div>

Categories

Resources