If I wanted to hide all elements except for those within a <div id="content"> (including div#content itself), I could use the following CSS:
*
{
visibility: hidden !important;
}
div#content, div#content *
{
visibility: visible !important;
}
One thing to note about this solution is that the hidden elements still take up space. Unfortunately, not all elements have the same display attribute, so you cannot simple simply replace visibility above with display.
Using JavaScript, how can I set all elements to that are not within the <div id="#content"> 'family' to display: none?
A general purpose solution to change the style on the fewest objects, but make sure that #content and all it's sub-elements are visible requires an algorithm to traverse up from #content and hide all siblings at each level up without ever hiding an ancestor of #content. Because this starts at #content and goes up, it never hides any elements inside of #content.
function hideAllExcept(id) {
var el = document.getElementById(id);
while (el && el != document.body) {
// go one level up
var parent = el.parentNode;
// get siblings of our ancesotr
var siblings = parent.childNodes;
// loop through the siblings of our ancestor, but skip out actual ancestor
for (var i = 0, len = siblings.length; i < len; i++) {
if (siblings[i] != el && siblings[i].nodeType == 1) {
// only operate on element nodes
siblings[i].style.display = "none";
}
}
el = parent;
}
}
hideAllExcept("content");
Caveat: this first version does not hide text nodes that are siblings of an ancestor of #content (all other text nodes outside of #content are hidden because their parent is hidden). To hide those text nodes too, they would have to get wrapped in a <span> tag so the style could be set on the <span> tag, but I don't know if the OP needs that level of complexity or wants the text nodes wrapped in that way.
For completeness, here's a version that will wrap parent sibling text nodes so they can also be set to display: none. This may or may not be needed depending upon the source HTML:
function hideAllExcept(id) {
var el = document.getElementById(id);
while (el && el != document.body) {
// go one level up
var parent = el.parentNode;
// get siblings of our ancesotr
var siblings = parent.childNodes;
// loop through the siblings of our ancestor, but skip out actual ancestor
for (var i = 0, len = siblings.length; i < len; i++) {
var node = siblings[i];
if (node != el) {
if (node.nodeType == 1) {
// only operate on element nodes
node.style.display = "none";
} else if (node.nodeType == 3) {
// wrap text node in span object so we can hide it
var span = document.createElement("span");
span.style.display = "none";
span.className = "xwrap";
node.parentNode.insertBefore(span, node);
// Be wary of the dynamic siblings nodeList changing
// when we add nodes.
// It actually works here because we add one
// and remove one so the nodeList stays constant.
span.appendChild(node);
}
}
}
el = parent;
}
}
hideAllExcept("content");
And a working demo: http://jsfiddle.net/jfriend00/yVWDx/
Try this
var al = document.body.getElementsByTagName("*");
for(var i =0;i<al.length;i++)
{
var elm = al[i];
if(elm.parentNode.id != 'content') {
elm.style.display = 'none';
}
}
Related
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
);
How can this jQuery-dependent code
$('.myElement').click(function () {
drawMode = !drawMode;
$icon = $(this).children('i');
if (drawMode) {
$icon.removeClass('a').addClass('b');
} else {
$icon.removeClass('b').addClass('a');
}
});
be rewritten into native javascript?
I have tried
var element = document.getElementsByClassName('myElement')[0];
element.addEventListener('click', function (e) {
drawMode = !drawMode;
var icon = this.children()[0];
if (drawMode) {
icon.classList.remove('a').add('b');
} else {
icon.classList.remove('b').add('a');
}
});
but I cannot find the children element correctly.
jQuery's children allows you to filter by selector, something that isn't in the DOM API (you can find all descendants matching a given CSS selector, but you can't [for now] limit it to just children).
If it doesn't matter whether it's a child or just any descendant, then:
var icon = this.querySelector("i");
That finds the first descendant within the element that's an i element. I suspect that would work just fine for you. The only time it might not would be if you had this:
<div class="myElement">
<span>
<i>You DON'T want this one</i>
</span>
<i>You do want this one</i>
</div>
If that's the case and you need to only look at children, not all descendants, you'll need a loop:
var icon = null;
var n;
for (n = 0; n < !icon && this.children.length; ++n) {
if (this.children[n].tagName.toLowerCase() === "i") {
icon = this.children[n];
}
}
In ES2015+ (you can transpile to use it today), that's so much tidier:
let icon = Array.from(this.children)
.find(child => child.tagName.toLowerCase() === "i");
A few notes:
The add and remove functions of the classList do not return the classList object, so you can't concatenate them (e.add().remove(), like you are used to do in jQuery).
In your code you only go over the first element, while when using jQuery the changes are made for all elements that you selected.
I used the querySelectorAll and filtered out elements that are not direct childs, (checked for the parentElement since you used the children() function of jQuery).
Here is an example:
drawMode = true;
var elements = document.getElementsByClassName('myElement');
for (var i = 0; i < elements.length; i++) {
elements[i].addEventListener('click', function() {
var that = this;
drawMode = !drawMode;
var icons = this.querySelectorAll('i');
for (var j = 0; j < icons.length; j++) {
var icon = icons[j];
if (icon.parentElement != that) {
continue;
}
if (drawMode) {
icon.classList.remove('a');
icon.classList.add('b');
} else {
icon.classList.remove('b')
icon.classList.add('a');
}
}
});
}
i.a {
background: red;
}
i.b {
background: blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="myElement">
<i>asd</i><br />
<i>fgh</i><br />
<span><i>This element will not change because he isn't a direct child</i></span><br />
</div>
Generally, document.querySelectorAll is very useful when converting jQuery to vanilla javascript.
Returns a list of the elements within the document (using depth-first pre-order traversal of the document's nodes) that match the specified group of selectors. The object returned is a NodeList.
// get a NodeList of elements which match CSS Selector '.myElement'
var elements = document.querySelectorAll('.myElement');
for (var i = 0; i < elements.length; i++ ) {
// loop through every element with class 'myElement'
var element = elements[i];
element.addEventListener('click', function(event) {
drawMode = !drawMode;
var icon = element.querySelector('i');
if (drawMode) {
icon.classList.remove('a');
icon.classList.add('b');
} else {
icon.classList.remove('b');
icon.classList.add('a');
}
});
}
Note I've also used element.querySelector to match descendants of the currently processed element.
I wrote a function to insert divs dynamically into the web page. but then I have to remove these divs. While adding the divs I used a classname to all the divs. So I got all the elements from the dom using getElementsByClassName() and looping through all these elements and deleting the divs. My code is not removing all the divs that were created earlier.
Please find my code snippet:
var elements = document.getElementsByClassName("xxxx");
for (var i = 0; i < elements .length; i++) {
var element = elements[i];
if (element && element.hasChildNodes()) {
var parent_node = element.parentNode;
while(element.firstChild) {
parent_node.insertBefore(element.firstChild, element);
}
parent_node.removeChild(element);
}
elements = document.getElementsByClassName("xxxx");
}
You need to remove the last line "elements = document.getElementsByClassName("xxxx");" - that line will cause you to miss out every other element as you go around the loop.
If all you want is to remove all DIV elements with that class, you can use this code:
var elements = document.getElementsByClassName("xxxx");
while ( elements[0] ) {
elements[0].parentNode.removeChild(elements[0]);
}
It will remove your DIVs and their child nodes as well.
When I click on All, I want to get all the li elements in different rows of table. Here I get all the li elements of one row.
<ul class="clsInfo">
<li id="liAll" onclick="Activelink(this);">
<a href="#">
<label id="lblAll">All</label>
</a>
</li>
</ul>
Code:
function Activelink(sender) {
var a_elements = sender.parentNode.getElementsByTagName("a");
for (var i = 0, len = a_elements.length; i < len; i++) {
a_elements[i].style.color = 'black';
}
}
You describe li elements and tables, but your code is setting the properties of a (anchor) tags. If your "All" link is nested in a table that we can't see in the html sample, then you need to find it with a loop:
function findParentalTable(el) {
var current = el;
while((current !== document) && (current.tagName != "TABLE")) {
current = current.parentNode;
}
return current;
}
Then your code would be
function Activelink(sender) {
var parentalTable = findParentalTable(sender);
var a_elements = parentalTable.getElementsByTagName("a");
for (var i = 0, len = a_elements.length; i < len; i++) {
a_elements[i].style.color = 'black';
}
}
The parent node of an element is another DOM element, and all DOM elements have the property tagName, which is the uppercase tag string, like TABLE. The while loop is just walking up the DOM tree looking for a <table> tag. As soon as it finds a table tag, the current.tagName will be TABLE and the condition will fail, permitting the function to return the element. Just in case there is no table element above the element passed to findParentalTable, I also check that we haven't reached the top of the DOM, the document node; !== checks for an exact reference match (generally good practice to use !== and ===).
I have a series of p tags on my page and I want to wrap them all into a container, e.g.
<p>foo</p>
<p>bar</p>
<p>baz</p>
I want to wrap all the above tags into a container as follows:
<div>
<p>foo</p>
<p>bar</p>
<p>baz</p>
</div>
How to wrap a NodeList in an element using vanilla JavaScript?
Posted below are a pure JavaScript version of jQuery's wrap and wrapAll methods. I can't guarantee they work exactly as they do in jQuery, but they do in fact work very similarly and should be able to accomplish the same tasks. They work with either a single HTMLElement or an array of them. I haven't tested to confirm, but they should both work in all modern browsers (and older ones to a certain extent).
Unlike the selected answer, these methods maintain the correct HTML structure by using insertBefore as well as appendChild.
wrap:
// Wrap an HTMLElement around each element in an HTMLElement array.
HTMLElement.prototype.wrap = function(elms) {
// Convert `elms` to an array, if necessary.
if (!elms.length) elms = [elms];
// Loops backwards to prevent having to clone the wrapper on the
// first element (see `child` below).
for (var i = elms.length - 1; i >= 0; i--) {
var child = (i > 0) ? this.cloneNode(true) : this;
var el = elms[i];
// Cache the current parent and sibling.
var parent = el.parentNode;
var sibling = el.nextSibling;
// Wrap the element (is automatically removed from its current
// parent).
child.appendChild(el);
// If the element had a sibling, insert the wrapper before
// the sibling to maintain the HTML structure; otherwise, just
// append it to the parent.
if (sibling) {
parent.insertBefore(child, sibling);
} else {
parent.appendChild(child);
}
}
};
See a working demo on jsFiddle.
wrapAll:
// Wrap an HTMLElement around another HTMLElement or an array of them.
HTMLElement.prototype.wrapAll = function(elms) {
var el = elms.length ? elms[0] : elms;
// Cache the current parent and sibling of the first element.
var parent = el.parentNode;
var sibling = el.nextSibling;
// Wrap the first element (is automatically removed from its
// current parent).
this.appendChild(el);
// Wrap all other elements (if applicable). Each element is
// automatically removed from its current parent and from the elms
// array.
while (elms.length) {
this.appendChild(elms[0]);
}
// If the first element had a sibling, insert the wrapper before the
// sibling to maintain the HTML structure; otherwise, just append it
// to the parent.
if (sibling) {
parent.insertBefore(this, sibling);
} else {
parent.appendChild(this);
}
};
See a working demo on jsFiddle.
You can do like this:
// create the container div
var dv = document.createElement('div');
// get all divs
var divs = document.getElementsByTagName('div');
// get the body element
var body = document.getElementsByTagName('body')[0];
// apply class to container div
dv.setAttribute('class', 'container');
// find out all those divs having class C
for(var i = 0; i < divs.length; i++)
{
if (divs[i].getAttribute('class') === 'C')
{
// put the divs having class C inside container div
dv.appendChild(divs[i]);
}
}
// finally append the container div to body
body.appendChild(dv);
I arrived at this wrapAll function by starting with Kevin's answer and fixing the problems presented below as well as those mentioned in the comments below his answer.
His function attempts to append the wrapper to the next sibling of the first node in the passed nodeList. That will be problematic if that node is also in the nodeList. To see this in action, remove all the text and other elements from between the first and second <li> in his wrapAll demo.
Contrary to the claim, his function won't work if multiple nodes are passed in an array rather than a nodeList because of the looping technique used.
These are fixed below:
// Wrap wrapper around nodes
// Just pass a collection of nodes, and a wrapper element
function wrapAll(nodes, wrapper) {
// Cache the current parent and previous sibling of the first node.
var parent = nodes[0].parentNode;
var previousSibling = nodes[0].previousSibling;
// Place each node in wrapper.
// - If nodes is an array, we must increment the index we grab from
// after each loop.
// - If nodes is a NodeList, each node is automatically removed from
// the NodeList when it is removed from its parent with appendChild.
for (var i = 0; nodes.length - i; wrapper.firstChild === nodes[0] && i++) {
wrapper.appendChild(nodes[i]);
}
// Place the wrapper just after the cached previousSibling,
// or if that is null, just before the first child.
var nextSibling = previousSibling ? previousSibling.nextSibling : parent.firstChild;
parent.insertBefore(wrapper, nextSibling);
return wrapper;
}
See the Demo and GitHub Gist.
Here's my javascript version of wrap(). Shorter but you have to create the element before calling the function.
HTMLElement.prototype.wrap = function(wrapper){
this.parentNode.insertBefore(wrapper, this);
wrapper.appendChild(this);
}
function wrapDiv(){
var wrapper = document.createElement('div'); // create the wrapper
wrapper.style.background = "#0cf"; // add style if you want
var element = document.getElementById('elementID'); // get element to wrap
element.wrap(wrapper);
}
div {
border: 2px solid #f00;
margin-bottom: 10px;
}
<ul id="elementID">
<li>Chair</li>
<li>Sofa</li>
</ul>
<button onclick="wrapDiv()">Wrap the list</button>
If you're target browsers support it, the document.querySelectorAll uses CSS selectors:
var targets = document.querySelectorAll('.c'),
head = document.querySelectorAll('body')[0],
cont = document.createElement('div');
cont.className = "container";
for (var x=0, y=targets.length; x<y; x++){
con.appendChild(targets[x]);
}
head.appendChild(cont);
Taking #Rixius 's answer a step further, you could turn it into a forEach loop with an arrow function
let parent = document.querySelector('div');
let children = parent.querySelectorAll('*');
let wrapper = document.createElement('section');
wrapper.className = "wrapper";
children.forEach((child) => {
wrapper.appendChild(child);
});
parent.appendChild(wrapper);
* { margin: 0; padding: 0; box-sizing: border-box; font-family: roboto; }
body { padding: 5vw; }
span,i,b { display: block; }
div { border: 1px solid lime; margin: 1rem; }
section { border: 1px solid red; margin: 1rem; }
<div>
<span>span</span>
<i>italic</i>
<b>bold</b>
</div>