javascript count visible elements - javascript

I have been searching the forums but unable find any specific help in relation to my problem. I am trying to design an application using javascript which will count div elements which are either hidden or visible.
I am using
document.getElementById("div-element").childElementCount;
and could use something like:
document.querySelectorAll('#div-element .dic-class').length;
both of which work as intended by returning the total elements.
I am changing the visibility of specific div elements by:
document.getElementById('div-element').style.display == "block or none";

For something like this Array.prototype.filter comes to mind (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter), but you can't use that on a nodelist, which is what you get when using querySelectorAll, so I would solve this by converting the nodelist to an array, then use filter on that.
To convert a nodelist to an array:
var array = [].slice.call(someNodeList);
(https://davidwalsh.name/nodelist-array)
So, we can do this:
//gives node list
divs = document.querySelectorAll('#div-element > div');
//convert to an array
var divsArray = [].slice.call(divs);
//so now we can use filter
//find all divs with display none
var displayNone = divsArray.filter(function(el) {
return getComputedStyle(el).display === "none"
});
//and all divs that are not display none
var displayShow = divsArray.filter(function(el) {
return getComputedStyle(el).display !== "none"
});
//and use length to count
var numberOfHiddenDivs = displayNone.length;
var numberOfVisibleDivs = displayShow.length;
Just a note: its important to use getComputedStyle (https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle) rather than element.style because element.style will not reflect properties applied by css, it will only reflect inline styles.
Second note, this only counts "display:none;" as hidden, if you also want to include visibility:hidden; or some other criteria, use
var displayNone = divsArray.filter(function(el) {
return getComputedStyle(el).display === "none" ||
getComputedStyle(el).visibility === "hidden"
});
//and all divs that are not display none
var displayShow = divsArray.filter(function(el) {
return !(getComputedStyle(el).display === "none" ||
getComputedStyle(el).visibility === "hidden")
});
Demo:
//gives node list
divs = document.querySelectorAll('#div-element > div');
//convert to an array
var divsArray = [].slice.call(divs);
//so now we can use filter
//find all divs with display none
var displayNone = divsArray.filter(function(el) {
return getComputedStyle(el).display === "none"
});
//and all divs that are not display none
var displayShow = divsArray.filter(function(el) {
return getComputedStyle(el).display !== "none"
});
//and use length to count
var numberOfHiddenDivs = displayNone.length;
var numberOfVisibleDivs = displayShow.length;
alert("hidden:"+numberOfHiddenDivs+", visible:"+numberOfVisibleDivs);
#div-element > div{
width:100px;
height:50px;
border:1px solid gold;
background-color:red;
}
div.hide{
display:none;
}
<div id='div-element'>
<div class=hide></div>
<div class=hide></div>
<div class=hide></div>
<div></div>
<div></div>
<div></div>
<div class=hide></div>
<div></div>
<div class=hide></div>
<div></div>
<div style=display:none;></div>
<div style=display:none;></div>
</div>

While you are changing the visibility of specific div elements by document.getElementById('div-element').style.display = "none"; the count of visible and hidden elements can be easily performed by checking a "generated" style display attribute:
var countHidden = document.querySelectorAll("#div-element .dic-class[style='display: none;']").length;
var countVisible = document.querySelectorAll("#div-element .dic-class:not([style='display: none;'])").length;

Loop the items and then you can check the style by checking offsetParent , then push to an array.
var elem = document.querySelectorAll('#div-element .dic-class');
var visible = [];
for (i = 0; i < elem.length; i++) {
_this = elem[i];
if (_this.offsetParent !== null)
visible.push(elem[i]);
}
alert(visible.length);
#div-element {
display: none;
}
<div id="div-element">
<div class="dic-class"></div>
</div>

