Prepend only once on scroll - javascript

Prepend an element on scroll only once if class exist. Remove if class does not exist.
For example scroll down -> class is added + prepend. Scroll back up -> class is removed so is prepend and so on.
$(window).scroll(function() {
if($("#container").hasClass("active")){
$("#container-wrapper").addClass("active-exists");
$("#container-wrapper").prepend("<p>Test</p>");
}
else {
$("#container-wrapper").removeClass("active-exists");
$("#container-wrapper").remove("<p>Test</p>");
}
});
Current behavior keeps adding infinite <p>Test</p> non stop, just spams it while scrolling up and down.

You can check this with a flag "appended"
window.appended = false; // set as global variable
$(window).scroll(function() {
if($("#container").hasClass("active")){
$("#container-wrapper").addClass("active-exists");
if (!window.appended){ // check with global variable
$("#container-wrapper").prepend("<p>Test</p>");
appended = true;
}
}
else {
$("#container-wrapper").removeClass("active-exists");
$("#container-wrapper").remove("<p>Test</p>");
}
}

Instead of checking whether the element has a specific class, check whether the y-axis scroll position is 0. You'll also need to store whether you've appended the element in a variable, so as not to keep on appending on scroll.
let elem = $('<p>Test</p>')
var hasAdded = false;
$(window).scroll(function() {
const scrollY = window.scrollY;
if (scrollY == 0) {
$('#container-wrapper').removeClass('active-exists')
elem.remove()
hasAdded = false;
} else {
if (!hasAdded) {
$('#container-wrapper').addClass('active-exists').prepend(elem)
hasAdded = true
}
}
});
html,
body {
height: 300%;
}
#container-wrapper{
position:fixed;
height:50px;
}
.active-exists{
background-color:yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="container-wrapper">
<div id="container">
</div>
</div>

Related

JavaScript: Select multiple elements by z-index number (in dynamic DOM)

I would like to select ALL elements in DOM that have the z-index = 2147483647 using 100% JavaScript (NO jQuery)
The DOM is constantly dynamically changing; adding and removing
elements. The code to remove elements by z-index ### MUST have a DOM event listener
I've tried so many iterations of similar codes without success. This is my last iteration attempt and for some reason it is not working
window.addEventListener('change', function() {
var varElements = document.querySelectorAll("[style='z-index: 2147483647']");
if(varElements) { for(let varElement of varElements) { varElement.remove(); } }
} //function
}) //window.
check below code
const check = () => {
var varElements = document.querySelectorAll("*");
for(let varElement of varElements) {
if(varElement.style['z-index'] == 10) {
var node = document.createElement("LI");
var textnode = document.createTextNode(varElement.className);
node.appendChild(textnode);
document.getElementById('list').appendChild(node)
}
}
}
window.addEventListener('change', check )
window.addEventListener('load', check);
<div class="top" style="z-index:10">
<div class="inner1" style="display:'block';z-index:10">
<div class="inner2" style="z-index:10">
</div>
</div>
<div class="inner3" style="z-index:12">
</div>
</div>
<ul id="list">
</ul>
There are some things that come into play here i.e. it has to be positioned to get a z-index. Here I show some examples and how to find stuff that has a z-index not "auto";
You can then loop the list to find a z-index you desire. Here, I just pushed all elements with a z-index not "auto" but you could use your selected index value to filter those out in the conditional for example if (!isNaN(zIndex) && zIndex != "auto" && zIndex == 4042) for those with 4042 value;
Once you have your elements, you can do what you desire which is to set an event handler on each of them.
This specifically answers the question of finding the elements by z-index, not the ultimate desire which is another question of how to manage the changes to the DOM and adding/removing on mutation.
var getZIndex = function(checkelement) {
let compStyles = window.getComputedStyle(checkelement);
let z = compStyles.getPropertyValue('z-index');
if (typeof z == "object" || (isNaN(z) && checkelement.parentNode != document.body)) {
return getZIndex(checkelement.parentNode);
} else {
return z;
}
};
let evallist = document.querySelectorAll("div");
let zthings = [];
for (let item of evallist) {
let zIndex = getZIndex(item);
if (!isNaN(zIndex) && zIndex != "auto") {
zthings.push(item);
}
}
console.log(zthings);
.sureThing {
z-index: 4242;
position: absolute;
background: gold;
top: 4em;
}
<div class="mything">Howddy</div>
<div class="sureThing">Here I am</div>
<div class="onelink">https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle</div>
<div class="otherthing" style="z-index:4040;">other thing set internal woops Ihave no position so I am not in list</div>
<div class="otherthing" style="z-index:4040;position: absolute;top:5em;">other thing set internal OK</div>
You cannot select an element based on css in css. So you cannot use the querySelectorAll. This code works if the css is set by the inline style attribute. Here is the code explained:
Get all element using *.
Turn the NodeList into an array.
Filter out the elements that do not have a specific css property.
get the css properties using: window.getComputedStyle()
window.addEventListener( 'load', () => {
let all = document.querySelectorAll('*');
all = Array.from(all);
const filtered = all.filter( zindex_filter )
console.log( filtered )
})
function zindex_filter (element) {
const style = window.getComputedStyle(element);
console.log( style.getPropertyValue('z-index') )
if( style.getPropertyValue('z-index') == 100 ) return true;
else return false;
}
.div {
width: 100px;
height: 100px;
margin: 10px;
}
.zindex {
position: relative;
z-index: 100;
}
<div class='zindex div'></div>
<div class='div'></div>
<div class='div' style='position: relative; z-index: 100; width: 100px;'></div>
Notes:
window.getComputedStyle() docs
note that the z-indexed must be positioned correctly to return a value other than auto.

