Cleaner way to write element.parent().parent().parent().parent().parent() - javascript

If I need to select 10-th parent, is there a cleaner way, then repeating .parent() 10 times?
$('#element_id').parent().parent().parent().parent().parent().parent().parent().parent().parent().parent();

If there's a selector that represents the target you're after, then use .closest() or .parents().
$('#element_id').closest('.someClass');
$('#element_id').parents('.someClass:first');
...but both of these will return the first match found. The proper solution will depend on your actual HTML markup.
(Note that .closest() also evaluates the original element, while parents() starts with the first ancestor.)
Also keep in mind that browsers make HTML corrections. So if you're traversing from inside a <table> that has no <tbody> to an element outside the <table>, doing x number of .parent() may give different results in different browsers.

The following post here uses this implementation:
jQuery.fn.getParent = function(num) {
var last = this[0];
for (var i = 0; i < num; i++) {
if(!last) break;
last = last.parentNode;
}
return jQuery(last);
};
// usage:
$('#myElement').getParent(3);
so your usage would simply be:
$('#element_id').getParent(10);

If you truly need to get the 10th parent, and you are unable to use a selector to get there, the smoothest way would probably be something like this:
$('#element_id').parents().eq(9);

I've used this code:
var position = $("#test").parents("div").length - 10;
$("#test").closest("div:eq(" + position + ")").append("here!!!");
with that HTML :
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<span id="test">here</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
Yes that is 11 div. so running the code should stop at the 10th div and append here!!!
I'm sure this code could be even more clean.
No need to add class.
Edit:
I used 11 DIV so you can see it's not going to the very first one, it actually stop at the 10th.

Related

Why using .filter() together with .match() is only returning the first element matching the condition?

