If vs. while in specific JavaScript code - javascript

Why does the author of Test-Driven JavaScript Development (Christian Johansen) use the while statement instead of the if statement in the code below?
function getEventTarget(event) {
var target = event.target || event.srcElement;
while (target && target.nodeType != 1) {
target = target.parentNode;
}
return target;
}

Because the author wanted to keep walking up the tree until the correct node type was found; it might not be the immediate parent.
However, in this case it makes no sense, as parentNode will always return an element in real-world usage (or a Document).

Because he is walking up..
If you see pretty well, in the loop he is assigning the target again with its parent and the parent is not nodetype 1
target = target.parentNode;
I don't know what is he trying or what is the purpose or the goal but it's quite simple..
Imagine the DOM
<div>
<div>
<div>
<div>
Imagine he starts from here.. he will always get the max parent with not nodetype 1 the hightes parent so the first div..
</div>
</div>
</div>
</div>
SO basically. He is getting the Higher parent ... That's why is he looping.. if uses the If.. he will get just the First parent

Related

Check if an element IS an offsetParent

Is there a convenient way to check if an HTMLElement is an offsetParent?
I have a situation where I need to determine an element's offsetParent before it is inserted in the DOM. I can access the element's immediate parent, before insertion.
There doesn't seem to be any properties on HTMLElements that indicate whether or not it is an offsetParent.
Is there a good way to do this?
There is to my knowledge unfortunately nothing in the DOM API that does expose this information on the Element itself.
According to specs, an ancestor can be an offsetParent if
The element is a containing block of absolutely-positioned descendants
This means that not only positioned elements will qualify, but any element with a transform, or a filter property, a will-change with a value containing any of the aforementioned ones will also do.
However this behavior was not always specified this way, so it may return false positives in some browsers.
Also, it may be that in the future other CSS properties will affect what makes a containing block, or even in the present since I only got these from the tip of my head...
With that in mind, the surest is to append a test element inside your element and to check its offsetParent.
However, this will create forced reflows, so use it sporadically.
document.querySelectorAll('.container > div')
.forEach(elem => {
elem.textContent = isOffsetParent(elem) ? 'offsetParent' : 'not offsetParent';
});
function isOffsetParent(elem) {
const test = document.createElement('span');
elem.appendChild(test);
const result = test.offsetParent === elem;
elem.removeChild(test);
return result;
}
<div class="container">
<div class="is-offset-parent" style="position:relative"></div>
<div class="can-be-offset-parent" style="transform:translate(0)"></div>
<div class="can-be-offset-parent" style="filter:blur(1px)"></div>
<div class="is-not"></div>
</div>
But if you really wish some unsafe way which may need to be updated, then you could check all the properties I mentioned before using getComputedStyle(elem).

Selecting the parent element that is hiding its children

I feel like this could have been asked before, but I can't seem to find it, so I'll ask myself.
I want to select the parent element that is the "cause" of its children being hidden. For example, a group of elements are essentially "hidden" but not because they have display:none but because some parent along the way has display:none.
If I only have a child element and I know that it is hidden, how might I easily find the parent that is causing it to be hidden.
I realize one solution is just to recursively loop through the parents such as...
function findHiddenParent(el){
var $el = $(el);
if($el.css('display') == 'none'){
return $el;
}
return findHiddenParent($el.parent());
}
Note I haven't tested the above code, its just for conceptualizing a solution
But is there an easier way, perhaps through some selector magic?
You can use .parents() then filter that to the last element that is hidden.
$(child).parents(":hidden").last().show();
it will select the parent element highest in the hierarchy that has display: none
Demo: http://jsfiddle.net/X9W2v/
Note, :hidden will also select elements with a width/height of 0.
Here's something similar to what you've got:
function findHiddenParents ( el ) {
return $(el).parents().filter(function () {
return $(this).css('display') == 'none';
});
}
This'll return all ancestors that are hidden. If you only want the closest or the farthest, you can just return it with .first() or .last() respectively.
If you're not actually interested in which elements are hidden, but just want to reveal them all, then you can't get any simpler than this:
$(el).parents().show()
This is how I would do it in plain JavaScript. Somehow I feel it's cleaner than the jQuery solutions that involve loops or callbacks (Kevin B's one-liner looks great!). The cons are, it's longer, and it doesn't check computed styles:
<div id="container">
<div id="a" style="display: none;">A
<div id="b">B
<div id="c">C</div>
</div>
</div>
</div>
var currentNode = document.getElementById('c');
while(currentNode.parentNode && currentNode.style.display !== 'none') {
currentNode = currentNode.parentNode;
}
alert(currentNode.id);
http://jsfiddle.net/nc4h2/

How to get the parent node of an element when the parent has siblings?

