getBoundingClientRect to know if an element is above a particular vertical point - javascript

I am using getBoundingClientRect to know if an element is above a particular point in the page. So I do this:
var rect = el.getBoundingClientRect();
if (rect.top < bottom_limit) {
return true;
}
I think this is working well, but in some debugging I have found that some elements return 0 for all top,bottom,right,left. For example: Example1 and Example2
<span id='a'>
<span id='b'>This is some crazy text.</span>
</span>
#b {
float: left;
}
and
#b {
position: absolute;
}
I am concerned about getting all zeros. Why is this happening? Can I trust this method for what I am trying to accomplish?

You could look into using document.getBoxObjectFor(el), or try el.offsetTop/offsetLeft/offsetWidth/offsetHeight. Think they may be Mozilla only though, and not sure if they will work with non-block level elements.

Related

element.scrollTop always returns 0

I'm trying to get the scrollTop position of an element, but it always returns 0. What am I doing wrong, and how can I fix it?
JSFiddle
var inner = document.getElementById('inner');
window.addEventListener('scroll', function() {
console.log(inner.scrollTop);
})
#outer {
background-color: tan;
height: 1000px;
}
#first {
background-color: bisque;
height: 200px;
}
#inner {
background-color: aquamarine;
height: 100px;
}
<div id="outer">
<div id="first"></div>
<div id="inner">scrollTop always returns 0</div>
</div>
As #FelixKling pointed out in the comments:
inner.offsetTop is what to use for this. scrollTop returns the amount you scrolled in that particular container. So because inner doesn't have a scrollbar, it never scrolls, and therefore scrollTop is 0.
But offsetTop, on the other hand, returns the distance of the current element relative to the top of the offsetParent node.
So the formula to get the amount scrolled of an element based on window, would be:
inner.offsetTop - document.body.scrollTop;
For some reason, removing 'height: 100%' from my html and body tags fixed this issue.
I hope this helps someone else!
I am not sure why, but the only thing I could get to work was using window.pageYOffset:
window.addEventListener('scroll', function() {
let scrollPosition = window.pageYOffset;
if (scrollPosition >= 30) {
headerContainer.classList.add('lg:bg-green-lightest');
} else {
headerContainer.classList.remove('lg:bg-green-lightest');
}
});
I found a simplest way to do this by adding eventListener to window object this way:
window.addEventListener("click", () => {
let value = window.scrollTop;
console.log(value);
});
This isn't the cleanest way, but It's the only way I got it work as it should.
Removing overflow: hidden from my html tag fixed this for me.

get the type from input tag in HTML using JavaScript

The application creates a temp HTML Page for Print Version.
I can disable everything on the page so that user can not interact with the page..but that makes evrything grey in color and user is having problem with reading. SO i want to make everything to be readonly..
Here is my piece of code,
var x = 0;
var element;
while (x < document.getElementsByTagName("input").length) {
element = document.getElementsByTagName("input")[x].type
if (element = "BUTTON") {
document.getElementsByTagName("input")[x].onclick = null;
}
if (element = "TEXT") {
document.getElementsByTagName("input")[x].readOnly = true;
}
if (element = "CHECKBOX") {
document.getElementsByTagName("input")[x].disabled = true;
}
x++;
}
if i remove the if block for checkbox it is working fine. but if i include the checkbox condition it is making everything disabled(even buttons and text)..when i debug i see all the if blocks are executing.. i do not understand why.
Can some one plz help me out in this regard?
When checking for equality, use === right now you're using the assignment operator, =, which means every check will return true.
to compare use == instead of =
if (element == "BUTTON") { }
Use == instead of = inside if condition.
If you simple want to disable everything on the page - a faster way to do it is place transparent DIV over page content with z-index of a higher value.
Something like this:
Hello: <input type="text"/> <br>
Check this: <input type="checkbox" />
<div style="position: absolute; z-index: 10; top:0; left;0; width:100%; height:100%" />
Demo: http://jsfiddle.net/j39Au/
I think the differential styling for disabled inputs is useful because it provides a visual feedback for the user.
This doesn't address your question directly but it may be a good alternative. You could apply styling to the disabled inputs to suit your preference, maybe something like this.
input[disabled=disabled] {
background: rgba(255,0,0,0.05);
border: dashed 1px rgba(255,0,0,0.1);
color: black;
cursor: not-allowed;
}
Finally I made a workaround to this issue.
I converted all my elements(hyperlinks, dropdowns, text) to labels and displayed them in black color.

Find first element in div that has margin? (purpose: remove margin)

