Count hidden next-line items in a list - javascript

I have a list with inline list-items. I can see about 8 but then there is not enough space to show more and the rest appears on the next line, hidden (behind the container's z-index) because of its overflow:hidden. How can I count these next line items with JavaScript?
I know jQuery has $('li:visible') but I need a vanilla JavaScript way.
I've tried to leverage getComputedStyle(el) but there doesn't seem to be any way to target these hidden items.
There is also a complicated Page Visibility API but I don't think it can be used for elements, only the document visibility.
Here's a JSFiddle of the list items

For your situation, you need to id if the top of the LI is "under the fold" of the UL. For this we can use element.getBoundingClientRect():
function isVis(elm){
var box = elm.getBoundingClientRect(),
par = elm.parentNode.getBoundingClientRect();
return par.bottom-par.top>= box.top;
}
Live demo:
https://jsfiddle.net/ntcsh18g/
note this is NOT a general purpose "is the element hidden" routine, which is complicated and slow, rather its a simple check against the OP's property that's causing "hiddennes"...

Edit: As suggested by #dandavis, my solution was not generic at all (only works for one row of elements), so only use this in extremely specific and simple cases! To generalize this approach you could try and check whether the offsetTop of an element is greater than the height of the container or the offsetLeft is bigger than the width. Anyway, you are probably better off using his solution anyway. ;)
Well, depending on what you need this for, a very ugly, but working solution could be to check for elements that have a different offsetTop than the first element like this:
const children = [...document.querySelectorAll('#container li')]
const normalOffset = children[0].offsetTop
const overflownChildren = children.filter(li => li.offsetTop !== normalOffset)
https://jsfiddle.net/Isti115/nc3tahw3/

You'll need to target the ul element and save that into a variable var lists = document.getElementsByTagName("ul"); Then use for-loop something like below
<ul id="foo">
<li>First</li>
<li>Second</li>
<li>Third</li>
</ul>
var lists = document.getElementsByTagName("ul");
for (var i = 0; i < lists.length; ++i) {
// filter so that only lists with the class "foo" are counted
if (/(^|\\s)foo(\\s|$)/.test(lists[i].className)) {
var items = lists[i].getElementsByTagName("li");
for (var j = 0; j < items.length; ++j) {
// do something with items[j]
}
}
}

Related

Appending all page <table>'s to respective <div> with pure Javascript

I have a website with a lot of posts with <table> and a new responsive layout.
When big table elements (lot of content) are opened on mobile devices, even with max-width: 100% or other CSS tricks, they get bigger than the screen.
The solution is append tables in a overflow: scroll <div>, but I can't edit all the post content, so I want to do that with pure Javascript. jQuery not allowed.
The objetive is:
Get all <table> elements of the page that are inside #post
Get their content
Append that content in a <div class="table_div"> in the same place that the <table> is
Remove old table elements (the ones that are not inside .table_div)
I already can get all content from table elements and remove the tables, but the part of the code that supposedly should append table content in .table_div do not work.
Part of my code so far:
var post = document.getElementById('post'),
tables = post.getElementsByTagName('table');
for(var i=0; i<tables.length; i++) {
var table = tables[i];
var table_content = table.innerHTML,
table_div = document.createElement('div');
tables.remove();
table_div.appendChild(table);
table_div.className = 'table_div';
document.appendChild(table_div);
}
Full code: http://jsfiddle.net/hmaesta/6mambr93/
Please, help with just pure Javascript. Thank you.
NodeList has two mode; static or live (ref When is NodeList live and when is it static?). Live means that changes in the DOM are reflected in the NodeList. And NodeList from getElementsByTagName is live. In your code, the length of tables is decremented everytime you remove #post table.
There is some way to solve the problem.
Use static NodeList. For example, querySelectorAll returns static one.
Map NodeList to Array. You can do this with var ts = []; Array.prototype.push.apply(ts, tables);. And after this, use ts instead of tables.
Keep length and order of NodeList. Use insertChild instead of appendChild to keep order and to keep <table> in #post.
Here is example.
var tables = document.querySelectorAll('#post table');
for(var i = 0; i < tables.length; i++) {
var table = tables[i];
var table_div = document.createElement('div');
table.parentElement.insertBefore(table_div, table);
table_div.className = 'table_div';
table_div.appendChild(table);
}
https://jsfiddle.net/boy48ngs/2/
Note: You do not need to remove <table> manually in this case. Element cannot has multiple parents and appendChild removes the element from current parent.
See this updated fiddle:
var post = document.getElementById('post'),
tables = post.getElementsByTagName('table');
for(var i=0; i<tables.length; i++) {
var table = tables[i];
var elem = document.createElement('div');
elem.className = 'table_div';
post.replaceChild(elem, table);
elem.appendChild(table);
}
I used the replaceChild method to first replace the table element with a newly created div. Then I append the original table element to that div.

How do I use getclassbyelemenentnames?

So currently I have this bit of code:
function bgchanger(color){
document.getElementById("headwrapper").style.backgroundColor = color;
document.getElementById("footerwrapper").style.backgroundColor = color;
}
Accompanied by this HTML:
<div id="blue" style="cursor: pointer" onClick="bgchanger('blue')"></div>
It works well for changing the color of divs on click. However, it's notably inefficient. Rather than identifying two divs by Element Id, I should be calling them by a shared class. But I'm a little unclear on exactly how I would arrange a document.getElementsByClassName since it's apparently arranged differently than the ID method.
I assume you mean inefficient to code, since at runtime getElementById() will be fast. But as you imply, your JS would be neater if it could do this sort of thing by class rather than having a bunch of hard-coded ids. Indeed if your JS no longer needs to use ids you can omit them from your html too (unless they're used in your stylesheet).
So, getElementsByClassName() returns a list, so just loop through it:
function bgchanger(color){
var list = document.getElementsByClassName("someClass");
for (var i = 0; i < list.length; i++) {
list[i].style.backgroundColor = color;
}
}
Demo: http://jsfiddle.net/KNxBV/
Update with cookie monster's suggestion:
You can use querySelectorAll() instead of getElementsByClassName() - it has slightly better browser support:
var list = document.querySelectorAll(".someClass");
// other code as above. Note the . ---^
Updated demo: Demo: http://jsfiddle.net/KNxBV/1/

JS select child of this with specific class

I am writing a GreaseMonkey script that goes through a page with various elements and each element has text and a button. It uses document.getElementsByClassName to find the parent elements, and it has a for loop to do something to each parent element. In this loop, I need to select a child node with a specific class and find its text value (innerHTML). I can't figure out how to select the child with a specific class of this element.
You'll want to grab the currently iterated element and use querySelector()
For example:
var elements = document.getElementsByClassName('class');
for (var i = 0, len = elements.length; i < len; i++) {
var child = elements[i].querySelector('.class_of_desired_element');
//do stuff with child
}
Note the dot before the class name in querySelector as it works similar to jQuery.
Try querySelectorAll(), which you can use to find elements within the current element.
var parent = document.getElementsByClassName('parentClass'),
parent[0].querySelectorAll('.childClass');
Depending on exactly what you are looking to do, you could also skip selecting the parent, if you don't explicitly need a reference to it.
document.querySelectorAll('.parentClass .childClass');
https://developer.mozilla.org/en-US/docs/Web/API/Element.querySelectorAll
You can use
var yourelement = document.getElementsByClass("");
var i = yourelement.nextSibling;
var e = i.nextSibling;
and keep getting the nextSibling of the element till you get it.
However, like #teddy said in the comments, I would suggest you use jQuery. It has a MUCH easier way to do it:
var value = $('.parentClass .childClass').html();

Remove class on adding class to another element

I have a long list of image elements, each with it's own ID. I've already set it up so that when you click on one image, it will toggle a class "foo".
What I'd like to do is set it up so that when you click on another image, the first image's class "foo" will be removed. I'd like to do this with pure javascript if possible. Thanks.
Here's a fiddle: http://jsfiddle.net/q3aRC/
function clicked($id) {
document.getElementById($id).classList.toggle('foo');
}
I'd suggest, given that you're already using the classList api:
function clicked($id) {
// get all the elements with the class-name of 'foo':
var others = document.querySelectorAll('.foo');
// if there *are* any elements with that class-name:
if (others.length){
// iterate through that collection, removing the class-name:
for (var i = 0, len = others.length; i < len; i++){
others[i].classList.remove('foo');
}
}
/* add the class-name back (if it already had the class-name
we removed it in the previous section): */
document.getElementById($id).classList.add('foo');
}
JS Fiddle demo.
References:
document.querySelectorAll().
element.classList.
I would add a common class to all images and remove the foo class from all of them. Then I would add the class to the specific image
function clicked(id){
var images = document.getElementsByClassName('images');
for (var i = 0; i < images.length; ++i) {
images[i].classList.remove('foo');
}
document.getElementById(id).classList.add('foo');
}
Since you are already using classList I assume you're only catering to browsers new enough for addEventListener(), so I'd suggest removing all of the onclick attributes and doing something like this:
document.addEventListener('click',function(e){
if (e.target.tagName === "IMG") {
var imgs = document.getElementsByTagName('IMG');
for (var i = 0; i < imgs.length; i++)
if (imgs[i] != e.target)
imgs[i].classList.remove('foo');
e.target.classList.toggle('foo');
}
}, false);
Demo: http://jsfiddle.net/q3aRC/3/
That is, bind a single click handler to the document (or you could bind to a parent element of the images if they share a common parent), and then on click test if the clicked item is one of the elements you care about (i.e., an img) and go from there... The JS ends up about the same length, but the html ends up shorter and neater. You could actually remove the id attribute too if you weren't using it for anything other than your original clicked() function.
I used getElementsByTagName() just to show you yet another way of doing it, but getElementsByClassName() or querySelectorAll() (as in the other answers) are probably better options. But that's an easy switch to make.

finding elements with z-index

I was working on an issue in which I was looping through same controls placed on a single page and assigning a z-index to them.
I want to get a collection of all the elements which currently have the z index defined either directly in html or using css, and then iterate over them top to bottom and assign their z-index using JQuery.
What would the selector for this look like, and how performant would it be?
There is no specific selector to achieve this, so you would need to use filter() like this:
var zIndex = 5;
var $zElements = $('.selector').filter(function() {
return $(this).css('z-index') == zIndex;
});
$zElements.each(function() {
// loop through the elements with a matching z-index
});

Categories