Multiple class query not working - javascript

I have this HTML-element:
<div class="option default"></div>
...and I try to get to it in Dojo like this:
var el = dojo.query(".option.default");
But when I try:
alert(dojo.attr(el, "class"));
...I get undefined.
Update:
For some reason, this works:
alert(el.attr("class"));
How come the other method doesn't work?

dojo.query() returns a list of DOM nodes based on a CSS selector, not a single element.
You would need to do:
var els = dojo.query(".option.default");
var el = els[0];
alert(dojo.attr(el, "class"));
Here's a working example: http://jsfiddle.net/ArtBIT/L35k6/

Would add to the comment but I can't.
#Orolin, how does Dojo know that you know you're expecting a single Node to be returned. If it special cased single elements then anywhere you call dojo.query() and genuinely don't know how many elements you're expecting to get back, you'd have to test yourself. That would be horrid, and take more than three characters!
If you really want this functionality, just do one of the following:
dojo.queryFirst = function() {
return(dojo.query(arguments)[0]);
}
or, if you must
dojo._oldQuery = dojo.query;
dojo.query = function() {
var nodes = dojo._oldQuery(arguments);
return nodes.length > 1 ? nodes : nodes[0];
}

Related

parentNode.removeChild not removing last element. But why?

I am working on a javascript code where I can clone an element, but also want to delete one on click. Cloning works, but I can't remove one.
I have the following elements.
<input list="accountsdeb" name="deblist[1]" id="deblist" autocomplete="off">
<input list="accountsdeb" name="deblist[2]" id="deblist" autocomplete="off">
And now I want to remove the last one on click (last = highest number).
function remove1() {
var dbl = document.querySelectorAll('#deblist').length;
var dbla = dbl;
var elem = document.getElementsByName("deblist["+dbla+"]");
alert(dbla);
//var last = debelelast - 1;
elem.parentNode.removeChild(elem);
}
As an orientation I used to have a look on an example from W3S and Stack. I have also seen that this works:
var elem = document.getElementById("myDiv");
elem.parentNode.removeChild(elem);
But this is random and as you can see I have tried to include this in my code.
The error I get is:
Uncaught TypeError: Cannot read property 'removeChild' of undefined
at HTMLAnchorElement.remove1 (index.php:179)
Where's the problem in my code, where is my thinking wrong?
I see two issues in the piece of code you provided,
deblist is used as id for 2 elements which is not advisable and due to this document.querySelectorAll('#deblist').length returns 2 (I am not sure if you intending to do so)
document.getElementsByName() (check here) will return a NodeList which needs to be iterated in order to access any of the returned elements. So here you need to select the child element by giving its index. In your case elem will have one element for the matched name deblist[2] and hence you need to access it like elem[0] for selecting its parent and deleting its child.
So the updated the code would be,
var dbl = document.querySelectorAll('#deblist').length;
var dbla = dbl;
// console.log('dbla', dbla);
var elem = document.getElementsByName("deblist["+dbla+"]");
// console.log('elem 0', elem[0]);
// console.log('elem parentNode', elem[0].parentNode);
//var last = debelelast - 1;
elem[0].parentNode.removeChild(elem[0]);
Check the fiddle here
If the inputs are part of a group they could share a name property or such, and the use of jQuery could help you do something like...
$("input[name='group1']").last().parent().remove()
Or if not part of a group then just....
$("input").last().parent().remove()

Traversing elements in javaScript