You can convert the NodeList into an array and then apply reduce to get the number of elements that don't have the display set to none inline:
var divs = Array.prototype.slice.call(document.querySelectorAll('#div-element .dic-class'));
console.log(divs.reduce(function(a, b){return a + (b.style.display != "none" ? 1 : 0)}, 0))
.dic-class {
display: inline-block;
margin: 5px;
height: 50px;
width: 50px;
background: green;
border: 1px solid black;
}
<div id="div-element">
<div class="dic-class"></div>
<div class="dic-class"></div>
<div class="dic-class" style="display: none"></div>
<div class="dic-class" style="display: none"></div>
<div class="dic-class"></div>
</div>
If you want to check for applies rules (not just inline ones), you can use getComputedStyle instead.

This is my first time using this forum and wow such good and fast replies.
I ended up using ChillNUT's code and the end result is this:
function IconCount() {
divs = document.querySelectorAll('#IconFX > div');
var divsArray = [].slice.call(divs);
var displayNone = divsArray.filter(function(el) {
return getComputedStyle(el).display === "none"
});
var displayShow = divsArray.filter(function(el) {
return getComputedStyle(el).display !== "none"
});
var numberOfHiddenDivs = displayNone.length;
var numberOfVisibleDivs = displayShow.length;
document.getElementById('VisibleIcons').innerHTML = numberOfVisibleDivs;
document.getElementById('HiddenIcons').innerHTML = numberOfHiddenDivs;
}
This way I can call this as a function when anything is hidden or visible and can interact with these output values with another script.

Related

Count children elements with inline style