I need to find the first (and last) element in a div that has a CSS margin set (> 0 px). (important: this is not necessarily the first or last element!)
Basically, I need to do this so that I can remove the margin-top for the first element and the margin-bottom for the last. Now I know you'll probably say "why not use "p:last" CSS syntax?"
Because the first or last element can be something else as well, it could be a list (UL, OL), an image, a paragraph, etc.. I cannot simply do, ul:last, ol:last, p:last as that could result in multiple elements being matched (one per type).
I only want to apply this to a single element. So that's why I think jquery is the only solution. I would gladly be wrong on this though.
Since you want only the first/last child with a margin, I imagine you'll need to use jQuery/JavaScript:
var isfirst = 1, lastelm;
$("#divid").find("*").each(function() {
var cur = $(this);
if(parseInt(cur.css("margin-top")) > 0 && isfirst == 1) {
cur.css("margin-top", 0);
isfirst = 0;
}
if(parseInt(cur.css("margin-bottom")) > 0) {
lastelm = cur;
}
});
lastelm.css("margin-bottom", 0);
I would suggest the filter method in jquery(http://api.jquery.com/filter/).
Here is a sample i could think of
<html>
<body>
<p>
<p>A<p>
<p>B<p>
<p style="margin: 10px;">C<p>
<p style="margin: 10px;">C<p>
<p>D<p>
</p>
<script>
$(document).ready(function(){
$('p').filter(function(){
return this.style.margin != '';
}).last().css('color','red');
});
</script>
<body>
​
You might want to build upon the filter logic...
:last isn't actually a CSS selector outside of jQuery. But what you could use is :first-child as well as :last-child:
#my-element > *:first-child {
margin-top: 0;
}
#my-element > *:last-child {
margin-bottom: 0;
}
Edit: Too late. However I'd definitely use the > selector so as not to style every first child in #my-element (like the first li in a ul or a strong inside a p etc).

Store positioning information from each element (JQuery/Javascript)