I need to change the href of link in a box. I can only use native javaScript. Somehow I have problems traversing through the elements in order to match the correct <a> tag.
Since all the a tags inside this container are identical except for their href value, I need to use this value to get a match.
So far I have tried with this:
var box = document.getElementsByClassName('ic-Login-confirmation__content');
var terms = box.querySelectorAll('a');
if (typeof(box) != 'undefined' && box != null) {
for (var i = 0; i < terms.length; i++) {
if (terms[i].href.toLowerCase() == 'http://www.myweb.net/2/') {
terms[i].setAttribute('href', 'http://newlink.com');
}
}
}
However, I keep getting "Uncaught TypeError: box.querySelectorAll is not a function". What do I need to do in order to make this work?
Jsfiddle here.
The beauty of querySelectorAll is you dont need to traverse like that - just use
var terms = document.querySelectorAll('.ic-Login-confirmation__content a');
And then iterate those. Updated fiddle: https://jsfiddle.net/4y6k8g4g/2/
In fact, this whole thing can be much simpler
var terms = document.querySelectorAll('.ic-Login-confirmation__content a[href="http://www.myweb.net/2/"]');
if(terms.length){
terms[0].setAttribute('href', 'http://newlink.com');
}
Live example: https://jsfiddle.net/4y6k8g4g/4/
Try This:
var box = document.getElementsByClassName('ic-Login-confirmation__content')[0];
Since you are using getElementsByClassName ,it will return an array of elements.
The getElementsByClassName method returns returns a collection of all elements in the document with the specified class name, as a NodeList object.
You need to specify it as follows for this instance:
document.getElementsByClassName('ic-Login-confirmation__content')[0]
This will ensure that you are accessing the correct node in your HTML. If you console.log the box variable in your example you will see an array returned.
you can select by href attr with querySelector,
try this:
document.querySelector('a[href="http://www.myweb.net/2/"]')
instead of defining the exact href attribute you can simplify it even more :
document.querySelector('a[href?="myweb.net/2/"]'
matches only elments with href attribute that end with "myweb.net/2/"

Javascript - How to get attribute value from a tag, inside a specific div class?

Snippet of HTML code I need to retrieve values from:
<div class="elgg-foot">
<input type="hidden" value="41" name="guid">
<input class="elgg-button elgg-button-submit" type="submit" value="Save">
</div>
I need to get the value 41, which is simple enough with:
var x = document.getElementsByTagName("input")[0];
var y = x.attributes[1].value;
However I need to make sure I'm actually retrieving values from inside "elgg-foot", because there are multiple div classes in the HTML code.
I can get the class like this:
var a = document.getElementsByClassName("elgg-foot")[0];
And then I tried to combine it in various ways with var x, but I don't really know the syntax/logic to do it.
For example:
var full = a.getElementsByTagName("input")[0];
So: Retrieve value 41 from inside unique class elg-foot.
I spent hours googling for this, but couldn't find a solution (partly because I don't know exactly what to search for)
Edit: Thanks for the answers everyone, they all seem to work. I almost had it working myself, just forgot a [0] somewhere in my original code. Appreciate the JQuery as well, never used it before :-)
The easiest way is to use jQuery and use CSS selectors:
$(".elgg-foot") will indeed always get you an element with class "elgg-foot", but if you go one step further, you can use descendent selectors:
$(".elgg-foot input[name='guid']").val()
That ensures that you only get the input named guid that is a child of the element labelled with class elgg-foot.
The equivalent in modern browsers is the native querySelectorAll method:
document.querySelectorAll(".elgg-foot input[name='guid']")
or you can do what you have yourself:
var x = document.getElementsByClassName("elgg-foot")
var y = x.getElementsByTagName("input")[0];
Assuming you know it is always the first input within the div
You can combine it like this:
var a = document.getElementsByClassName("elgg-foot")[0];
var b = a.getElementsByTagName("input")[0];
var attribute = b.attributes[1].value;
console.log(attribute); // print 41
Think of the DOM as the tree that it is. You can get elements from elements in the same way you get from the root (the document).
You can use querySelector like
var x = document.querySelector(".elgg-foot input");
var y = x.value;
query the dom by selector https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector
var fourty1 = document.querySelector('.elgg-foot input[name=guid]').value;
querySelector will return the first match from the selector. This selector will find the element with class elgg-foot and then look at the input element inside of that for one named guid and then take the value of the selected element.
I think the simplest way would be using JQuery. But using only javascript,
the simplest way would be:
var div = document.getElementsByClassName("elgg-foot")[0];
var input = div.getElementsByTagName("input")[0];
alert(input.value)
Take a look at this JSFiddle here: http://jsfiddle.net/2oa5evro/

InDesign Scripting: Deleting elements from the structure panel

I've imported some XML files inside InDesign (you can see the structure in the picture below) and I've also created a script to get some statistics concerning this hierarchy.
For example, to count the "free" elements:
var items = app.activeDocument.xmlElements.everyItem();
var items1 = items.xmlElements.itemByName("cars");
var cars = items1.xmlElements.everyItem();
var c_free = cars.xmlElements.itemByName("free");
var cars_free = c_free.xmlElements.count().length;
I also have apartments in my structure that's why I'm using itemByName.
The code above returns the correct number of free cars in my structure.
What I'm trying to do - without any luck so far - is to target those free items (inside cars) and either delete all of them or a specific number.
My last attempt was using:
var del1 = myInputGroup2.add ("button", undefined, "Delete All");
del1.onClick = function () {
cars.xmlElements.everyItem().remove();
}
inside a dialog I've created.
Any suggestions will be appreciated cause I'm really stuck here.
I would probably use XPath for this. You can use evaluateXPathExpression to create an array of the elements you want to target. Assuming your root element is cars and it contains elements called cars1, and you want to delete all free elements within a cars1 element, you could do something like:
var myDoc = app.activeDocument;
//xmlElements[0] is your root element, in this case "cars". The xPath expression is evaluated from cars.
//evaluateXPathExpression returns an array of all of the free elements that are children of cars.
var myFrees = myDoc.xmlElements[0].evaluateXPathExpression("cars1/free");
for (var i = myFrees.length - 1; i>=0; i--){
myFrees[i].remove();
}
Tweaking this would require some knowledge of xPath, but it's not terribly hard to learn the basics and it does seem like the simplest approach.
I think your main problem was that XMLElements hasn't a itemByName method. You can only reference XMLElements through their indeces or ids.
Secondly you assume that you got xmlElements from XPath expression but it's likely that you got nothing as your xpath seems uncorrect.
var myFrees = myDoc.xmlElements[0].evaluateXPathExpression("./cars1/free");
var n = myFrees.length;
if ( !n ) {
alert("Aucun élément trouvé");
}
else {
while (n--) myFrees[n].remove();
}
You need to start your expression by setting the origin of your xpath. Here a dot "./" is used to tell you want to look for cars1/free xml elements at the "root" of the xmlelement. Using "//" on the contrary would have returned any cars/free items unregardingly of their locations.