I'm stuck on how to get number of all elements that has inline style.
var galleryElements = document.getElementsByClassName("popup-gallery")[0].children;
$('.myButton').click(function() {
var totalItems = galleryElements.length;
var itemsWithStyle = ($(galleryElements).css('display') == 'inline').length;
if (totalItems == itemsWithStyle){
/* do something */
}
});
Say I have a NodeList of the children of some div, i.e.
const div = document.querySelector('#target');
const children = div.children;
If I want to get the number of children with the display attribute set to inline, then I can iterate through children and increment a counter each time I encounter a child element with the display attribute set to inline.
For example:
let count = 0;
for (let child of children) {
if (child.style.display === 'inline') count++;
}
Where count represents the number of child elements of the parent div that have a display attribute set to inline.
this is without jquery
You can check this out for starters:
https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle
basic
<h1 id="hello">index.html</h1>
<-- css -->
#hello {display: inline;}
// js
const title = document.getElementById('hello');
let res = window.getComputedStyle(title)
let count = 0;
if(res.getPropertyValue('display') === 'inline') {
console.log('yes')
count ++;
console.log(count)
// 1
}
what I learned:
" The object from getComputedStyle is read-only, and should be used to inspect the element's style — including those set by a element or an external stylesheet.
The element.style object should be used to set styles on that element, or inspect styles directly added to it from JavaScript manipulation or the global style attribute."
You are in fact checking if all gallery elements are display: inline, so you can simplify your code a bit:
const $galleryElements = $('.popup-gallery').first().children();
$('.myButton').click(function() {
const areAllInline = $galleryElements.filter(function () {
$(this).css('display') !== 'inline';
}).length === 0;
if (areAllInline) {
/* do something */
}
});
If there are no elements with display different than inline, you're good to go.
This will give you a tally of both inline (including inline-block) and other. If there is a class associated with the element, it will report back that as well.
$('.myButton').click(function() {
let inline = [], block = []
$('.popup-gallery *').each(function() {
let tg = $(this).prop('tagName').toLowerCase()
if ($(this).attr('class')) tg += "." + $(this).attr('class');
if (['inline','inline-block'].includes($(this).css('display'))) inline.push(tg)
else block.push(tg)
})
console.log('inline',inline)
console.log('not-inline',block)
console.log('inline/total',inline.length + "/" + (inline.length + block.length))
console.log('has inline elements?',inline.length>0)
});
.special{
display:inline;
}
.special-ib{
display:inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class='popup-gallery'>
<div>A div
<ul>
<li>li list - <i>italics...</i></li>
</ul>
</div>
<div class='special'>inline div</div>
<div class='special-ib'>inline-block div</div>
<p>paragraph</p>
<span>span, <dd>dd</dd></span>
</div>
<button class='myButton'>Click me </button>
These answers are good, however I feel it is even easier than how others are solving this problem.
console.log(
[...document.querySelectorAll(".popup-gallery")]
.filter(element =>
"inline" ==
window.getComputedStyle(element)
.getPropertyValue("display")
).length
);

Is there a way to check if 2 array elements match in javascript?

I have an array in which list elements get pushes when I click on them. After 2 elements are inside of it I need a way to check them against each other and see if they are exactly the same. I've tried the basic if(array[0] === array[1]) but that simply returns true (they match) no matter if array[0] = li.whatever and array[1] = li.notTheSame .
var $card1 = $clicked[0];
var $card2 = $clicked[1];
function checkMatch(){
if ($clicked.length === 2){
if ($card1 === $card2){
matched();
}else {
$clicked.length = 0;
$('.card').removeClass('open show');
console.log('NOT A MATCH!')
};
}else{
};
};
You seem you just want to compare two values.
So, I've made a really basic function basing on what you explained on your code. Is this right one for you?
var div = document.querySelector('div');
var cards = [];
div.addEventListener('click', function(e){
e.stopPropagation();
e.target.style.background = 'green';
cards.push(e.target.textContent);
checkMatch();
});
function checkMatch(){
if(cards.length <= 1){
return;
}
if(cards[0] === cards[1]){
alert('Same');
}else{
alert('Different');
}
}
div{
width:500px;
}
li{
width: 100px;
height: 100px;
background: #E7F3F7;
list-style-type:none;
text-align: center;
display:inline-block;
margin:10px;
}
<div>
<li>Cat</li>
<li>Cow</li>
<li>Bird</li>
<li>Bird</li>
<li>Dog</li>
<li>Cat</li>
<li>Dog</li>
<li>Cow</li>
</div>
I found the answer from another question I posted.
Basically:
let firstCard = array[0];
let secondCard = array[1];
And I changed the "if" that checks the cards to this:
if(firstCard.children().attr('class') === secondCard.children().attr('class')){
"Do something"
}
And it works perfectly. It doesn't find them all as a match anymore!

How to sequentially .append() created elements to the previously added element

The code
JavaScript:
var recurringF = (function(){
this.$el = $("#target");
this.arg = arguments[0];
this.spl = (!_.isEmpty(this.arg)) ? this.arg.split(" ") : false;
if(this.spl){
for(var i=0;i<this.spl.length;i++){
if(i===0){
this.$el.append(document.createElement(this.spl[i]));
}else{
this.$el.children().last().append(document.createElement(this.spl[i]));
}
}
}
return {
"$":this.$el
}
});
var t = new recurringF("div h1 span");
HTML-Body:
<body>
<div id="target"></div>
</body>
The Goal
I'd like to append elements sequentially to an parent element $("#target") so that the end result in the HTML is the following:
<body>
<div id="target">
<div>
<h1>
<span></span>
</h1>
</div>
</div>
</body>
The loop does not append the created elements to the the last appended element, but to the in loop cycle 1 created element 'div' like the following:
<div id="target">
<div>
<h1></h1>
<span></span>
</div>
</div>
What am I missing?
By using .children(), you'll only get the immediate div on every iteration after the first, thus resulting in
<div id="target">
<div>
<h1></h1>
<span></span>
<alltherest></alltherest>
</div>
</div>
because .children only looks at children, not all descendants. What you want is .find(*) so that it will get the deepest nested descendant on each iteration.
this.$el.find('*').last().append(document.createElement(this.spl[i]));
https://jsfiddle.net/f3fb997h/
That said, it would be better if you just stored a reference to the last created element and append to it, rather than having to reselect it every iteration.
var $tempEl = this.$el, newEl;
if(this.spl){
for(var i=0;i<this.spl.length;i++){
newEl = document.createElement(this.spl[i]);
$tempEl.append(newEl);
$tempEl = $(newEl);
}
}
https://jsfiddle.net/f3fb997h/1/
Note that at this point you're not really benefiting from jQuery at all, so a small tweak and you're not depending on it.
var recurringF = (function(){
this.el = document.getElementById('target');
this.arg = arguments[0];
this.spl = (!_.isEmpty(this.arg)) ? this.arg.split(" ") : false;
console.log(this.spl);
var tempEl = this.el, newEl;
if(this.spl){
for(var i=0;i<this.spl.length;i++){
newEl = document.createElement(this.spl[i]);
tempEl.appendChild(newEl);
tempEl = newEl;
}
}
return {
"el":this.el
}
});
You can try using regular javascript functionality, as it has child appending built in:
const recurseElement = (elementString, target) => {
const elements = elementString.split(' ');
elements.forEach(function(ele) {
const domElement = document.createElement(ele); // create the element
target.appendChild(domElement); // append to the target
target = domElement; // this element is the new target
});
}
So now you can use it like so:
recurseElement('div h1 span', document.getElementById('target'));
const recurseElement = (elementString, target) => {
const elements = elementString.split(' ');
elements.forEach(function(ele) {
const domElement = document.createElement(ele); // create the element
target.appendChild(domElement); // append to the target
target = domElement; // this element is the new target
});
};
recurseElement('div h1 span', document.getElementById('target'));
#target div {
background: green;
height: 16px; width: 128px; padding: 10px;
}
#target div h1 {
background: red;
height: 16px; width: 64px; padding: 10px;
}
#target div h1 span {
background: purple; display: block;
height: 16px; width: 32px; padding: 10px;
}
<div id="target"></div>
It should be noted that arrow functions are available for Chrome 45+, Firefox 22.0+, Edge, and Opera. They do not work in IE or Safari. Or they will work if you have a transpiler (like babel)

