How to get nth- parent element in pure javascript and not jquery - javascript

For example,
<div class="panel">
<div class="inner-panel">
<button onclick="hideParentElement()">
Hide
</button>
</div>
</div>
If I want to add a class to the outer most division, I need to fetch that element and add a class to it. How can this be achieved using pure javascript?
I have tried the following:
this.parentElement.parentElement.classList.add("classname");
This works but if there are more than 2 parentElements it is not the right way.
Please suggest a better way

You have to cycle the parents like this.
var el = this.parentElement;
while(el.className != 'panel'){
el = el.parentElement;
}
//el is now your parent
el.className += "classname";
With pure Javascript this is the way to do it.

In case when your class list is changing dynamically:
let parent = this.parentElement;
while (!parent.classList.contains('panel'))
parent = parent.parentElement;
parent.classList.add('classname');

Kind of late to the party, but this seems to work:
var pn = element.parentNode
for(i=4;i>0;i--){
pn = pn.parentNode;
}

Related

How do I access a specific element in the inner HTML?

I am trying to use JS or jQuery to access an html value called "data-button". I can access the whole HTML div and pull out class name as well as the text content from the button, but I cant get the data-button value.
In the code below I have a captureRecipeButtons() function that can get the "recipe-1-container" div.
function captureRecipeButtons(){
let theWholeDiv = document.getElementsByClassName("recipe-1-container")[0];
let buttonValue = ?;
}
<div class="recipe-1-container">
<button class="listed-recipe-link" data-button="1">Element</button>
</div>
In my captureRecipeButtons() function I want buttonValue to equal "1" in my above code. Any help would be appreciated thanks.
You can use the full power of CSS selectors with querySelector:
function captureRecipeButtons(){
let button = document.querySelector(".recipe-1-container [data-button]");
}
querySelector returns the first matching element (or null if none do).
If you wanted the value of data-selector on that element, then getAttribute or dataset:
function captureRecipeButtons(){
let buttonValue = document.querySelector(".recipe-1-container [data-button]").getAttribute("data-button");
// or
let buttonValue = document.querySelector(".recipe-1-container [data-button]").dataset.button;
}
Live Copy:
function captureRecipeButtons(){
const button = document.querySelector(".recipe-1-container [data-button]");
console.log(button.getAttribute("data-button"));
// or
console.log(button.dataset.button);
}
captureRecipeButtons();
<div class="recipe-1-container">
<button class="listed-recipe-link" data-button="1">Element</button>
</div>
But note that dataset does some transformations.
But there are lots of different ways to do this. More to explore in the DOM.
So you could just get the button element by class or tag name as I have done. then data-button is legit just an attribute so just use getAttribute('data-button');
The way I've written below will just get the first button that is a direct child of theWholeDiv element.
function captureRecipeButtons(){
let theWholeDiv = document.getElementsByClassName("recipe-1-container")[0];
let buttonValue = theWholeDiv.getElementsByTagName('button')[0].getAttribute('data-button');
console.log(buttonValue);
}
captureRecipeButtons();
<div class="recipe-1-container">
<button class="listed-recipe-link" data-button="1">Element</button>
</div>
According to Mozilla Docs,
You can access the Data attributes via the dataset object.
function captureRecipeButtons(){
let theButton = document.querySelector("recipe-1-container > button");
let buttonValue = theButton.dataset.button;
}
Another way using the DOM, including some short-cuts to avoid excessive horizontal scrolling. The code precisely targets the first DIV element and its first BUTTON element, using the getAttribute() method to return the value of the indicated property. What is nice about JavaScript is the fabulous amount of chaining that one can do between parent and child elements.
function captureRecipeButtons() {
let d = document;
d.g = d.getElementsByTagName;
let buttonValue = d.g("div")[0].getElementsByTagName("button")[0].getAttribute("data-button");
console.log(buttonValue);
};
captureRecipeButtons();
<div class="recipe-1-container">
<button class="listed-recipe-link" data-button="1">Element</button>
</div>
Alternatively, you could write code as follows:
function captureRecipeButtons() {
let d = document;
d.g = d.getElementsByTagName;
let button = d.g("div")[0].childNodes[1];
button.g = button.getAttribute;
let buttonValue = button.g("data-button");
console.log(buttonValue);
}
captureRecipeButtons();
<div class="recipe-1-container">
<button class="listed-recipe-link" data-button="1">Element</button>
</div>
The DIV element's first child node as per the format of the code is not the BUTTON element but a text object. The childNode[1] holds the BUTTON element, so you can use its getAttribute() method to retrieve the value of the data-button attribute.

How to wrap the content of DOM Element into another Element with minimal performance overhead?

