Remove hidden elements from cloned element - javascript

There are some elements that are hidden in this web page. Now if I want to find the hidden elements:
var node = jQuery('body')[0];
$(node).find(":hidden").remove();
This removes the hidden elements from the main node (which further changes the layout of the page). What I want to do is to copy(clone) the elements which are not hidden. For which I am trying this:
var clone = node.cloneNode(true);
$(clone).find(":hidden").remove();
But this removes all the elements inside the clone and not just the hidden elements (as expected, since its not in the dom). What's the best possible way to remove hidden elements from the clone.

I assume the issue is that until your clone is re-inserted into the DOM, then all of it is being considered hidden.
Maybe you could mark the hidden elements for removal first, then clone and then remove the marked elements:
var $node = ... ; // jQuery object of node to be cloned
$node.find(':hidden').addClass('markedForRemoval');
var $clone = $node.clone();
$clone.find('.markedForRemoval').remove();
// tidy up:
$clone.find('.markedForRemoval').removeClass('markedForRemoval');
$node.find('.markedForRemoval').removeClass('markedForRemoval');
demo: http://jsfiddle.net/BYossarian/6ysq8/

Sometimes the :visible selector will not be enough, and you will also want a selector for styles with height:0px, since display:none; and height:0px; are not equivalent.
Before cloning, we need to mark the elements as visible or invisible, because once cloned, the clone is in a variable, but not on the page, so everything inside it will qualify as :hidden. (Bonus: Let's make this as efficient as possible, by not hijacking the class or id fields, and instead using a custom data-attribute.)
Identify truly hidden elements:
$(node).find(':hidden').attr('data-hidden', 'true');
$(node).find('
*[style*="height:0px"],
*[style*="height: 0px"]
').attr('data-hidden', 'true');
Deep clone the node:
var clone = node.clone(true, true);
Remove hidden elements:
clone.find('*[data-hidden="true"]').remove();

I would stick to jQuery clone. My method is kinda rough, but it works.
<div class="bla" >
<span class="hidden">hidden</span>
<span class="hidden">hidden</span>
<span class="hidden">hidden</span>
<span > visible </span>
</div>
So, first, clone the parent.
var a = $('.bla').clone()
Then clone the visible children.
var b = $('.bla > :visible').clone() ;
Then add them to each other.
a.html(b)
The whole thing will be like so:
var a = $('.bla').clone()
var b = $('.bla > :visible').clone() ;
a.html(b)
Here is an example : http://jsfiddle.net/4Dky9/1/

var clone = node.cloneNode(true);
var hiddenElements = clone.querySelectorAll('.hidden'); // if hidden elements are applied the css class hidden
for(var i = 0; i < hiddenElements.length; i++){
clone.removeChild(hiddenElements[i])
}
If there is no hidden class, iterate through all the child elements and check for the display property.
var children = clone.childNodes;
for(var i = 0; i < children.length; i++){
if(children[i].style && children[i].style.display == 'none'){
clone.removeChild(children[i]);
}
}

try using :
$(clone).children(':hidden').remove();

Related

Unable to show mCustomScrollBar on dynamically added elements

I have a div <div id="MetricsTypeYearModelList"></div> . In this div i am dynamically adding a ul element
$("#MetricsTypeYearModelList").append('<ul class="modal__list mCustomScrollbar" id="MetricsTypeYearModelListUl"></ul>');
After this i am looping over a JSON object and adding li element dynamically to the ul element
for (var i = 0; i < metricsTypeYearModel.length; i++)
{
var obj = metricsTypeYearModel[i];
$("#MetricsTypeYearModelListUl").append('<li data-name='+obj.ModelTypeName+' data-value='+obj.ModelTypeID+' data-id='+obj.ModelTypeID+' class="pModel"> '+obj.ModelTypeName+'</li>');
}
I have used "mCustomScrollbar" class in my ul element but this does not show up, normal scroll bar does show up. How can i show the CustomScrollBar
You can set the live configuration property to true in order to target elements that are dynamically added to the DOM.
So
$(".mCustomScrollbar").mCustomScrollbar({
live:true // add this after your existing config options
});
Alternatively, and in this case might be a better option, just manually call mCustomScrollbar on the newly added element, after adding the contents to it.
for (var i = 0; i < metricsTypeYearModel.length; i++)
{
var obj = metricsTypeYearModel[i];
$("#MetricsTypeYearModelListUl").append('<li data-name='+obj.ModelTypeName+' data-value='+obj.ModelTypeID+' data-id='+obj.ModelTypeID+' class="pModel"> '+obj.ModelTypeName+'</li>');
}
$('#MetricsTypeYearModelListUl').mCustomScrollbar();

Why is my javascript file not re-setting a div's attribute?

I have a script that is adding and removing a class to a couple divs when a link is clicked on. Each div has a set class that does not need to be removed. However, said class is being removed. How do I stop this from happening?
HTML
<div id="home" class="page pageShowing"></div>
<div id="portfolio" class="page"></div>
JS
let holder = document.getElementById("main");
let pageShowingClass = holder.getElementsByClassName("pageShowing");
let pages = holder.getElementsByClassName("page");
Navigation.Links.forEach(function(value){
let createNavLink = document.createElement("li");
let createNavText = document.createTextNode(value.title);
createNavLink.appendChild(createNavText);
createNavList.appendChild(createNavLink);
createNavLink.addEventListener("click", function(){
let link = createNavLink.innerHTML;
link = link.toLowerCase().replace(" ", "_");
let page = document.getElementById(link);
page.setAttribute("class", "page");
for(let i = 0; i < pageShowingClass.length; i++){
Here, the click handler should only be removing the pageShowing class
if it exists but is also removing the page class
if(pageShowingClass[i].getAttribute("class") == "pageShowing"){
pageShowingClass[i].removeAttribute("class");
}
}
Here, the click handler should be readding the page class when the
link is clicked on.
page.setAttribute("class", "page");
page.setAttribute("class", "pageShowing");
page.style.display = "block";
});
});
I know it's easier to do this in jQuery, but I don't want it to be in jQuery. I also already have it to where it will add and remove the pageShowing class dynamically, so that's not an issue.
As Siguza said in the reply, you're removing the class attribute, which is what you DON'T want to be doing in this case.
Let's put the element in question here for reference:
<div id="home" class="page pageShowing"></div>
class is an attribute of the element div. When you call removeAttribute('class'), it will do as it says:
<div id="home"></div>
If you check the element in chrome's dev tools or whatever you use, you'll be seeing the element as it says above.
You're probably looking for Element.className to modify your classes, so instead of
if(pageShowingClass[i].getAttribute("class") == "pageShowing"){
pageShowingClass[i].removeAttribute("class");
}
you'll want
if(pageShowingClass[i].getAttribute("class") == "pageShowing"){
pageShowingClass[i].className = "page";
}
and if you want to add the pageShowing class again, you'd just say pageShowingClass[i].className = "page pageShowing"
Element.setAttribute() adds a new attribute or changes the value of an existing attribute on the specified element.
Use Element.classList.add(String [, String]), adds specified class values. If these classes already exist in attribute of the element, then they are ignored.
page.classList.add('page', 'pageShowing')

Get an element's value without an id

I've got following HTML:
<span class="testClass1" >
wanted Text
<a class="ctx" href="#"></a>
</span>
Now I want to get the text "wanted Text".
How can I achieve this?
I tried with:
document.getElementsByClassName("testClass1");
I also tried with document.getElementsByTagName() but I don't know how to use them properly.
You can use querySelectorAll
hence:
document.querySelectorAll('.testclass1 a')
will return all the <a> items children of a .testclass1
Snippet example:
var elements = document.querySelectorAll('.testClass1 a')
console.log(elements) // open the console to see this
console.log(elements[0].text) // this gets the first <a> text `wanted Text`
<span class="testClass1" >
wanted Text
<a class="ctx" href="#"></a>
</span>
The getElementsByClassName() function returns an array of matching elements, so if you need to access them, you could do so using a loop :
// Get each of the elements that have the class "testClass1"
var elements = document.getElementsByClassName("testClass1");
// Iterate through each element that was found
for(var e = 0; e < elements.length; e++){
// Get the inner content via the innerHTML property
var content = elements[e].innerHTML;
}
If you need to actually access the <a> tags directly below some of the elements as your edit indicates, then you could potentially search for those wihtin each of your existing elements using the getElementsbyTagName() function :
// Get each of the elements that have the class "testClass1"
var elements = document.getElementsByClassName("testClass1");
// Iterate through each element that was found
for(var e = 0; e < elements.length; e++){
// Find the <a> elements below this element
var aElements = elements[e].getElementsByTagName('a');
// Iterate through them
for(var a = 0; a < aElements.length; a++){
// Access your element content through aElements[a].innerHTML here
}
}
You can also use an approach like squint's comment or Fred's which take advantage of the querySelectorAll() function as the getElementsByClassName() and getElementsByTagName() are better served when accessing multiple elements instead of one specifically.
Try this:
document.getElementsByClassName("testClass1")[0].getElementsByTagName('a')[0].innerText
var testClass1 = document.getElementsByClassName("testClass1");
console.log(testClass1[0].innerHTML);

Javascript get class text inside element

I have a bunch of span4 class elements in my html. they look something like this:
<div class="span4">
<div class="widget">
<div class="header">blablabla</div>
</div>
</div>
I want to sort the span4 by that text iside header class.
I do this to sort them
$(".span4").sort(sortAlpha)
but how do I select the text inside the header class?
I'm doing this but I guess there is a better way
function sortAlphaAsc(a,b){
var nomeA = $(a.childNodes[1].childNodes[1]).text();
var nomeB = $(b.childNodes[1].childNodes[1]).text();
return a.innerHTML.toLowerCase() > b.innerHTML.toLowerCase() ? 1 : -1;
};
there must be a better way than
$(a.childNodes[1].childNodes[1]).text()
var elems = $(".span4");
elems.sort(function(a, b) {
return $(a).find('.header').text().toUpperCase().localeCompare(
$(b).find('.header').text().toUpperCase()
);
});
$(".span4").parent().html(elems);​
FIDDLE
Try this:
function sortAlphaAsc(a,b){
var nomeA = $(a).find('div.header').text();
var nomeB = $(b).find('div.header').text();
return nomeA.toLowerCase() > nomeB.toLowerCase();
};
You could detach the spans, sort and append them.
That will be very fast too as changing elements in memory and only updating the DOM once in the end is very efficient.
var $spans = $(".span4").detach();
var sortedSpans = $spans.sort(function(spanA, spanB) {
var spanTextA = $("div.header", spanA).text();
var spanTextB = $("div.header", spanB).text();
return spanTextA > spanTextB;
});
$("body").append(sortedSpans);
Obviously instead of body you append it back to it's actual container element.
Or if the spans are in a common container store the parent in cache var $parent = $spans.parent() and in the end simply do $parent.html(sortedSpans).
I don't know your whole mark-up but that should get you started.
DEMO - Detach spans, sort them and append again
Do you mean something like this:
$('.span4').find('.header').text();
This will return the text inside the header div.

Add class to first child using javascript

is there any reason this chain does not work? It does not add the class:
document.getElementsByTagName('nav')[0].firstChild.className = "current"
It should return the first child of the nav element which is an <a> which does not happen.
Thanks for your help!
That's because you have text nodes between nav and a. You can filter them by nodeType:
var childNodes = document.getElementsByTagName('nav')[0].childNodes;
for (var i = 0; i < childNodes.length; i++) {
if (childNodes[i].nodeType !== 3) { // nodeType 3 is a text node
childNodes[i].className = "current"; // <a>
break;
}
}
It may seem strange but, for example, if you have the following markup:
<nav>
<a>afsa</a>
</nav>
Here's a DEMO.
Why does this happen? Because some browsers may interpret the space between <nav> and <a> as an extra text node. Thus, firstChild will no longer work since it'll return the text node instead.
If you had the following markup, it'd work:
<nav><a>afsa</a></nav>
You can simply document.querySelectorAll to select the list.
use "firstElementChild" to get first child node and add class.
const firstChild = document.querySelectorAll('nav').firstElementChild;
firstChild.classList.add('current');
The statement:
document.getElementsByTagName('nav')[0].firstChild.className = "current"
is somewhat fragile as any change in the assumed document structure breaks your code. So more robust do do something like:
var links,
navs = document.getElementsByTagName('nav');
if (navs) links = nav[0].getElementsByTagName('a');
if (links) links[0].className = links[0].className + ' ' + 'current';
You should also have robust addClassName and removeClassName functions.
Jquery can make this very easy:
$("#nav:first-child").addClass("current");

Categories