How to access DOM from previous sibling in JavaScript? - javascript

I am trying to get id(id 1) name of a div from a div(class 2) in the next line. When I use previousSibling, it worked on google chrome but not on safari since safari catches the area between divs as "#text".
In my code, id name is very unique and there are many different id name so I do not want to find by id name.
Is there any good way to access previous sibling??
<div id="parent">
<div id="1">
<div class="1-1"></div>
<div class="1-2"></div>
</div>
<div class="2"></div>
</div>

When I use previousSibling, it worked on google chrome but not on safari since safari catches the area between divs as "#text".
It works. It just doesn't do what you want it to do. It returns sibling Nodes. In this case a TextNode.
What you are looking for is an Element, a specific subclass of Node. For that there's the .previousElementSibling property.
Inspecting a DOM node with the debugger and then looking up the documentation on the available properties is a good way to find such things.

Related

Find the first id of any descendant using jQuery

For draggable divs in JS on my page, I want to store the last location in local storage so that when a user refreshes, the draggable elements on the page stay put.
My HTML is in general like this:
<div id="some_id" class="draggable">
<p>I am a draggable thing.</p>
</div>
I then use the id of the div as a key in local storage so that having multiple draggable objects on the page doesn't result in them all being given the same position on refresh.
However, templates like this are sometimes used inside a template which handles visibility, so sometimes they'll be like this:
<div class="visibility_container draggable">
<button class="close_button">Close</button>
<div id="some_id">
<p>I am a draggable thing.</p>
</div>
</div>
Note that the draggable class is added programmatically each time.
These templates may vary but will never have ids within them – they'd be pretty terrible templates if they did – so I only need to find the first descendant element which has an id and use the value of that id as my local storage key.
How can I find the nearest element with JS? I'm aware that jQuery has a .closest() method which finds the nearest ancestor – I need to go in the opposite direction. I'm also aware of jQuery's .find() which can find me all descendants matching a selector, but I'm unsure I can guarantee the order in which jQuery returns these children as the API docs were not clear on that point.
I'm also aware of jQuery's .find() which can find me all descendants matching a selector, but I'm unsure I can guarantee the order in which jQuery returns these children as the API docs were not clear on that point.
find lists elements in document order (aka "DOM order") (and you're right, I'm surprised not to see a clear statement of that in the docs). "Document order" is a well-defined DOM term, it means a depth-first search of the descendant elements. Or put it another way: The one whose text is first in the markup.
So for instance:
<div id="container">
<div>
<div>
<div id="one"></div>
</div>
<div id="two"></div>
</div>
...then
console.log($("#container").find("[id]").first().attr("id"));
...will log one, not two.
This document order thing is common across most of the jQuery API and DOM methods like querySelectorAll, getElementsByTagName, and such. I'm not having any luck finding a clear statement of it in the jQuery documentation, though, which seems like an oversight. The closest I've found so far is a bit documenting an exception to that (e.g., saying here that "The second and third...create a jQuery object using one or more DOM elements that were already selected in some other way...unlike most other multi-element jQuery operations, the elements are not sorted in DOM order." [my emphasis].) The multiple selector docs also point out that the results will be in document order (not the order of the selectors).

Can I select an element by its array position in nightwatch.js?

I am looking to automate some of my testing processes and I am relatively new to Nightwatch.js and javascript. Is there a way that I can click an element based on it's class and position in the subsequent array that will be returned if there are multiple elements with the same class.
For example take the following HTML: -
<div class="clickable-button"><p>Some Text</p></div>
<div class="clickable-button"><p>Some Text 2</p></div>
<div class="clickable-button"><p>Some Text 3</P></div>
If I use chrome development tools and run the following command in the console: -
$('.clickable-button')
It returns an array of the three elements listed above.
I would like to click the first <div> element and want to know if there is a way I can do this using a CSS selector? I cannot select via the text that appears within the <p> tag as this is dynamic data.
I have tried the following commands in Nightwatch: -
browser.click('.clickable-button'[0])
browser.click('clickable-button[0]')
Neither of these options work. Any help or advice would be appreciated.
You could probably use :nth-of-type
browser.click('.clickable-button:nth-of-type(1)');
BTW :nth-of-type is part of CSS3 so it is not supported by older browsers.
Besides using CSS selector, XPath is another option, you can do
browser
.useXpath() //ignore this line if you already selected xpath as strategy
.click('(//div[#class="clickable-button"])[1]')
to locate the first button. Reference

Javascript document.getElementById() with multiple elements

How does document.getElementById() handle it when there are multiple elements with the same ID? MDN simply says:
Returns a reference to the element by its ID.
In the "Notes" section of the documentation, where I would expect a mention of what should happen, all that was stated is:
If there is no element with the given id, this function returns null.
So I decided to find out myself, and I did a test in Chrome's dev console. Here's what I got:
> document.write('<div id="myid"></div>')
> document.getElementById("myid")
<div id="myid"></div>
> document.write('<span id="myid"></span>')
> document.getElementById("myid")
<div id="myid"></div>
It appears that it only returns the first value- could someone clarify that?
It will return the first element with the ID in the document because of the way the code runs.
Logically it is illegal for there to be two elements with the same id. So why look for another element with the same id as the one that is already found, there should only be one element with this ID. Most browsers scan the document from the top down, so the first element with a matching ID is returned.
I can't speak for all browsers, but I've been able to use the same ID multiple times only if they are a child of a uniquely ID'd item:
<div id="parent1">
<div id="item"></div>
</div>
<div id="parent2">
<div id="item"></div>
</div>
I could always access each individually if I included their parent name (using jQuery)
$("#parent1 #item") and $("#parent2 #item")
Different browsers may return different things because having duplicate IDs is not valid HTML in the first place!
This is undefined behavior.
MDN doesn't say what happens because that's illegal in the first place.
IDs must be unique.