Get an element id and generic element in JavaScript

I have a CSS selector #menu li {background-color: red;}.
I want to access its attributes in JavaScript. It's important that I need to access both #menu and li since #menu alone has different attributes. It seems like getElementById(menu li), QuerySelector and getComputedStyle are not working in this case.
Is there any other way to achieve that or am I missing something here?
You should use jQuery for this, here the easy code example
//html
<div id="menu" data-number="123" >
</div>
//jquery
var menu = $('#menu').attr('data-number');
console.log(menu);
//print 123
jquery version
https://jsfiddle.net/pn52uvw1/
$(".button_1").click(function(){
alert($("#menu").attr("data-item-id"));
})
$(".button_2").click(function(){
alert($("#menu li").attr("data-item-id"));
})
non jquery version
https://jsfiddle.net/pn52uvw1/2/
window.firstFunction = function(){
var target = document.getElementById("menu");
alert(target.getAttribute('data-item-id'));
}
window.secondFunction = function(){
var target = document.getElementById("menu").children[0];
alert(target.getAttribute('data-item-id'));
}
but you will need to get rid of that [0] index probably, and use a for or something for multiple li items
If you want to get that css rule's property, you can do like this:
function getStyleFromSelector(selector, styleName) {
// Get all style elements
var styles = document.styleSheets;
var styleIndex = 0, styleCount = styles.length;
var rules, ruleCount, ruleIndex;
// Iterate though styles
for (styleIndex = 0; styleIndex < styleCount; ++styleIndex) {
// Get the css rules under the style.
rules = styles[styleIndex].rules;
ruleCount = rules.length;
for (ruleIndex = 0; ruleIndex < ruleCount; ++ruleIndex) {
// Check if the selector match the one we want
if (rules[ruleIndex].selectorText === selector) {
return styleName ?
rules[ruleIndex].style.getPropertyValue(styleName) : rules[ruleIndex];
}
}
}
}
var div = document.getElementById("results");
var result = getStyleFromSelector('#menu li');
console.log(result);
div.innerHTML = 'background-color is : ' + result.style.backgroundColor;
console.log(getStyleFromSelector('#menu li', 'background-color'));
#menu li {background-color: red;}
<div id="results"></div>
You can try it without additional Libraries with the following
var len = document.querySelectorAll("#menu li").length;
for(i = 0; i<len; i++)
document.querySelectorAll("#menu li")[i].style.backgroundColor="blue";
I also made you (a not very beautiful) jsfiddle
https://jsfiddle.net/gmeewsmz/