I have some HTML code where at the most nested level there is some text I'm interested in:
<div class="main">
<div class="container">
<div class="output_area">
<pre>WHITE 34</pre>
</div>
<div class="output_area">
<pre>RED 05</pre>
</div>
<div class="output_area">
<pre>WHITE 16</pre>
</div>
<div class="output_area">
<pre>BLACK</pre>
</div>
</div>
</div>
What I need to do is I need to return the output_area elements only when their nested <PRE> element contains a word + a number (for example WHITE 05, and not just BLACK).
So this is what I did:
I made an array from all output_area elements:
output_areas = Array.from(document.getElementsByClassName('output_area'));
I filtered the output_areas array to only return those output_area elements whose nested <PRE> satisfies my condition of a word + a number, using a regexp, like so:
output_areas.filter(el => el.textContent.match(/^WHITE \d+$/g));
Now, what happens is this function will only return the first matching result, so I will get an object of length 1 containing just :
<div class="output_area">
<pre>WHITE 34</pre>
</div>
and the output_area element containing <PRE> with "WHITE 16" is not returned.
As you can see at the end of the regular expression I put a "g" to request a global search and not just stop at the first result.
Not understanding why this did not work, I tried to verify what would happen if I would use includes() to perform a search:
output_areas.filter(el => el.textContent.includes('WHITE')
(let's just forget about the numbers now, it's not important)
And what happens? This will also return only the first output_area...
But why??? What am I doing wrong?
I am not ashamed to say I've been banging my head on this for the last couple of hours... and at this point I just want to understand what is not working.
The only clue I think I got is that if I simplify my search using just a == or !=, for example:
output_areas.filter(el => el.textContent != "")) // return all not empty elements
I get back all output_area elements and not just the first one!
So I suspect there must be some kind of problem when using together filter() & match(), or filter() & includes(), but with relation to that my google searches did not take me anywhere useful...
So I hope you can help!
You should use trim here to remove space before and after the text
output_areas.filter( el => el.textContent.trim().match( /^WHITE \d+$/g ))
const output_areas = Array.from(document.getElementsByClassName('output_area'));
const result = output_areas.filter(el => el.textContent.trim().match(/^WHITE \d+$/g));
console.log(result);
<div class="main">
<div class="container">
<div class="output_area">
<pre> WHITE 34 </pre>
</div>
<div class="output_area">
<pre> RED 05 </pre>
</div>
<div class="output_area">
<pre> WHITE 16 </pre>
</div>
<div class="output_area">
<pre> BLACK </pre>
</div>
</div>
</div>
Answering myself as for some reason it then begin to work without any changes from my side... Yes, just one of those typical IT cases we all know... :)
Jokes aside, I think for some reason the webpage (the DOM) got stuck...
Probably the Jupyter Runtime (which was serving the page) had crashed without me noticing, and this caused somehow the kind of inconsistency I was looking at.
Moral of the story: if you see weird behaviour in the interaction with a Python Notebook, always go check the Jupyter Runtime status before getting stupid at trying to fix impossible errors.
I'm not sure what the issue with the Jupyter notebooks is, but generally speaking - based only on the HTML in the question - what I believe you are trying to do can be achieved using xpath instead of css selectors:
html = `[your html above]
`
domdoc = new DOMParser().parseFromString(html, "text/html")
const areas = domdoc.evaluate('//div[contains(./pre," ")]', domdoc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for (let i = 0; i < areas.snapshotLength; i++) {
console.log(areas.snapshotItem(i).outerHTML)
}
The output should be the 3 divs meeting the condition.

Using a variable value on multiple elements with the same iD

So I have a file like
<div class="u1">
<p id="level1"></p>
<p id="level2"></p>
</div>
<div class="u2">
<p id="level1"></p>
<p id="level3"></p>
</div>
and if I use something like
document.getElementbyId("level1").innerText(or innerHtml) = "Hello"
it writes that string on one element, and not on every element with id="level1"
I tried with
$('#level1').text("Hello");
but it works only for one.
I need something to write a string on every element with id="level1".
ID is a unique identifier, so you can't have few items with the same ID. Provided HTML code is invalid. You can use CLASS attribute or data-id or....
If you can't change HTML and it comes from third-party dependency, then we have a list of NOT recommended but WORKING solutions
jQuery:
$('[id="level1"]').text("Hello");
js:
var elements = document.querySelectorAll('#level1'); + for loop to iterate elements
And a lot of similar solutions based on knowledges how elements selecting works under the hood
It is ideal to have Unique Ids and thus you cannot update it this way. What you can do is you can give it a class like this:
<div class="u1">
<p id="level1" class="helloText"></p>
<p id="level2"></p>
</div>
<div class="u2">
<p id="level1" class="helloText"></p>
<p id="level3"></p>
</div>
Now use this JS to update the HTML:
var paras = document.getElementsByClassName("helloText");
for (i = 0; i < paras.length; i++) {
paras[i].innerHTML = "Hello";
}
You can also remove the Ids or make them unique to make your code cleaner. It is never recommended to use duplicate Ids.
You give a classname to the elements you need:
class = "levels"
in the script, use document.getElementsByClassName, which return an array with all elements. then you can loop through that array to get the info of each elements.
let levels = document.getElementsByClassName("levels");
for (let j=0; j<levels.length; j++){
levels[j] do something...;
}

How to get div text value?

I've got the following html:
<div class="question">
<div class="text">
Blah Blah
</div>
</div>
I am trying to get the value "Blah Blah" using javascript.
So something like?
alert(document.getElementsByName("question")[0].value);
document.getElementsByClassName('question')[0].innerText;
or
document.querySelector('.question').innerText;
You can do it like this
alert(document.getElementsByClassName("question")[0].children[0].innerHTML);
You need to select the element correctly first. It doesn't (and can't) have a name attribute so getElementsByName is wrong. You can use getElementsByClassName or (with more limited support) the new and shiny querySelector:
var div = document.querySelector('.question');
Then you need to get it's "value". It isn't a form control so it doesn't have a value property. It has childNodes, the one of which you care about is another div.
var childDiv = div.querySelector('.text');
You can skip the two stages if you are are using querySelector and just use a descendant combinator:
var childDiv = document.querySelector('.question .text');
This child div then has another child node, but it is a text node rather than an element node. You can get it like so:
var textNode = div.firstChild;
Finally you can get the text in a textNode using the data property.
var text = textNode.data;
And if you put it all together:
alert(document.querySelector('.question .text').firstChild.data);
Such: http://jsfiddle.net/LR93S/
No need to rely on jQuery here, use innerText
document.querySelectorAll('div.question')[0].innerText
$('.question').text(); // jquery
document.getElementsByClassName("question")[0].innerText; // javascript
Try this: document.getElementsByClassName("text")[0].innerHTML (http://jsfiddle.net/hv9Dx/15/)
The simplest way to do this is with jquery:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
var divvalue = $('.text').html()
alert (divvalue);
You could also change your html to use an ID
<div class="question">
<div id="text">
Blah Blah
</div>
</div>
and use:
document.getElementById("text").innerHTML;
Example: http://jsfiddle.net/DanBrown180/N9Z8Z/
document.querySelectorAll('.text')[0].innerHTML
For the following example:
<div class="question" id="question">
<div class="text" id="text">
Blah Blah
</div>
</div>
You can try to get the value of its inner div tag like this:
alert(document.getElementById('question').childNodes[1].innerHTML);
Cheers!
Use the standard DOM property textContent, which is widely supported and standardized, unlike innerText:
document.getElementsByClassName("question")[0].textContent;
If you need to support IE <= 8 then you've got two problems: neither document.getElementsByClassName() nor textContent is supported.
For the first issue, it's not too hard to write/find a function to retrieve elements by class name using other DOM methods (example).
For the second, you can use IE's innerText property, which will do roughly (but not precisely) the same thing as textContent.

how can content between a class put in a variable after click?

How do I retrieve the content between and including the following <div class="adding"> and store it in a variable?
<div class="adding">
<b>
<div class="column">
<div class="mediumCell">
<input type="text" name="name" placeholder="توضیح" title="نام پکیج تور خارجی">
</div>
</div>
<div class="column" style="margin: 5px 3px;">
<div class="mediumCell">
<div class="adda">
</div>
</div>
</div>
</b>
</div>
var adding = '<div class="adding"><b><div class="column"><div class="mediumCell"><input type="text" name="name" placeholder="توضیح" title="نام پکیج تور خارجی"></div></div><div class="column" style="margin: 5px 3px;"><div class="mediumCell"><div class="adda"></div></div></div></b></div>'
In each click I want to get the content just once.
Unfortunately, after two or more clicks getting content several times together (E.x: after two clicks it stores the content twice).
I tried this:
$(function () {
var i = $('.adding').size();
$('.add_input').live('click', function () {
var scntDiv = '.' + $(this)
.closest('.find_input')
.find('div')
.attr('class');
var input = $(scntDiv).html();
$(this).remove();
$(input).appendTo(scntDiv);
i++;
return false;
});
});
You can use the html() method, as others have said, but there's a catch: that method returns the inner HTML content of the element, so the markup of the outer <div> element won't be included in the result.
Since you seem to want that markup, you can work around the issue with clone(), wrap() and parent():
var adding = $("div.adding").clone().wrap("<div>").parent().html();
You can get the inner HTML using the html function:
var adding = $(".adding").html():
...which will give you the browser's version of the markup within the first matching div (the first div with the class "adding"). It's fairly simple at that point to wrap it with the markup for the div, unless there are a lot of chaotic attributes involved.
However, note that the markup you get back may not be what you expect, because your HTML is invalid. b elements cannot contain div elements (b elements may only contain phrasing content; div elements are flow content), and so the browser adjust things as it sees fit to display something reasonable. This is a Bad Thing(tm), it's much better with dynamic web apps to ensure that your HTML is valid.
Is that what you're asking for ?
var adding;
$('.adding').click(function(){
adding = $(this).html();
alert(adding);
});
var adding = $(".adding").html();
maybe?

what's the best way (fastest) to sort divs on a div ? (in javascript without JQuery)

I have the following:
<div id='a'>
<div id='a1' />
<div id='a2' />
<div id='a3' />
<div id='a4' />
<div id='a5' />
</div>
Suppose that when a button is pressed I need to rearrange it like this:
<div id='a'>
<div id='a2' />
<div id='a4' />
<div id='a3' />
<div id='a1' />
<div id='a5' />
</div>
or something similar.
Now,
I know how to get all the children from div a.
I know how I can sort them (whatever my algorithm is)
The question is then what's the best way to rearrange them ?
I could remove them all.
and then readd them one by one in the correct order.
That'd work but I think it would be wrong because of the reflow everytime a new item is added ?
can anybody help ?
Point me to an article on the web or write down a solution
thanks
So you've got a sorted array, you can do 2 things [Performance Test]
var array = [ ... ]; // sorted array of elements
var len = array.length;
Detach the parent before appending the elements, then append the parent back.
This seems to be the fastest alternative (especially for large collections).
var parent = root.parentNode;
var next = root.nextSibling;
parent.removeChild(root);
for (var i = 0; i < len; i++) {
root.appendChild(array[i]);
}
parent.insertBefore(root, next);
Another option would be to use documentFragment, and then you append it to the DOM at once.
var fragment = document.createDocumentFragment();
for (var i = 0; i < len; i++) {
fragment.appendChild(array[i]); // lighweight, off-DOM container
}
root.appendChild(fragment); // append to the DOM at once
Unfortunately, it slows down your code in Chrome. (faster in IE, and FF)
I would like to point out that unless you're dealing with 1000+ elements the difference won't be noticable.
Use the insertBefore/appendChild DOM methods to re-order them.
And better use jQuery or another javascript framework so you don't have to do all the dirty work yourself.

Categories

Resources