Complex JavaScript DOM query - javascript

Consider this markup:
<div data-something="something">
<div>
<div>
<div data-something="something">
<div data-something="something"></div>
</div>
</div>
</div>
</div>
<div data-something="something"></div>
I would like to query (not necessarily but preferably with a single query) all the elements that do not have any children with the "data-something" attribute. In other words, I would like to query all top level elements that do have this attribute. Please note that all this elements are not necessarily immediate descendants of the root element.
So the query should in this case produce two elements. The first one and the last one. Both two children of the first element should be omitted.
The best approach I could think of took about five seconds to process 300 elements which is completely inadequate and I would rather not even show it :)

Including Sizzle into Dojo worked.

Related

Do HTML elements have 'hidden indexes' for the DOM?

For example, document.getElementsByClassName("whatever") returns a list of elements, and each element has an index (so the element x is the [3] in that list, for example).Do HTML elements save that index inside the element, somehow? Or they're 'unaware' of their position?
Example of the usage I'd do with that property:
You click an element with class "People", using event.target when onclick. So you want to know which position it has, in the 'People' list. Let's say it's event.target.classNameIndex. So once you know the index, you can do things in JavaScript.
Obviously the simple alternative I can think of this is simply picking event.target and searching it inside the getElementsByClassName list. Or simply giving IDs to all elements. But avoiding that would be nice.
Hope you understand my question. :)
No
The elements are generated either dynamically or statically and are independent from everything done with them after being displayed. There are pure javascript ways of obtaining the index of an element in a array-like structure but they will most likely depend on the use of a element.onClick function and pairing them with other elements via some sort of selector.
No, for lots of reasons.
First of all, you are doing a query on the internal DOM structure, and the DOM tree itself might change immediately after your query. Elements can be added, moved or removed.
Furthermore, two very different queries might have overlapping results. E.g. query 1 might return:
[ <div id="a">, <div id="b"> ]
While query 2 could return:
[ <div id="b">, <div id="c"> ]
(for simplicity I am representing the results as arrays)
In the above, how would the element <div id="b"> know its unique and unchanging "index", given the truly infinite amount of possible queries, not the mention the possibly variable DOM again?

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).

css selector for nth element of type - weird implementation of nth-of-type()

I may be stupid but I'm trying to select the 2nd div that has the class specialclass. Whats the right querySelector for this?
For test purpost I thought to use document.querySelector("#content-wrapper > .specialclass:nth-of-type(2)"); I thought nth-of-type is used for this. but it returns undefined.nth-of-type as well as nth-child seem to count from the parent element. The problem is that the order of elements change pretty often.
Website Structure
<div id="content-wrapper">
<div class="a"></div>
<div class="a"></div>
<div class="specialclass">Can be selectet through document.querySelector(".specialclass:nth-of-type(3)");</div>
<div class="a"></div>
<div class="a"></div>
<div class="a"></div>
<div class="specialclass">element I'd like to select. Can be selectet through document.querySelector(".specialclass:nth-of-type(7)");</div>
<div class="a"></div>
<div class="a"></div>
<div class="a"></div>
</div>
IMPORTANT: Please be aware I CANNOT change the website markup!
The "type" in :nth-of-type() refers to the element type, which in this case is div. Since the children are all div elements, :nth-of-type() functions identically to :nth-child(). This is entirely by design.
While the general problem of trying to select the nth child matching a specific selector is commonly associated with a misconception of how :nth-of-type() works, this question isn't quite a duplicate of all the rest because they are all CSS-based, which poses certain limitations that are not present in selector APIs, such as the one provided by the DOM.
In your specific case, instead of trying to retrieve an individual element using querySelector(), you can use querySelectorAll() to select all the .specialclass elements and index off of the resulting node list to get the one you want (remember that this is a zero-based index, unlike the structural pseudo-classes which are one-based):
var secondElement = document.querySelectorAll("#content-wrapper > .specialclass")[1];
You could also use something like
var secondElement = document.querySelector("#content-wrapper > .specialclass ~ .specialclass");
as mentioned in the comments, and querySelector() (note: not querySelectorAll()) will only pick up the first such element — this is always the closest .specialclass that is a following sibling of the first .specialclass regardless of how many of these elements there are. However I prefer indexing off a node list as the intent is much clearer.

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.

Categories