How to bring the selected div on top of all other div's

I have a bunch of divs on the screen.
What I want to do is, when I select any div, I want its zIndex to be always higher than all other divs.
In my application, i will need to do this repeatedly whenever I select a div, put it on top of others.
Is this possible using Javascript?
In some cases, you can bring an element to top without using CSS directly. If you place your DIVs to the body tag or under another parent element, you can re-append itself to the parent. JavaScript will move it to the last position in the child list which will bring the element in a "natural" way to the top.
Example:
myElement.parentNode.appendChild(myElement);
I used this trick for my custom context menu. Works without zIndex.
Yes, very much so.
HTML:
<div>foo</div>
<div>bar</div>
<div>bas</div>
Javascript:
//Collect all divs in array, then work on them
var all = document.selectElementsByTagName("div");
var prev = false;
for(i = 0; i < all.length; i++) {
all[i].onclick = function() {
all[i].style.position = 'relative'; //necessary to use z-index
if (prev) { prev.style.zIndex = 1; }
this.style.zIndex = 1000;
prev = this;
}
}
}
//Edit: Sorry, forgot a brace. Corrected now.
I recommend setting a variable at the beginning and incrementing that.
var top_z = 10;
function bringToTop(element)
{
element.style.zIndex = ++top_z;
}
(++top_z increments and then returns top_z)
I would do something like that:
var my_index = 100; //global var on page
function sendontop(div_id) {
if (typeOf(div_id)=="string") {
div_id=document.getElementById(div_id);
}
div_id.style.zIndex = my_index++;
}
on the object
<div id="bringmeup" onclick="sendontop(this);" >somecontent</div>
or otherwise you can also use
sendontop("bringmeup");
i came across this problem and solved like that
Hope it helps!
document.getElementById("mainactiondiv").firstElementChild.appendChild(bubble);
This worked for me (a modified version of Ben's solution, because it generated so many errors).
When you click the element, it should go to the top.
Hope it helps!
var allDivs = document.getElementsByTagName("div"); //Changed variable name to 'allDivs' because the name 'all' generated an error.
var prev = false;
for(ii = 0; ii < allDivs.length; ii++) { // changed the for variable to 'ii' instead of 'i' so i can find it easier (searching for 'i' will return avery instance of the letter i, even inside words, not just the variable, whereas ii is unique).
allDivs[ii].onclick = function() {
this.style.position = 'absolute'; //You have to have a position type defined. It doesn't matter what type, you just have to. If you define it elsewhere in your styles you can remove this.
if (prev) { prev.style.zIndex = 1; }
this.style.zIndex = 1000;
prev = this;
}
}
div {
padding:20px;
border:2px solid black;
border-radius:4px;
}
#d4 {
background-color:lightblue;
position:absolute;
top:30px;
left:20px;
}
#d3 {
background-color:lightgreen;
position:absolute;
top:70px;
left:20px;
}
#d2 {
background-color:yellow;
position:absolute;
top:30px;
left:70px;
}
#d1 {
background-color:pink;
position:absolute;
top:70px;
left:70px;
}
<html>
<div id="d1">div1</div>
<div id="d2">div2</div>
<div id="d3">div3</div>
<div id="d4">div4</div>
</html>
This is what worked for me. It traverses all elements on the page and increases the max z-index by 1.
Note that this method still works when there have been dynamically increased z indices on the page as well:
function bring_to_front(target_id) {
const all_z = [];
document.querySelectorAll("*").forEach(function(elem) {
all_z.push(elem.style.zIndex)
})
const max_index = Math.max.apply(null, all_z.map((x) => Number(x)));
const new_max_index = max_index + 1;
document.getElementById(target_id).style.zIndex = new_max_index;
}
Simply pass-in the id of the element you wish to bring to the front.

Categories