I am using the following code (Javascript within a webpage) to create a 'new' element in the DOM dynamically. I wish to position this say 200px 'below' an existing element. However my output has the positioning of the new element(s) all wrong...as if the position (top, left) I am specifying is ignored.
var _reference = document.getElementById("outputs");
for (_count = 0; _count < _limits; _count++) {
var _structure = document.createElement("div");
_structure.setAttribute("class", "container-fluid");
_structure.setAttribute("id", "struct_" + _tally);
if (_count === 0){
_rect = _reference.getBoundingClientRect();
//get the bounding box of the "outputs" id element...
document.getElementById("outputs").appendChild(_structure);
_structure.style.top = _rect.top + "200px"; //NOT positioned 200px below 'outputs'
_structure.style.left = _rect.left; //NOT positioned same position as 'outputs'
} //_count is "0"
} //for loop
I would have thought this should be fairly straightforward...however it is driving me crazy...any help appreciated.
You'll need to set _structure.style.position to 'relative', 'absolute', 'fixed', or 'sticky' in order to use top, left, right, bottom.
You need to set your position to realtive or absolute in order for this to work, also note that position: absolute sets the position according to the nearest relative positioned parent while position: relative positions according to the current position of the element
Related
I created Parallax script for some elements (rect & circles) so when I scroll from top to bottom, elements should move to the top
Elements starting position is added in HTML directly using < style >
The problem is when I scroll back elements move down, but they are not in the same starting position when I reach the top of the page.
HTML:
<img style="top: 62%; left: 46.3%;" class="l-parallax_item" src="./path/to/img">
<img style="top: 74%; left: 42.7%;" class="l-parallax_item" src="./path/to/img">
CSS:
Parent has position relative, l-parallax_item is position absolute
JS:
var parallaxElements = document.getElementsByClassName("l-parallax_item");
var lastScrollTop = 0;
window.onscroll = function() {
var st = window.pageYOffset || document.documentElement.scrollTop;
if (st > lastScrollTop){
console.log("bottom");
for(i = 0; i < parallaxElements.length; i++) {
var position = parallaxElements[i].offsetTop;
var movePx = parallaxElements[i].getAttribute("data-px-per-scroll");
parallaxElements[i].style.top = (position - parseInt(movePx))+"px";
}
} else {
console.log("top");
for(i = 0; i < parallaxElements.length; i++) {
var position = parallaxElements[i].offsetTop;
var movePx = parallaxElements[i].getAttribute("data-px-per-scroll");
parallaxElements[i].style.top = (position + parseInt(movePx))+"px";
}
}
lastScrollTop = st <= 0 ? 0 : st; // For Mobile or negative scrolling
}
I noticed when I scroll from top to bottom I get more console.log("bottom") messages. * When I scroll top > bottom and vise versa.
So I guess that is the reason why the element is not in the same position too when I go bottom > top.
http://prntscr.com/ka9osq
How can I fix this?
EDIT: http://jsfiddle.net/r9751p8q/6/
Try to scroll to the bottom and then back you will see that some element disappear
Here is a possible solution of your problem.
HTML
<div class="section">
<div style="top: 100px; left: 46.3%;" data-px-per-scroll="0.5" data-initial-position="100" class="l-parallax_item"></div>
<div style="top: 300px; left: 37.7%;" data-px-per-scroll="0.9" data-initial-position="300" class="l-parallax_item"></div>
<div style="top: 80px; left: 56%;" data-px-per-scroll="0.3" data-initial-position="80" class="l-parallax_item"></div>
<div style="top: 230px; left: 75%;" data-px-per-scroll="0.8" data-initial-position="230" class="l-parallax_item"></div>
<div style="top: 60px;left: 7.1%;" data-px-per-scroll="0.5" data-initial-position="60" class="l-parallax_item"></div>
</div>
JS
function parallaxBlocks(){
let parallaxElements = document.getElementsByClassName("l-parallax_item");
let st = window.pageYOffset || document.documentElement.scrollTop;
let elementsLength = parallaxElements.length;
for(i = 0; i < elementsLength; i++) {
let movePx = 1 + parseFloat(parallaxElements[i].dataset["pxPerScroll"]);
let position = 1 + parseInt(parallaxElements[i].dataset["initialPosition"]);
parallaxElements[i].style.top = Math.floor(position - (st * movePx)) + 'px';
}
}
window.addEventListener('scroll', parallaxBlocks)
But I also changed your html. For working example check this fiddle .
And some explanation what I'm actually doing in the JS code:
In this code we don't need to check if we scroll up or down because the logic of the positioning of the elements is shifted form what was their current position (as in your code) to what was their initial position.
I'm using let to declare variables instead of var for scope reasons (and because I love new things). Still if you are going to support older browsers you might want to check caniuse for compatibilities.
For the top css property I'm using fixed values, and I store their original top offset in a new data attribute called initial-position. This initial position will be used later to calculate the new position of each element. If you need a % values then you can keep the top property with % value, but you will need also another loop to go through all the .l-parallax_item and check their offset from the top and record this value in their data-initial-position.
Note that I'm using dataset instead getAttribute. dataset is made for all data attributes. And see how the cebab-case became camelCase. More to read here
Also the px-per-scroll is no longer a fixed amount of pixels instead it is a ratio from the scroll offset from top. You can play with the fiddle to see how it works.
Bonus: why I added another variable for the elements length instead just using it in the for loop arguments.
I believe that there is another way to do this but hope that this one will help you.
The reason it's not working:
Scrolling is not precise and because you base your position on the total sum of scrolls the positioning will end up unpredictable. To show you what I mean:
Your console.log() fires every time you scroll. If you change both logs to console.log(parallaxElements[0].style.top) it will show you the top position of the first parallaxed element each time you make a scroll move. Now take your mouse and scroll one "tick" down, then one "tick" up and repeat many times. The position numbers will not be the same every time, and therein lies the problem.
Solution:
Base your parallax elements position on the actual pageYOffset. Since Ale already posted a working solution with this in mind one more code example is reduntant.
Is there a way to check positioning of two elements?
For example:
.bigBox {
width: 100%;
height: 200px;
}
.btn {
position: fixed
display: none;
top: 10px;
right: 0;
}
There is a big box over the whole website. And i have a button with positioning fixed and display none. The button should fadeIn() if it is 100px under the .bigBox.
First thing, the the position of .bigBox's dimensions:
var bottomBigBox = $(".bigBox").offset().top + $(".bigBox").height();
var topOfBtn = $(".btn").offset().top;
// Check the condition and fade it.
if (bottomBigBox + 100 == topOfBtn)
$(".btn").fadeIn();
You can use position() or offset() methods to know the position of the element relative to the parent or relative to the document respectively.
$('.element').position().top; // returns the top value relative to parent
$('.element').position().left; // returns the left value relative to parent
$('.element').offset().top; // returns the top value relative to document
$('.element').offset().left; // returns the left value relative to document
See more:
http://api.jquery.com/offset/
https://api.jquery.com/position/
You can get an element position with offset(). Then you can sum its computed .height() and the desired margin by 100:
var bb = $(".bigBox");
var o = bb.offset();
var h = bb.height();
$(".btn").css("top", o.top + h + 100).fadeIn();
Working demo
Why use height() ? It will get the element computed height, so if you change on CSS or even if you use an relative value, it will work.
With plain Javascript
var bb = document.querySelector(".bigBox");
var t = bb.offsetTop;
var h = bb.offsetHeight;
document.querySelector(".btn").style.top = t + h + 100 + "px";
$(".btn").fadeIn(); // jQuery only for fadeIn effect
Working demo
I have a function that I built (it's kind of bare bones at the moment), but I can't find how to move the object when it expands onMouseOver so it doesn't move the others on the page, is this possible?
function expandbox(x) {
x.style.height = "100px";
x.style.width = "100px";
}
function returnbox(x) {
x.style.height = "80px";
x.style.width = "80px";
}
Make the element position relative inside a relative container, then change it's dimensions.
Make sure to give it higher z-index.
Place it with left,right,top,bottom properties of style.
If you want to keep the element in document flow, set it to position: relative and move it around using top and left, which will be relative to where the element started out.
I'll use the same type of function you made, to demonstrate:
function moveBox(x) {
x.style.position = "relative";
// move 20px down and 10px left from original position
x.style.top = "20px";
x.style.left = "-10px";
}
If you want to remove the box from document flow (following elements will not be affected by its presence) set its position to absolute instead. Top and left values will be relative to its closest positioned ancestor (or <body> if there is none, as i assume in the comment below)
function moveBox(x) {
x.style.position = "absolute";
// position box 20px from top of body
x.style.top = "20px";
// and 10px from the left
x.style.left = "10px";
}
An element is considered to be "positioned" if it has a value other than static (default, read more here), so if you want to control what the absolutely positioned element is relative to, you can give a container element position: relative
I have a div element of css width and height, 800x600. I am using javascript to generate three object elements in the div that should be in a diagonal line, touching each other. I am using position:relative, and the left and top css properties to position the object elements. However when I do it this way, there is a horizontal gap between the squares that shouldn't be there. When I use positon:fixed, they line up how I want it but not inside the div element.
Html of my div element
<div id="Stage" style="background:black;width:800px;height:600px;margin-left:auto;margin-right:auto;overflow:hidden;">
and my javascript
w="w";
level_data =
[
[w,0,0],
[0,w,0],
[0,0,w],
];
function generate(level_data){
for(row=0;row<level_data.length;row++){
for(col=0;col<level_data[row].length;col++){
posx = col*50; posy=row*50;
if(level_data[row][col]=="w"){
entity = document.createElement('object');
entity.style.position = "relative";
entity.style.left = String(posx)+"px"; entity.style.top = String(posy)+"px";
entity.data = "Objects/Sprites/Wall.jpg";
document.getElementById("Stage").appendChild(entity);
}
}
}
}
generate(level_data);
This is what I get: Link1
This is what I want: Link2 but the redsquares inside the big black square instead. What's the problem?
position: fixed positions elements relative to the viewport. position: relative gives that result because object element probably has some default value for widht and height. You'll need something like this:
entity.style.position = "absolute";
entity.style.left = String(posx)+"px";
entity.style.top = String(posy)+"px";
entity.style.width = "50px";
entity.style.height = "50px";
When using position: absolute, the code is supposed to work even without dimensions for the entity. Notice , that when using position: relative you should not multiply position values with col, they should be just 50px.
I have something vaguely like the following:
<div id="body">
surrounding text
<div id="pane" style="overflow: auto; height: 500px; width: 500px;">
lots and lots of text here
<span id="some_bit">tooltip appears below-right of here</span>
</div>
more surrounding text (should be overlapped by tooltip)
</div>
and:
<div id="tooltip" style="width: 100px; height: 100px;">Whee</div>
What I want to do is insert the tooltip such that it is positioned above the pane it's in. If it's attached to an element that's next to the pane boundary (like above), then it should be visible above the pane, and above the text surrounding the pane.
It should NOT a) extend the pane, such that you have to scroll down to see the tooltip (like in http://saizai.com/css_overlap.png), or b) be cut off, so you can't see all of the tooltip.
I'm inserting this with JS, so I can add a wrapper position:relative div or the like if needed, calculate offsets and make it position:absolute, etc. I would prefer to not assume anything about the pane's position property - the tooltip should be insertable with minimal assumptions of possible page layout. (This is just one example case.)
It's for a prototype tooltip library I'm writing that will be open source.
ETA: http://jsfiddle.net/vCb2y/5/ behaves visually like I want (if you keep re-hovering the trigger text), but would require me to update the position of the tooltip on all DOM changes and scrolling behavior. I would rather the tooltip be positioned with pure CSS/HTML so that it has the same visual behavior (i.e. it overlaps all other elements) but stays in its position relative to the target under DOM changes, scrolling, etc.
ETA 2: http://tjkdesign.com/articles/z-index/teach_yourself_how_elements_stack.asp (keep defaults except set cyan div 'a' to position:relative; imagine 'A' is the pane and 'a' the tooltip) seems to more closely behave as I want, but I've not been able to get it to work elsewhere. Note that if you make 'A' overflow: auto, it breaks the overlapping behavior of 'a'.
I can't think of a pure HTML/CSS solution for this.
The overflow declaration is the issue here. If the tooltip is in #pane:
you establish a positioning context within #pane, then the tooltip shows next to #some_bit (regardless of scrolling, etc.) but it gets cut-off.
you do not establish a positioning context, then the tooltip is not clipped but it has no clue where #some_bit is on the page.
I'm afraid you'll need JS to monitor where #some_bit is on the page and position the tooltip accordingly. You'd also need to kill that tooltip as soon as #some_bit is outside of the viewing area (not an issue if the trigger is mouseover).
Actually, if the trigger is mouseover then you may want to use the cursor coordinates to position the tooltip (versus calculating the position of #some_bit).
I would just put the tooltip outside of the #pane div and position it absolutely using JavaScript since you're using JS anyway.
I don't use Prototype so I don't know how it's done in Prototype, but in jQuery, you'd use $(element).position() to get the element position. If you have to do it manually, it's a little more complicated.
And you'll probably want to add a little extra logic to prevent the tooltip from extending outside of the document.
Edit: CSS used
#tooltip {
z-index: 9999;
display: none;
position: absolute;
}
JS used
Note: in jQuery, but it should be easy to change it to Prototype syntax.
$('#some_bit').hover(function() {
var docViewTop = $(window).scrollTop();
var docViewBottom = docViewTop + $(window).height();
// hovered element
var offset = $(this).offset();
var top = offset.top + docViewTop;
var left = offset.left;
var width = $(this).width();
var height = $(this).height();
var right = left + width;
var bottom = top + height;
// pane
var poffset = $('#pane').offset();
var ptop = poffset.top + docViewTop;
var pleft = poffset.left;
var pwidth = $('#pane').width();
var pheight = $('#pane').height();
var pright = pleft + pwidth;
var pbottom = ptop + pheight;
// tooltip
var ttop = bottom;
var tleft = right;
var twidth = $('#tooltip').width();
var theight = $('#tooltip').height();
var tright = tleft + twidth;
var tbottom = ttop + theight;
if (tright > pright)
tleft = pright - twidth;
if (tbottom > pbottom)
ttop = pbottom - theight;
if (tbottom > docViewBottom)
ttop = docViewBottom - theight;
$('#tooltip').offset({top: ttop, left: tleft});
$('#tooltip').css('display', 'block');
}, function() {
$('#tooltip').hide();
});
Edit: See it here.