Please take a look at the snippet below:
<div>
<div></div>
<div><!-- my target node -->
<div><!-- not my target node -->
<img /><!-- my source node -->
</div>
</div>
</div>
As you can see the img-elment has two enclosing divs. I want the first of those two enclosing divs to be considered the "real" parent (the one I need to find) of the img-elment because it has a brother div before so the search ends and the brother div and the outer enclosing div are ignored.
In the case there are no siblings at all, the outer div has to be yielded; in the case the element is not enclosed, the element itself has to be yielded.
I just would like to know how to target the element as I explained via JavaScript.
So it sounds like you want the first ancestor that has siblings elements. If so, you can do it like this:
var parent = img.parentNode;
while (parent && !parent.previousElementSibling && !parent.nextElementSibling) {
parent = parent.parentNode;
}
Or perhaps more appropriately written as a do-while loop:
do {
var parent = img.parentNode;
} while (parent && !parent.previousElementSibling && !parent.nextElementSibling);
So the loop will end when it finds one with at least one sibling element, or when it runs out of ancestors.
If you know if the sibling comes before or after the parent, you can just test for one or the other.
Also note that you'll need a shim for the ***ElementSibling properties if you're supporting legacy browsers.
You can make a function that will do this:
function prevElement(el) {
while ((el = el.previousSibling) && el.nodeType !== 1) {
// nothing needed here
}
return el;
}
function nextElement(el) {
while ((el = el.nextSibling) && el.nodeType !== 1) {
// nothing needed here
}
return el;
}
Then use the functions like this:
do {
var parent = img.parentNode;
} while (parent && !prevElement(parent) && !nextElement(parent));
If you don't know how many levels up the parent element is, it will be difficult to select it using methods like element.getParent alone. However, you CAN iterate through parent nodes until the node you're looking at has siblings and is the child of a body element. Let's assume that your img tag is referred to by imgNode.
function getParentWithSiblings(imgNode) {
for( ; n; n = imgNode.parentNode) {
if (n.nextSibling && n.parentNode.tagName == 'body') {
return n;
}
}
}
In the code above, we progressively iterate through the parents of the image node. At each iteration, we check whether the current node (some parent of the img node) has a sibling and is the child of a body tag.
Just in case you're curious, here's how you might implement user1689607's answer using jQuery.
function getAncestorWithSiblings(element) {
var ancestor = element.parent();
while (ancestor && ancestor.siblings().length === 0) {
ancestor = ancestor.parent();
}
return ancestor;
}
Whether it makes sense to use this library for your purposes depends on a great deal of context we don't have. As others have rightfully pointed out, you don't need jQuery to solve this problem, and it may be an unnecessarily heavyweight solution. That said, it can be a very useful library and is certainly worth your consideration if you weren't aware of it or hadn't already looked into it.

How to remove text (without removing inner elements) from a parent element using jquery

Imagine that I have something like the following (modified from http://viralpatel.net/blogs/jquery-get-text-element-without-child-element/)
<div id="foo">
first
<div id="bar1">
jumps over a lazy dog!
</div>
second
<div id="bar2">
another jumps over a lazy dog!
</div>
third
</div>
How can I remove just (only text) "first", "second" and "third" from DOM without affecting any of the child elements.
If you want to remove all child text nodes you can use .contents() and then .filter() to reduce the matched set to only text nodes:
$("#foo").contents().filter(function () {
return this.nodeType === 3;
}).remove();​​​​​​​
Here's a working example.
Note: This will preserve any existing event handlers on the child elements, which answers using .html() will not do (since the elements are removed from the DOM and added back in).
Note 2: Answers in some of the linked questions show similar code to that in my answer here, but they use the nodeType constants (e.g. return this.nodeType === Node.TEXT_NODE). This is bad practice since IE below version 9 does not implement the Node property. It's safer to use the integers (which can be found in the DOM level 2 spec).
Here's a non-jQuery version which works in all major browsers back to IE 5, just to demonstrate how unscary life without jQuery is.
Demo: http://jsfiddle.net/timdown/aHW9J/
Code:
var el = document.getElementById("foo"), child = el.firstChild, nextChild;
while (child) {
nextChild = child.nextSibling;
if (child.nodeType == 3) {
el.removeChild(child);
}
child = nextChild;
}
try this :
$("#foo").html($("#foo").find("div"));​
demo : http://jsfiddle.net/PXxC4/
You could use this.
$("#foo").html($("#foo").children());​

How to tell if a DOM element is displayed?

This is not to be confused with "How to tell if a DOM element is visible?"
I want to determine if a given DOM element is visible on the page.
E.g. if the element is a child of a parent which has display:none; set, then it won't be visible.
(This has nothing to do with whether the element is in the viewport or not)
I could iterate through each parent of the element, checking the display style, but I'd like to know if there is a more direct way?
From a quick test in Firefox, it looks like the size and position properties (clientWidth, offsetTop etc.) all return 0 when an element is hidden by a parent being display:none.
Using Prototype:
if($('someDiv').visible) {...}
As I'm using MochiKit, what I came up with based on Ant P's answer was:
getElementPosition('mydiv').y != 0
I can also check whether it's in the viewport (vertically) by:
y = getElementPosition('mydiv').y
(y < getViewportPosition().y + getViewportDimensions().h &&
getViewportPosition().y < y)
Incidentally this also works in IE6.
Relying on the position being 0 is brittle. You're better off writing a helper function to iterate through the parents to check their display style directly.
Here's the iterative solution -
var elementShown = function(e){
if (e == document)
return true;
if ($(e).css('display') == 'none') //or whatever your css function is
return false;
return elementShown(e.parentNode);
}
.getClientRects() will return an empty array if the element is not displayed by inheritance (display="none" from parent/ancestor element)

Categories