Select tags that starts with "x-" in jQuery

How can I select nodes that begin with a "x-" tag name, here is an hierarchy DOM tree example:
<div>
<x-tab>
<div></div>
<div>
<x-map></x-map>
</div>
</x-tab>
</div>
<x-footer></x-footer>
jQuery does not allow me to query $('x-*'), is there any way that I could achieve this?
The below is just working fine. Though I am not sure about performance as I am using regex.
$('body *').filter(function(){
return /^x-/i.test(this.nodeName);
}).each(function(){
console.log(this.nodeName);
});
Working fiddle
PS: In above sample, I am considering body tag as parent element.
UPDATE :
After checking Mohamed Meligy's post, It seems regex is faster than string manipulation in this condition. and It could become more faster (or same) if we use find. Something like this:
$('body').find('*').filter(function(){
return /^x-/i.test(this.nodeName);
}).each(function(){
console.log(this.nodeName);
});
jsperf test
UPDATE 2:
If you want to search in document then you can do the below which is fastest:
$(Array.prototype.slice.call(document.all)).filter(function () {
return /^x-/i.test(this.nodeName);
}).each(function(){
console.log(this.nodeName);
});
jsperf test
There is no native way to do this, it has worst performance, so, just do it yourself.
Example:
var results = $("div").find("*").filter(function(){
return /^x\-/i.test(this.nodeName);
});
Full example:
http://jsfiddle.net/6b8YY/3/
Notes: (Updated, see comments)
If you are wondering why I use this way for checking tag name, see:
JavaScript: case-insensitive search
and see comments as well.
Also, if you are wondering about the find method instead of adding to selector, since selectors are matched from right not from left, it may be better to separate the selector. I could also do this:
$("*", $("div")). Preferably though instead of just div add an ID or something to it so that parent match is quick.
In the comments you'll find a proof that it's not faster. This applies to very simple documents though I believe, where the cost of creating a jQuery object is higher than the cost of searching all DOM elements. In realistic page sizes though this will not be the case.
Update:
I also really like Teifi's answer. You can do it in one place and then reuse it everywhere. For example, let me mix my way with his:
// In some shared libraries location:
$.extend($.expr[':'], {
x : function(e) {
return /^x\-/i.test(this.nodeName);
}
});
// Then you can use it like:
$(function(){
// One way
var results = $("div").find(":x");
// But even nicer, you can mix with other selectors
// Say you want to get <a> tags directly inside x-* tags inside <section>
var anchors = $("section :x > a");
// Another example to show the power, say using a class name with it:
var highlightedResults = $(":x.highlight");
// Note I made the CSS class right most to be matched first for speed
});
It's the same performance hit, but more convenient API.
It might not be efficient, but consider it as a last option if you do not get any answer.
Try adding a custom attribute to these tags. What i mean is when you add a tag for eg. <x-tag>, add a custom attribute with it and assign it the same value as the tag, so the html looks like <x-tag CustAttr="x-tag">.
Now to get tags starting with x-, you can use the following jQuery code:
$("[CustAttr^=x-]")
and you will get all the tags that start with x-
custom jquery selector
jQuery(function($) {
$.extend($.expr[':'], {
X : function(e) {
return /^x-/i.test(e.tagName);
}
});
});
than, use $(":X") or $("*:X") to select your nodes.
Although this does not answer the question directly it could provide a solution, by "defining" the tags in the selector you can get all of that type?
$('x-tab, x-map, x-footer')
Workaround: if you want this thing more than once, it might be a lot more efficient to add a class based on the tag - which you only do once at the beginning, and then you filter for the tag the trivial way.
What I mean is,
function addTagMarks() {
// call when the document is ready, or when you have new tags
var prefix = "tag--"; // choose a prefix that avoids collision
var newbies = $("*").not("[class^='"+prefix+"']"); // skip what's done already
newbies.each(function() {
var tagName = $(this).prop("tagName").toLowerCase();
$(this).addClass(prefix + tagName);
});
}
After this, you can do a $("[class^='tag--x-']") or the same thing with querySelectorAll and it will be reasonably fast.
See if this works!
function getXNodes() {
var regex = /x-/, i = 0, totalnodes = [];
while (i !== document.all.length) {
if (regex.test(document.all[i].nodeName)) {
totalnodes.push(document.all[i]);
}
i++;
}
return totalnodes;
}
Demo Fiddle
var i=0;
for(i=0; i< document.all.length; i++){
if(document.all[i].nodeName.toLowerCase().indexOf('x-') !== -1){
$(document.all[i].nodeName.toLowerCase()).addClass('test');
}
}
Try this
var test = $('[x-]');
if(test)
alert('eureka!');
Basically jQuery selector works like CSS selector.
Read jQuery selector API here.

Categories