Know when flex-box puts item to new row [duplicate]

I have flex container with items inside. How to detect flex wrap event? I want to apply some new css to elements that have been wrapped. I suppose that it is impossible to detect wrap event by pure css. But it would be very powerful feature! I can try to "catch" this break point event by media query when element wraps into new line/row. But this is a terrible approach. I can try to detect it by script, but it's also not very good.
I am very surprised, but simple $("#element").resize() doesn't work to detect height or width changes of flex container to apply appropriate css to child elements. LOL.
I have found that only this example of jquery code works
jquery event listen on position changed
But still terribly.
Here's one potential solution. There might be other gotchas and edge cases you need to check for.
The basic idea is to loop through the flex items and test their top position against the previous sibling. If the top value is greater (hence further down the page) then the item has wrapped.
The function detectWrap returns an array of DOM elements that have wrapped, and could be used to style as desired.
The function could ideally be used with a ResizeObserver (while using window's resize event as a fallback) as a trigger to check for wrapping as the window is resized or as elements in the page change due to scripts and other user-interaction. Because the StackOverflow code window doesn't resize it won't work here.
Here's a CodePen that works with a screen resize.
var detectWrap = function(className) {
var wrappedItems = [];
var prevItem = {};
var currItem = {};
var items = document.getElementsByClassName(className);
for (var i = 0; i < items.length; i++) {
currItem = items[i].getBoundingClientRect();
if (prevItem && prevItem.top < currItem.top) {
wrappedItems.push(items[i]);
}
prevItem = currItem;
};
return wrappedItems;
}
window.onload = function(event){
var wrappedItems = detectWrap('item');
for (var k = 0; k < wrappedItems.length; k++) {
wrappedItems[k].className = "wrapped";
}
};
div {
display: flex;
flex-wrap: wrap;
}
div > div {
flex-grow: 1;
flex-shrink: 1;
justify-content: center;
background-color: #222222;
padding: 20px 0px;
color: #FFFFFF;
font-family: Arial;
min-width: 300px;
}
div.wrapped {
background-color: red;
}
<div>
<div class="item">A</div>
<div class="item">B</div>
<div class="item">C</div>
</div>
Little bit improved snippet on jQuery for this purpose.
wrapped();
$(window).resize(function() {
wrapped();
});
function wrapped() {
var offset_top_prev;
$('.flex-item').each(function() {
var offset_top = $(this).offset().top;
if (offset_top > offset_top_prev) {
$(this).addClass('wrapped');
} else if (offset_top == offset_top_prev) {
$(this).removeClass('wrapped');
}
offset_top_prev = offset_top;
});
}
I've modified sansSpoon's code to work even if the element isn't at the absolute top of the page. Codepen: https://codepen.io/tropix126/pen/poEwpVd
function detectWrap(node) {
for (const container of node) {
for (const child of container.children) {
if (child.offsetTop > container.offsetTop) {
child.classList.add("wrapped");
} else {
child.classList.remove("wrapped");
}
}
}
}
Note that margin-top shouldn't be applied to items since it's factored into getBoundingClientRect and will trigger the wrapped class to apply on all items.
I'm using a similar approach in determining if a <li> has been wrapped in an <ul> that has it's display set to flex.
ul = document.querySelectorAll('.list');
function wrapped(ul) {
// loops over all found lists on the page - HTML Collection
for (var i=0; i<ul.length; i++) {
//Children gets all the list items as another HTML Collection
li = ul[i].children;
for (var j=0; j<li.length; j++) {
// offsetTop will get the vertical distance of the li from the ul.
// if > 0 it has been wrapped.
loc = li[j].offsetTop;
if (loc > 0) {
li[j].className = 'wrapped';
} else {
li[j].className = 'unwrapped';
}
}
}
}
I noticed elements will typically wrap in relation to the first element. Comparing offset top of each element to the first element is a simpler approach. This works for wrap and wrap-reverse. (Probably won't work if elements use flex order)
var wrappers = $('.flex[class*="flex-wrap"]'); //select flex wrap and wrap-reverse elements
if (wrappers.length) { //don't add listener if no flex elements
$(window)
.on('resize', function() {
wrappers.each(function() {
var prnt = $(this),
chldrn = prnt.children(':not(:first-child)'), //select flex items
frst = prnt.children().first();
chldrn.each(function(i, e) { $(e).toggleClass('flex-wrapped', $(e).offset().top != frst.offset().top); }); //element has wrapped
prnt.toggleClass('flex-wrapping', !!prnt.find('.flex-wrapped').length); //wrapping has started
frst.toggleClass('flex-wrapped', !!!chldrn.filter(':not(.flex-wrapped)').length); //all are wrapped
});
})
.trigger('resize'); //lazy way to initially call the above
}
.flex {
display: flex;
}
.flex.flex-wrap {
flex-wrap: wrap;
}
.flex.flex-wrap-reverse {
flex-wrap: wrap-reverse;
}
.flex.flex-1 > * { /*make items equal width*/
flex: 1;
}
.flex > * {
flex-grow: 1;
}
.cc-min-width-200 > * { /*child combinator*/
min-width: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="flex flex-1 flex-wrap-reverse cc-min-width-200">
<div>Hello</div>
<div>There</div>
<div>World</div>
</div>
If someone wants to find the last element of the row from where wrapped elements started can use the below logic. It's applicable for multiple lines as well
window.onresize = function (event) {
const elements = document.querySelectorAll('.borrower-detail');
let previousElement = {};
let rowTop = elements[0].getBoundingClientRect().top;
elements.forEach(el => el.classList.remove('last-el-of-row'))
elements.forEach(el => {
const elementTop = el.getBoundingClientRect().top;
if (rowTop < elementTop) {
previousElement.classList.add('last-el-of-row');
rowTop = elementTop;
}
previousElement = el;
})
};

How to detect CSS flex wrap event

I have flex container with items inside. How to detect flex wrap event? I want to apply some new css to elements that have been wrapped. I suppose that it is impossible to detect wrap event by pure css. But it would be very powerful feature! I can try to "catch" this break point event by media query when element wraps into new line/row. But this is a terrible approach. I can try to detect it by script, but it's also not very good.
I am very surprised, but simple $("#element").resize() doesn't work to detect height or width changes of flex container to apply appropriate css to child elements. LOL.
I have found that only this example of jquery code works
jquery event listen on position changed
But still terribly.
Here's one potential solution. There might be other gotchas and edge cases you need to check for.
The basic idea is to loop through the flex items and test their top position against the previous sibling. If the top value is greater (hence further down the page) then the item has wrapped.
The function detectWrap returns an array of DOM elements that have wrapped, and could be used to style as desired.
The function could ideally be used with a ResizeObserver (while using window's resize event as a fallback) as a trigger to check for wrapping as the window is resized or as elements in the page change due to scripts and other user-interaction. Because the StackOverflow code window doesn't resize it won't work here.
Here's a CodePen that works with a screen resize.
var detectWrap = function(className) {
var wrappedItems = [];
var prevItem = {};
var currItem = {};
var items = document.getElementsByClassName(className);
for (var i = 0; i < items.length; i++) {
currItem = items[i].getBoundingClientRect();
if (prevItem && prevItem.top < currItem.top) {
wrappedItems.push(items[i]);
}
prevItem = currItem;
};
return wrappedItems;
}
window.onload = function(event){
var wrappedItems = detectWrap('item');
for (var k = 0; k < wrappedItems.length; k++) {
wrappedItems[k].className = "wrapped";
}
};
div {
display: flex;
flex-wrap: wrap;
}
div > div {
flex-grow: 1;
flex-shrink: 1;
justify-content: center;
background-color: #222222;
padding: 20px 0px;
color: #FFFFFF;
font-family: Arial;
min-width: 300px;
}
div.wrapped {
background-color: red;
}
<div>
<div class="item">A</div>
<div class="item">B</div>
<div class="item">C</div>
</div>
Little bit improved snippet on jQuery for this purpose.
wrapped();
$(window).resize(function() {
wrapped();
});
function wrapped() {
var offset_top_prev;
$('.flex-item').each(function() {
var offset_top = $(this).offset().top;
if (offset_top > offset_top_prev) {
$(this).addClass('wrapped');
} else if (offset_top == offset_top_prev) {
$(this).removeClass('wrapped');
}
offset_top_prev = offset_top;
});
}
I've modified sansSpoon's code to work even if the element isn't at the absolute top of the page. Codepen: https://codepen.io/tropix126/pen/poEwpVd
function detectWrap(node) {
for (const container of node) {
for (const child of container.children) {
if (child.offsetTop > container.offsetTop) {
child.classList.add("wrapped");
} else {
child.classList.remove("wrapped");
}
}
}
}
Note that margin-top shouldn't be applied to items since it's factored into getBoundingClientRect and will trigger the wrapped class to apply on all items.
I'm using a similar approach in determining if a <li> has been wrapped in an <ul> that has it's display set to flex.
ul = document.querySelectorAll('.list');
function wrapped(ul) {
// loops over all found lists on the page - HTML Collection
for (var i=0; i<ul.length; i++) {
//Children gets all the list items as another HTML Collection
li = ul[i].children;
for (var j=0; j<li.length; j++) {
// offsetTop will get the vertical distance of the li from the ul.
// if > 0 it has been wrapped.
loc = li[j].offsetTop;
if (loc > 0) {
li[j].className = 'wrapped';
} else {
li[j].className = 'unwrapped';
}
}
}
}
I noticed elements will typically wrap in relation to the first element. Comparing offset top of each element to the first element is a simpler approach. This works for wrap and wrap-reverse. (Probably won't work if elements use flex order)
var wrappers = $('.flex[class*="flex-wrap"]'); //select flex wrap and wrap-reverse elements
if (wrappers.length) { //don't add listener if no flex elements
$(window)
.on('resize', function() {
wrappers.each(function() {
var prnt = $(this),
chldrn = prnt.children(':not(:first-child)'), //select flex items
frst = prnt.children().first();
chldrn.each(function(i, e) { $(e).toggleClass('flex-wrapped', $(e).offset().top != frst.offset().top); }); //element has wrapped
prnt.toggleClass('flex-wrapping', !!prnt.find('.flex-wrapped').length); //wrapping has started
frst.toggleClass('flex-wrapped', !!!chldrn.filter(':not(.flex-wrapped)').length); //all are wrapped
});
})
.trigger('resize'); //lazy way to initially call the above
}
.flex {
display: flex;
}
.flex.flex-wrap {
flex-wrap: wrap;
}
.flex.flex-wrap-reverse {
flex-wrap: wrap-reverse;
}
.flex.flex-1 > * { /*make items equal width*/
flex: 1;
}
.flex > * {
flex-grow: 1;
}
.cc-min-width-200 > * { /*child combinator*/
min-width: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="flex flex-1 flex-wrap-reverse cc-min-width-200">
<div>Hello</div>
<div>There</div>
<div>World</div>
</div>
If someone wants to find the last element of the row from where wrapped elements started can use the below logic. It's applicable for multiple lines as well
window.onresize = function (event) {
const elements = document.querySelectorAll('.borrower-detail');
let previousElement = {};
let rowTop = elements[0].getBoundingClientRect().top;
elements.forEach(el => el.classList.remove('last-el-of-row'))
elements.forEach(el => {
const elementTop = el.getBoundingClientRect().top;
if (rowTop < elementTop) {
previousElement.classList.add('last-el-of-row');
rowTop = elementTop;
}
previousElement = el;
})
};

Add class to nav when scrolling to the anchor

This is dynamic. The nav changes based upon the page. The code here adds a class to the nav as you scroll to the section and then removes it as you scroll past. The problem is it only removes as you scroll down not up past the section. How do I do my condition to remove classes as you scroll up while achieving adding the class and removing it as you scroll down??
jQuery( document ).ready(function() {
var sectionelements = jQuery('.nav li');
(function(jQuery) {
var scrolling = function(){
jQuery(sectionelements).each(function(){
var object=jQuery('#'+this);
var wh = jQuery(window).height();
var st = jQuery(document).scrollTop();
var ot = jQuery(object).offset().top;
var eh = jQuery(object).height();
var href="a[href*=#"+object.attr('id')+"]";
if(st>ot){
jQuery(href).addClass('posreached');
}
if (st>ot+eh) {
jQuery(href).removeClass('posreached');
};
})
};
jQuery(window).scroll(scrolling);
jQuery(window).bind('resize orientationchange',scrolling);
//fire initial scroll
jQuery(window).scroll();
})(jQuery);
});
this:
if(st>ot){
jQuery(href).addClass('posreached');
}
if (st>ot+eh) {
jQuery(href).removeClass('posreached');
};
even in the second state the first will be true, so u need to add:
if(st>ot || st<ot+eh){
jQuery(href).addClass('posreached');
}
give it a try
I used these for my conditions and it worked
if(st>ot){
jQuery(href).addClass('posreached');
}
if (st<ot) {
jQuery(href).removeClass('posreached');
}
if (st>ot+eh) {
jQuery(href).removeClass('posreached');
}

How to leave one active button in the JavaScript?

I am beginner.
I have four buttons and I want to leave one active button every time with expression operator (if). One button must have active every time .
I tried to do it something like that. I am open to your ideas, if you can do without (if) .Help me!
var count = 4;
var flag = true;
function select(currentColor, changeColor){
if(count > 1 && flag === true){
var currentElement = angular.element(document.getElementsByClassName(currentColor));
currentElement.toggleClass(changeColor);
count--;
console.log(count);
console.log('From minus: ' + count);
}else{
flag = false;
}
if(count < 4 && flag === false) {
var currentElement = angular.element(document.getElementsByClassName(currentColor));
currentElement.toggleClass(changeColor);
count++;
console.log(count);
console.log('From plus: ' + count);
}else{
flag = true;
}
}
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<style>
.changeColor{
color: red !important;
}
.first{
color: #07888A;
}
.second{
color: #07888A;
}
.third{
color: #07888A;
}
.fourth{
color: #07888A;
}
h1{
display: inline;
margin-right: 20px;
}
</style>
</head>
<body>
<h1 class="first" onClick="select('first', 'changeColor')">First</h1>
<h1 class="second" onClick="select('second', 'changeColor')">Second</h1>
<h1 class="third" onClick="select('third', 'changeColor')">Third</h1>
<h1 class="fourth" onClick="select('fourth', 'changeColor')">Fourth</h1>
</body>
</html>
Add this bit:
function select(currentColor, changeColor) {
// Get the list of the `.changeColor` elements.
changed = document.querySelectorAll(".changeColor");
// Loop through all the elements with `changeColor` class.
for (i = 0; i < changed.length; i++)
// Remove the changeColor class from their class list.
changed[i].classList.remove("changeColor");
// Rest comes your code.
if(count > 1 && flag === true){
are you trying to get one button disabled when any three buttons are enabled ? if so, perhaps this could help. I highly suggest not to use the h1 tags for this purpose, and use something like a button or div, and removing the onclick attributes from your elements and incorporate them in your main js file similar to the js snippet found below.
(function() {
//an empty array to track the number of elements currently colored
var numberOfElesColored = [],
eles = document.querySelectorAll('h1'),
//the number of active elements allowed at once
numberOfActiveElementsAllowed = eles.length - 1;
//loop though all the elements and attach click event
[].forEach.call(eles, function(ele, i) {
ele.addEventListener('click', function(event) {
var currentEle = event.target;
//is there at least two other elements avaliable still ?
if (!(numberOfElesColored.length === numberOfActiveElementsAllowed)) {
//yes
//is the current clicked element not active already ?
if (!currentEle.classList.contains('changeColor')) {
//yes
//add 1 to tracking array
numberOfElesColored.push(1);
//activate element
return currentEle.classList.add('changeColor');
} else {
//no
//remove 1 from tracking array
numberOfElesColored.pop();
//deactivate elements
return currentEle.classList.remove('changeColor');
}
//are all the elements active already ?
} else if (numberOfElesColored.length === numberOfActiveElementsAllowed) {
//yes
//is the current element an active one ?
if (currentEle.classList.contains('changeColor')) {
//yes
//remove 1 from tracking array
numberOfElesColored.pop();
//deactivate element
return currentEle.classList.remove('changeColor');
}
}
});
});
})();

Categories

Resources