Pleasantries
I've been playing around with this idea for a couple of days but can't seem to get a good grasp of it. I feel I'm almost there, but could use some help. I'm probably going to slap myself right in the head when I get an answer.
Actual Problem
I have a series of <articles> in my <section>, they are generated with php (and TWIG). The <article> tags have an image and a paragraph within them. On the page, only the image is visible. Once the user clicks on the image, the article expands horizontally and the paragraph is revealed. The article also animates left, thus taking up the entire width of the section and leaving all other articles hidden behind it.
I have accomplished this portion of the effect without problem. The real issue is getting the article back to where it originally was. Within the article is a "Close" <button>. Once the button is clicked, the effect needs to be reversed (ie. The article returns to original size, only showing the image, and returns to its original position.)
Current Theory
I think I need to retrieve the offset().left information from each article per section, and make sure it's associated with its respective article, so that the article knows where to go once the "Close" button is clicked. I'm of course open to different interpretations.
I've been trying to use the $.each, each(), $.map, map() and toArray() functions to know avail.
Actual Code
/*CSS*/
section > article.window {
width:170px;
height:200px;
padding:0;
margin:4px 0 0 4px;
position:relative;
float:left;
overflow:hidden;
}
section > article.window:nth-child(1) {margin-left:0;}
<!--HTML-->
<article class="window">
<img alt="Title-1" />
<p><!-- I'm a paragraph filled with text --></p>
<button class="sClose">Close</button>
</article>
<article class="window">
<!-- Ditto + 2 more -->
</article>
Failed Attempt Example
function winSlide() {
var aO = $(this).parent().offset()
var aOL = aO.left
var dO = $(this).offset()
var dOL = dO.left
var dOT = dO.top
var adTravel = dOL-aOL
$(this).addClass('windowOP');
$(this).children('div').animate({left:-(adTravel-3)+'px', width:'740px'},250)
$(this).children('div').append('<button class="sClose">Close</button>');
$(this).unbind('click', winSlide);
}
$('.window').on('click', winSlide)
$('.window').on('click', 'button.sClose', function() {
var wW = $(this).parents('.window').width()
var aO = $(this).parents('section').offset()
var aOL = aO.left
var pOL = $(this).parents('.window').offset().left
var apTravel = pOL - aOL
$(this).parent('div').animate({left:'+='+apTravel+'px'},250).delay(250, function() {$(this).animate({width:wW+'px'},250); $('.window').removeClass('windowOP');})
$('.window').bind('click', winSlide)
})
Before you go scratching your head, I have to make a note that this attempt involved an extra div within the article. The idea was to have the article's overflow set to visible (.addclass('windowOP')) with the div moving around freely. This method actually did work... almost. The animation would fail after it fired off a second time. Also for some reason when closing the first article, the left margin was property was ignored.
ie.
First time a window is clicked: Performs open animation flawlessly
First time window's close button is clicked: Performs close animation flawlessly, returns original position
Second time SAME window is clicked: Animation fails, but opens to correct size
Second time window's close button is clicked (if visible): Nothing happens
Thank you for your patience. If you need anymore information, just ask.
EDIT
Added a jsfiddle after tinkering with Flambino's code.
http://jsfiddle.net/6RV88/66/
The articles that are not clicked need to remain where they are. Having problems achieving that now.
If you want to go for storing the offsets, you can use jQuery's .data method to store data "on" the elements and retrieve it later:
// Store offset before any animations
// (using .each here, but it could also be done in a click handler,
// before starting the animation)
$(".window").each(function () {
$(this).data("closedOffset", $(this).position());
});
// Retrieve the offsets later
$('.window').on('click', 'button.sClose', function() {
var originalOffset = $(this).data("originalOffset");
// ...
});
Here's a (very) simple jsfiddle example
Update: And here's a more fleshed-out one
Big thanks to Flambino
I was able to create the effect desired. You can see it here: http://jsfiddle.net/gck2Y/ or you can look below to see the code and some explanations.
Rather than having each article's offset be remembered, I used margins on the clicked article's siblings. It's not exactly pretty, but it works exceptionally well.
<!-- HTML -->
<section>
<article>Click!</article>
<article>Me Too</article>
<article>Me Three</article>
<article>I Aswell</article>
</section>
/* CSS */
section {
position: relative;
width: 404px;
border: 1px solid #000;
height: 100px;
overflow:hidden
}
article {
height:100px;
width:100px;
position: relative;
float:left;
background: green;
border-right:1px solid orange;
}
.expanded {z-index:2;}
//Javascript
var element = $("article");
element.on("click", function () {
if( !$(this).hasClass("expanded") ) {
$(this).addClass("expanded");
$(this).data("originalOffset", $(this).offset().left);
element.data("originalSize", {
width: element.width(),
height: element.height()
});
var aOffset = $(this).data("originalOffset");
var aOuterWidth = $(this).outerWidth();
if(!$(this).is('article:first-child')){
$(this).prev().css('margin-right',aOuterWidth)
} else {
$(this).next().css('margin-left',aOuterWidth)
}
$(this).css({'position':'absolute','left':aOffset});
$(this).animate({
left: 0,
width: "100%"
}, 500);
} else {
var offset = $(this).data("originalOffset");
var size = $(this).data("originalSize");
$(this).animate({
left: offset + "px",
width: size.width + "px"
}, 500, function () {
$(this).removeClass("expanded");
$(this).prev().css('margin-right','0')
$(this).next().css('margin-left','0')
element.css({'position':'relative','left':0});
});
}
});​

Selecting a div by classname

I got this div...
<div tabindex="0" class="button-base inline-block button aw-btn button-base-active">
<input type="text" tabindex="-1" style="opacity: 0; height: 1px; width: 1px; z-index: -1; overflow-x: hidden; overflow-y: hidden; position: absolute; ">
</div>
in the middle of my page, it has no id and I am unable to edit the pages HTML, I am also no able to use jQuery. Also trying to do it with IE7 and IE8.
Nightmare here :)
The solution would be document.getElementsByClassName but that is not ie7 and ie8 compatible.
This div is buried in about 10 divs, all of which are similar style with no id's etc. The classes on this div are unique!
The only solution I can see is to get ALL divs and loop them looking for hasAttriutes similar.
Anyone have a better idea?
Here's a cross-browser implementation of getElementsByClassName for non-compliant browsers (citation):
if (!document.getElementsByClassName)
{
document.getElementsByClassName = function(classname)
{
var elArray = [];
var tmp = document.getElementsByTagName("*");
var regex = new RegExp("(^|\\s)" + classname + "(\\s|$)");
for ( var i = 0; i < tmp.length; i++ ) {
if ( regex.test(tmp[i].className) ) {
elArray.push(tmp[i]);
}
}
return elArray;
};
}
Nope, that's how it's done. Unless they're in something with an ID you're stuck iterating all DIVs on the page. Fortunately it is just a list though (no need to recurse through a tree) so it's not so bad.
I would suggest using XPaths to select the nodes. Might work...
Use jQuery/Sizzle. IE6 and up. :)
Load it:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
Use it:
jQuery('.class.otherclass.anotherclass')

Categories