What I'm trying to accomplish: When any element with the class "yes" is scrolled to a specific Y position in the window (we'll just pretend it's the very top of the window to keep it simple), I just want to get that element so I can grab some of its attributes.
<div class="container">
<div class="no"></div>
<div class="yes"></div>
<div class="yes"></div>
<div class="yes"></div>
<div class="yes"></div>
<div class="no"></div>
</div>
I've tried this two ways, and this first way works, but it seems terribly inefficient, as it's constantly have to assess the position all of the elements during scroll. There can be a lot. But maybe I'm overestimating how how inefficient it is?
Method 1:
$el = $('.yes');
$el.each(function(){
var distance = $(this).offset().top;
$window.scroll(function() {
if ( $window.scrollTop() >= distance ) {
// get the stuff
}
});
});
The other way seems more efficient (but maybe not?) but I haven't gotten it to work as needed:
$window.scroll(function(){
var el = document.elementsFromPoint(0, 0);
var $el = $(el);
if($el.hasClass('yes')){
//get the stuff
}
});
This doesn't seem to be working correctly at all. Picks up some child elements ".yes" element, even if those elements don't have the "yes" class themselves, and also fails to pick up other ".yes" elements at all. I don't know why.
(Side note: I've simplified the code examples to remove other efficiencies, such as setTimeout for scrolling and tests to only perform functions if the element I'm trying to get changes, etc… I just need to figure out how to get position testing to work best).
I think what you're looking for is the intersection/observer API
<div class="container">
<div class="no"></div>
<div lang="javascript" class="yes"></div>
<div class="no"></div>
<div lang="python" class="yes"></div>
<div class="no"></div>
<div lang="go" class="yes"></div>
<div class="no"></div>
<div lang="rust" class="yes"></div>
<div class="no"></div>
<div class="end"><h1>HOORAY! YOU KNOW KNOW THE INTERSECTION OBSERVER API</h1>
Read more about it here
</div>
</div>
<script>
let observer = new IntersectionObserver((divs, observer) => {
divs.forEach(div => {
if(div.isIntersecting){
//grab your attributes
var _target = div.target
let theAttr = _target.getAttribute("lang");
console.log('div is intersecting with language attribute of: ' + theAttr);
observer.unobserve(div.target);
}
});
}, {threshold: 1});
document.querySelectorAll('div.container > div.yes').forEach(div => { observer.observe(div) });
</script>
<style>
.container {height: 500vh;}
.no {background-color: #e87e7e;
height: 50vh;}
.yes {background-color: #00800478;
height: 50vh;}
.end{text-align: center;}
</style>
here is mozilla's documentation on the intersection/observer API.
Related
HTML
<body>
<div id='el1'>
<div class='directChildren' style='width:50%;height:100px;float:left;margin-top:0'>
....
</div>
</div>
<div id='el2'>
<div class='directChildren' style='width:50%;height:100px;float:right;margin-top:20px'>
....
</div>
</div>
<div style='clear:both'/>
</body>
JS
const topEl1 = document.getElementById("el1").getBoundingClientRect().top; // return 0 (only example)
const topEl2 = document.getElementById("el2").getBoundingClientRect().top; // return 20 (topEl1 + margin top of el2)
QUESTION:
Both of divs (el1, el2) has same top coordinate but why getBoundingClientRect return different ?. This case also happen for margin-bottom.
Because the CSS is not properly defined. You are using float on inner element(.directChildren) instead of using it on #el1 and #el2 without properly clearing float behavior on parent elements. That is why the effect of margin-top is overflowing the parent's dimension.
It is much easier to achieve the correct result by using following HTML and CSS where I have used proper styling by using flex. You can also use float if you want.
<div class="container">
<div id='el1'>
<div class='directChildren' style='height:100px;margin-top:0'>
....
</div>
</div>
<div id='el2'>
<div class='directChildren' style='height:100px;margin-top:20px'>
....
</div>
</div>
</div>
.container {
display: flex;
}
.container .directChildren {
flex: 1;
}
I have the following HTML:
<div class="page">
<div class="somecontent_1">...</div>
<div class="somecontent_2">...</div>
<div class="somecontent_3">...</div>
<div class="somecontent_4">...</div>
</div>
Now I'd like to separate the content with a separate page so it looks something like this:
<div class="page">
<div class="somecontent">...</div>
<div class="somecontent">...</div>
</div>
<div class="newpage">
<div class="somecontent">...</div>
<div class="somecontent">...</div>
</div>
The function checks the height of each class somecontent and if it's larger than a certain amount, I need to move the content to a new page.
My guess is that I would need to create an empty div (newpage) and then fetch the elements after the height is exceeded and move them to the empty newpage and continue iterate like that.
My question would be how I would get all content that are after the last element that reached the height so I can move it to the new empty page that I would create. Other solutions are most welcome if there is a better way of doing it!
The code I came up with looks like this:
var page = $('.page');
var pageHeight = 0;
$.each(page.find('.somecontent'), function() {
if (pageHeight > 1000) {
page.next('<div class="newpage"></div>');
/* Somehow get all elements to add to the newly created page */
page.next('.newpage').append(<NEXT_ELEMENTS>);
pageHeight = 0;
}
pageHeight = pageHeight + $(this).height();
});
When you reach the page which answers the height criterion use the .nextAll function to get all the next siblings of it, then use .wrapAll to wrap them with your newpage div.
Here is the corresponding documentation of nextAll and wrapAll, it has everything you need to cover your scenario.
See comments in line below.
// Instead of the $.each() utiility function
// Just loop over each content area that needs
// examination
$('.somecontent').each(function(index, item) {
// Check if the height of the item is greater than the target (20px for this example)
if (parseInt(getComputedStyle(item).height,10) > 20) {
// Make a new div after the div that the item is currently in
// if one doesn't already exist
if($(".newpage").length === 0){
$(item.closest(".page")).after('<div class="newpage"></div>');
}
// Move the item into the new div
$(item.closest(".page")).next('.newpage').append(item);
}
});
//console.log(document.body.innerHTML); // shows resulting HTML
div.page {border:1px solid red; }
div.newpage {border:1px solid green; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="page">
<div class="somecontent">This<br>is<br>some<br>content</div>
<div class="somecontent">This is some content</div>
<div class="somecontent">
<ul>
<li>This</li>
<li>is</li>
<li>some</li>
<li>content</li>
</ul>
</div>
<div class="somecontent">Other</div>
</div>
So i have a div element which will be filled dynamically with others divs using the appendChild Method, this should display a list. The User is now able to sort that list with the JqueryUI Sortable option.I also added some sortable option attribues like follows:
Options:
$("#NameContainer").sortable("option", "axis", "y");
$("#NameContainer").sortable( "option", "containment", "parent" );
LIST
<div id="NameContainer" class="ui-widget">
<div id="Name_1">John</div>
<div id="Name_2">Jack</div>
<div id="Name_3">Charlie</div>
<div id="Name_4">Sawyer</div>
<div id="Name_5">Yin</div>
<div id="Name_6">Ben</div>
</div>
Now comes my problem. The appendChild always inserts the new div at the bottom of the container but i want to to add some space at the bottom of to the Container Div with a "br" or something like that. I want to add that space to make sure that when the user sorts the last item of that list it will get sorted correctly because the "containment" bounds sometimes wont allow to sort under the last item.
<div id="NameContainer" class="ui-widget">
<div id="Name_1">John</div>
<div id="Name_2">Jack</div>
<div id="Name_3">Charlie</div>
<div id="Name_4">Sawyer</div>
<div id="Name_5">Yin</div>
<div id="Name_6">Ben</div>
<br><!--SPACEHOLDER-->
</div>
So here comes my Question is there away to appendChild above a certain element? Like a "br" "div" or "p"?
Try this instead of appendChild:
Please note I have used random value to add in div as I don't have your dynamic value.
check fiddle: https://jsfiddle.net/dqx9nbcy/
<div id="NameContainer" class="ui-widget">
<div id="divspacer"></div>
</div>
<button id="btn">ADD Element</button>
$(document).ready(function(){
$("#btn").click(function(){
var parentnode = document.getElementById("NameContainer");
var existnode = document.getElementById("divspacer");
var rand = Math.floor((Math.random() * 10) + 1);
var newName = document.createElement("div");
newName.setAttribute("id", rand);
newName.setAttribute("value", rand);
newName.setAttribute("class","ui-widget-content");
newName.innerHTML = rand;
parentnode.insertBefore(newName,existnode);
});
});
refer http://api.jquery.com/appendto/ but you need to make sure that your are targeting right tag.
You can try with this code snippet.
HTML Snippet
<div id="NameContainer" class="ui-widget">
<div id="Name1">Name1</div>
<div id="Name2">Name2</div>
<div id="Name3">Name3</div>
<div id="Name4">Name4</div>
<br>
<br>
</div>
Javascript Snippet
$(document).ready(function(){
$("#btn").click(function(){
var containerDiv= $("#NameContainer");
var childList = containerDiv.children("div");
var newElementid = childList.length;
var newName = document.createElement("div");
newName.setAttribute("id", "Name"+(newElementid+1));
newName.setAttribute("value", "Name"+(newElementid+1));
newName.setAttribute("class","ui-widget-content");
newName.innerHTML = "Name"+(newElementid+1);
$(childList[childList.length-1]).after(newName);
});
});
This is specific to a situation where there are some elements in the initial list. The same can be modified for dynamic list of implementation by validating that childList.length is != 0 before using the same.
I have a page where in the content area a dynamic number of rows are generated. Each row has two columns: a side block and a content area. The idea is to have the side block stick while the page scrolls down until the next block appears and pushes the block above away and become sticky itself. The html structure is as follows:
<div class="container">
<div class="row">
<div class="col-md-4 sticky">
<h2>Example</h2>
<p class="page-side-block">...</p>
...
</div>
<div class="col-md-8 col-md-offset-4">
<p>...</p>
</div>
</div>
...
<div class="row">
<div class="col-md-4 sticky">
<h2>Example</h2>
<p class="page-side-block">...</p>
...
</div>
<div class="col-md-8 col-md-offset-4">
<p>...</p>
</div>
</div>
</div>
I've managed to get the first block to stick but I don't really know how to get about fixing the functionality as described above. Any help on the matter would be greatly appreciated.
I'll include the javascript (using mootools) below. I know this won't work with the html that is provided above as I've edited it to show the semantics rather than the actual markup.
window.onscroll = function() {
var stickyBlock = $('sticky-block');
if (window.getScroll().y > 235) {
stickyBlock.setStyles({
position: 'fixed',
top: '100px',
width: "350px"
});
} else if (window.getScroll().y < 235) {
stickyBlock.setStyles({
position: 'absolute',
top: 0,
width: null
});
}
}
I know that in the new w3c specs a definition has been made for a position: sticky; property. This would make javascript completely unnecessary but so far browser support for it is horrible as shown here.
Again if anyone could help me out, that would be awesome ;)
A bit late night here, but leave you a suggestion.
If you capture all positions and listen to scroll you can iterate the initial positions and compare it with scroll and make the element fixed when needed.
In my example I use .pin() that you can find in MooTools More, just to not re-invent the wheel.
Example: http://jsfiddle.net/2AZ28/
MooTools:
var stickyBlock = $$('.sticky').map(function(st){
return {
el: st,
pos: st.getPosition().y,
height: st.getHeight()
};
});
window.addEvent('scroll', function(){
var currentScroll = window.getScroll().y;
stickyBlock.each(function (st, i) {
if (st.pos <= currentScroll){
st.el.pin();
if (stickyBlock[i + 1] && (stickyBlock[i + 1].pos - st.height <= currentScroll)) st.el.setStyle('top', stickyBlock[i + 1].pos - st.height - currentScroll);
} else {
st.el.unpin();
}
});
})
Maybe could be more optimized, but hope this helps in your question.
I'm using the liferay framework and I need to add a JavaScript detected inline height to a very very specific div in my page. The problem is I need to target it going through an unknown number of dynamically added divs with dynamically added classes and IDs. To complicate this even further, the divs are randomly siblings or nested in each other.
Here's what it looks like:
<div class="known-class">
<div class="unknown dynamicallygenerated"></div>
<div class="unknown dynamicallygenerated">
<div class="unknown dynamicallygenerated">
<div class="unknown dynamicallygenerated"></div>
<div class="unknown dynamicallygenerated">
<div class="DIV-I-WANT-TO-TARGET">this is the div i need to Target with my css/javascript</div>
</div>
</div>
</div>
obviously I can't target it simply with
function resize() {
var heights = window.innerHeight;
jQuery('.DIV-I-WANT-TO-TARGET').css('height', heights + "px");
}
resize();
Because that class is present elsewhere, I would rather target it with something like.
jQuery('.known-class .DIV-I-WANT-TO-TARGET')
Which obviously doesn't work because there's a ton of other divs in the middle and my div is not a child of ".known-class"
I was asking myself if there was any jQuery that could help. Something like:
Catch any div with .DIV-I-WANT-TO-TARGET class that is "generically" inside another div that has .known-class
Is this possible? thanks a lot for your help!
Something like this would work:
// this will target the known-class and find all children with DIV-I-WANT-TO-TARGET
$('div.known-class').find('div.DIV-I-WANT-TO-TARGET');
// this will target the known-class and find the first DIV-I-WANT-TO-TARGET
$('div.known-class').find('div.DIV-I-WANT-TO-TARGET').first();
$('div.known-class').find('div.DIV-I-WANT-TO-TARGET:first');
$('div.known-class').find('div.DIV-I-WANT-TO-TARGET:eq(0)');
$('div.known-class').find('div.DIV-I-WANT-TO-TARGET').eq(0);
You can try in your css file
.known-class div div div div{}
The last div being the DIV-I-WANT-TO-TARGET
Assuming that you are adding the divs starting from the outer to the inner
Assign an equal name plus a number starting from 1
<div class="known-class">
<div class="unknown dynamicallygenerated" id="dynamicdiv1"></div>
<div class="unknown dynamicallygenerated" id="dynamicdiv2">
<div class="unknown dynamicallygenerated" id="dynamicdiv3">
<div class="unknown dynamicallygenerated" id="dynamicdiv4"></div>
<div class="unknown dynamicallygenerated" id="dynamicdiv5">
<div class="DIV-I-WANT-TO-TARGET" id="dynamicdiv6"></div>
</div>
</div>
</div>
The use jQuery [.each][1] to loop through all the divs on the document
$( document.body ).click(function() {
$( "div" ).each(function( i ) {
if ( this.style.color !== "blue" ) {
this.style.color = "blue";
} else {
this.style.color = "";
}
});
});
When you reach the last item in numeric order. (you can use any split function) add the attributes to that div
you need to select last div inside the known-class:
$('.known-class').find('div:last').css('background', 'Red')
OR if you want to select all the .known-class :
$('.known-class').each(function() {$(this).find('div:last').css('background', 'Red')});
Actually your selector works just fine:
$('.known-class .DIV-I-WANT-TO-TARGET')
With a space, selectors will find any descendant.
The search is only limited to direct descendants (immediate children) if you use the > operator.
So $('.known-class > .DIV-I-WANT-TO-TARGET') would not find what you wanted.