JQuery "#id.class" Selector Mashup

I have some bullet points which I want to show more text below them on clicking them. They are both two separate Ps that are paired together by sharing a common id. So, what I am trying to do below is to find the element with (id_same_as_this.class), so that the element with the class "expand" as well as the id that matches the clicked on P is toggled. Does that make sense?
$(document).ready(function(){
$(".expandable").click(function(){
$(this.attr('id')+"."+"expand").toggle(800);
});
});
I only ask if the above code could be made to work because it would make the expandable bullet points in my web page significantly less code intensive than a lot of the examples I have read about.
$(this.attr('id')+"."+"expand").toggle(800);
Must be
$("#" + this.id +".expand").toggle(800);
You missed the # there. That said, you shouldn't ever have a common ID. By definition IDs are meant to be unique. If you have the same ID on multiple elements, while it may work now on the browsers you try, you have no guarantee it won't break in the next rev of jQuery (or Chrome, or Konqueror, or iOS Safari). There's also no reason to do it. You could just use classes or data-* attributes.
Yes this will work but you need a # before the ID
They are both two separate Ps that are paired together by sharing a common id.
IDs are unique. Two elements can't share a common ID, as that defeats the whole purpose of having a unique identifier. JavaScript assumes that you're using valid HTML, so document.getElementById() will return only the first element with a matching id. By using non-unique IDs, things will start breaking in unpredictable ways:
$('#foo').find('.bar') // Won't search past first #foo
$('#foo .bar') // Will search past first #foo in IE8+
Try restructuring your HTML to make this task easier. Maybe you could do something like this:
<ul id="bullets">
<li>
<h2>Title</div>
<div>Text</div>
</li>
</ul>
And then use a simple event handler:
$('#bullets h2').click(function() {
$(this).next().toggle(800);
});
You don't need id values for this at all (which is good, as from the comments on hungerpain's answer, you're using the same id value on more than one element, which is invalid).
Just do this:
$(document).ready(function(){
$(".expandable").click(function(){
$(this).find(".expand").toggle(800);
});
});
That will find the element with the class expand within the expandable that was clicked. No relying on unspecified behavior of selectors.
If you really need that data on the expandable, just put it in a data-* attribute. So instead of this invalid structure:
<!-- INVALID -->
<div id="foo27" class="expandable">
<div class="expand">...</div>
</div>
<div id="foo27" class="expandable">
<div class="expand">...</div>
</div>
Do this
<!-- VALID -->
<div data-id="foo27" class="expandable">
<div class="expand">...</div>
</div>
<div data-id="foo27" class="expandable">
<div class="expand">...</div>
</div>
Use the above code to do the expansion. If you need the value, use .attr("data-id") or .data("id") to get it.

DOM: element created with cloneNode(true) missing element when added to DOM

I'm creating a tree control and I'm attempting to use a parent element as a template for its children. To this end I'm using the element.cloneNode(true) method to deep clone the parent element. However when I insert the cloned element into the DOM it is missing certain inner elements despite having an outerHTML value identical to its parent. Surprisingly I observe the same behavior is in IE, Firefox, and Chrome leading me to believe that it is by design.
This is the HTML for the node I'm attempting to clone.
<SPAN class=node><A class=nodeLink href="/SparklerRestService2.aspx?q={0}" name=http://dbpedia.org/data/Taylor_Swift.rdf>
<IMG class=nodeIcon alt="Taylor Swift" src="images/node.png"><SPAN class=nodeText>Taylor Swift</SPAN></A><SPAN class=nodeDescription>Taylor Swift is a swell gall who is realy great.</SPAN></SPAN>
Once I've cloned the node using cloneNode(true) I examine the outerHTML property and find that it is indeed identical to the original.
<SPAN class=node><A class=nodeLink href="/SparklerRestService2.aspx?q={0}" name=http://dbpedia.org/data/Taylor_Swift.rdf><IMG class=nodeIcon alt="Taylor Swift" src="images/node.png"><SPAN class=nodeText>Taylor Swift</SPAN></A><SPAN class=nodeDescription>Taylor Swift is a swell gall who is realy great.</SPAN></SPAN>
However when I insert it into the DOM and inspect the result using FireBug I find that the element has been transformed:
<span class="node" style="top: 0px; left: 0px;"<a class=nodeLink href="/SparklerRestService2.aspx?q={0}" name=http://dbpedia.org/data/Taylor_Swift.rdf>Taylor Swift</a><span class="nodeDescription">Taylor Swift is a swell gall who is realy great.</span></span>
Notice that the grandchildren of the node (the image tag and the span tag surrounding "Taylor Swift") are missing, although strangely the great grandchild "Taylor Swift" text node has made it into the tree.
Can anyone shed some light on this behavior? Why would nodes disappear after insertion into the DOM, and why am I seeing the same result in all three major browser engines?
I tried it and it works fine. I notice you say that the text changed to say "It's great" which wasn't even in the original. Sorry but I'm not buying that.

Categories