I would like to wrap the live content of a DOM element into another, keeping all the structure and all attached event listeners unchanged.
For example, I want this
<div id="original">
Some text <i class="icon></i>
</div>
to become
<div id="original">
<div id="wrapper">
Some text <i class="icon></i>
</div>
</div>
Preferably without jQuery.
If there is nothing else other than ID to distinguish your nodes, and given that #original has multiple child nodes, it would probably be simpler to create a new parent node and insert that:
var original = document.getElementById('original');
var parent = original.parentNode;
var wrapper = document.createElement('DIV');
parent.replaceChild(wrapper, original);
wrapper.appendChild(original);
and then move the IDs to the right place:
wrapper.id = original.id;
original.id = 'wrapper';
noting of course, that the variables original and wrapper now point at the 'wrong' elements.
EDIT oh, you wanted to leave the listeners attached... Technically, they still are, but they're now attached to the inner element, not the outer one.
EDIT 2 revised answer, leaving the event listeners attached to the original element (that's now the outer div):
var original = document.getElementById('original');
var wrapper = document.createElement('DIV');
wrapper.id = 'wrapper';
while (original.firstChild) {
wrapper.appendChild(original.firstChild);
}
original.appendChild(wrapper);
This works simply by successively moving each child node out of the original div into the new parent, and then moving that new parent back where the children were originally.
The disadvantage over the previous version of this answer is that you have to iterate over all of the children individually.
See https://jsfiddle.net/alnitak/d0jss2yu/ for demo
Alternatively, do it this way. It also displays result in the adjacent result div.
<div id="original">
Some text <i class="icon"></i>
</div>
<button onclick="myFunction()">do it</button>
<p type="text" id="result"></p>
<script>
function myFunction() {
var org = document.getElementById("original");
var i = org.innerHTML; //get i tag content
var wrap = document.createElement("div"); //create div
wrap.id="wrapper"; //set wrapper's id
wrap.innerHTML= i //set it to i tag's content
org.innerHTML=""; // clear #orignal first
org.appendChild(wrap); //append #wrapper and it's content
var result = org.outerHTML;
document.getElementById("result").innerText = result;
}
</script>
Updated answer:
This should work better and with less code than my previous answer.
var content = document.getElementById("myList").innerHTML;
document.getElementById("myList").innerHTML = "<div id='wrapper'></div>"
document.getElementById("wrapper").innerHTML = content;
EDIT: This will destroy any event listener attached to the child nodes.
Previous answer:
I don't tried it, but something like this should work:
var wrapper = document.createElement("DIV");
wrapper.id = "wrapper";
var content = document.getElementById("myList").childNodes;
document.getElementById("myList").appendChild(wrapper);
document.getElementById("wrapper").appendChild(content);
Create the wrapper element.
Get myList contents.
Add the wrapper element to myList.
Add myList contents to be child of the wrapper element.

How can I remove wrapper (parent element) without removing the child?

I would like to remove the parent without removing the child - is this possible?
HTML structure:
<div class="wrapper">
<img src"">
</div>
<div class="button">Remove wrapper</div>
After clicking on the button I would like to have:
<img src"">
<div class="button">Remove wrapper</div>
Pure JS (ES2015) solution, in my opinion easier to read than jQuery-solutions.
node.replaceWith(...node.childNodes)
Node has to be an ElementNode
const wrapperNode = document.querySelector('h1')
wrapperNode.replaceWith(...wrapperNode.childNodes)
<h1>
<a>1</a>
<b>2</b>
<em>3</em>
</h1>
Pure JS solution that doesn't use innerHTML:
function unwrap(wrapper) {
// place childNodes in document fragment
var docFrag = document.createDocumentFragment();
while (wrapper.firstChild) {
var child = wrapper.removeChild(wrapper.firstChild);
docFrag.appendChild(child);
}
// replace wrapper with document fragment
wrapper.parentNode.replaceChild(docFrag, wrapper);
}
Try it:
unwrap(document.querySelector('.wrapper'));
Surprised that nobody's posting the simplest answer:
// Find your wrapper HTMLElement
var wrapper = document.querySelector('.wrapper');
// Replace the whole wrapper with its own contents
wrapper.outerHTML = wrapper.innerHTML;
Could use this API: http://api.jquery.com/unwrap/
Demo http://jsfiddle.net/7GrbM/
.unwrap
Code will look something on these lines:
Sample Code
$('.button').click(function(){
$('.wrapper img').unwrap();
});
Pure javascript solution, i'm sure someone can simplify it more but this is an alternative for pure javascript guys.
HTML
<div class="button" onclick="unwrap(this)">Remove wrapper</div>
Javascript (pure)
function unwrap(i) {
var wrapper = i.parentNode.getElementsByClassName('wrapper')[0];
// return if wrapper already been unwrapped
if (typeof wrapper === 'undefined') return false;
// remmove the wrapper from img
i.parentNode.innerHTML = wrapper.innerHTML + i.outerHTML;
return true;
}
JSFIDDLE
if you're using jQuery:
$(".wrapper").replaceWith($(".wrapper").html());
If the wrapper element contains text, the text remains with child nodes.

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.

Is there a JS/jQuery Fully Recursive child node count function?

Folks,
Let's say that I have the following situation:
<div class="outer">
<div>
<span>
<ul>
<div> </div>
<div> </div>
</ul>
</span>
</div>
</div>
How would I find the total number of elements inside div.outer? Something like $('div.outer').children().size() or length returns 1 (I'm looking, in this case, for 5) is there a shortcut or function to find the total element count using js/jQuery or should I write a function just for this issue? TIA.
var totalDescendants = $('div.outer *').length;
and done.
var totalDescendants = $('div.outer').find('*').length;
would also work.
A simple (and likely faster than jQuery) JavaScript alternative would be
var el = document.getElementById("foo");
var descendantElementCount = el.getElementsByTagName("*").length;
This does assume you've given the element an id. You could use jQuery to get all the elements with class outer, if this is important:
var descendantElementCount = 0;
$('div.outer').each(function() {
descendantElementCount += this.getElementsByTagName("*").length;
});
jQuery recursive child node count function:
jQuery(document).ready(function(){
var totalChildren=jQuery("*","div.outer").length;
});

Categories