javascript xpath - get element value and its position, and save results in object array - javascript

I have a markup resembling the following (note: the spans are optional child elements):
<li class="prices">
11.11
<span>1.11</span>
</li>
<li class="prices">
22.22
</li>
<li class="prices">
33.33
<span>3.33</span>
</li>
<li class="prices">
44.44
</li>
<li class="prices">
55.55
<span>5.55</span>
</li>
I want to return a list of prices within the spans AND its position in an object array, so I get something like: { pos: 0, price, 1.11 }, { pos: 2, price, 3.33 }, { pos: 4, price, 5.55 } What's the JS code to do that? :)

Assuming that you have a container element which contains all your li.prices elements:
var container = document.getElementById("container");
var arr = [];
for (var pos = 0; pos < container.children.length; pos++) {
var li = container.children[pos];
var spans = li.getElementsByTagName("span");
var price = spans.length > 0 ? spans[0].innerHTML : "";
arr.push({pos: pos, price: price});
}
This will store the price as a string and it will use an empty string if there is no span element inside a li element.

You will at least need XPath 2.0 to do this.
For getting a string representation like you proposed, use for example
string-join(
for $li at $position in //li
return concat("{ pos: ", $position, ", price: ", $li/span/data(), " }")[$li/span]
, ', ')
If you prefer an array, you could return everything in a sequence and read odd/even pairs from it in JavaScript:
for $li at $position in //li
return ($position, $li/span/data())[$li/span]

Related

Get attributes for all (unknown) <li> elements with Javascript

I need to concatenate all the title value starting from second li elements with Javascript.
The problem is that I want to use it in different pages, so I can't know the exact number of li elements.
<div id="breadcrumb">
<ul>
<li title="One">One</li>
<li title="Two">Two</li>
<li title="Three">Three</li>
<li title="Four">Four</li>
</ul>
</div>
I use a variable for each element but if one or more element is missing the var is not valid and the concat function doesn't work.
var a = document.querySelector(".breadcrumb li:nth-child(2) > a").getAttribute("title");
var b = document.querySelector(".breadcrumb li:nth-child(3) > a").getAttribute("title");
var c = document.querySelector(".breadcrumb li:nth-child(4) > a").getAttribute("title");
var d = document.querySelector(".breadcrumb li:nth-child(4) > a").getAttribute("title");
var str = a.concat(b,c,d);
console.log(str)
Is there a way to do that?
Use querySelectorAll() and map():
const res = [...document.querySelectorAll("#breadcrumb li:not(:first-of-type)")].map(el => el.getAttribute("title")).join(" ")
console.log(res)
<div id="breadcrumb">
<ul>
<li title="One">One</li>
<li title="Two">Two</li>
<li title="Three">Three</li>
<li title="Four">Four</li>
</ul>
</div>
Using a little jquery i achieved this and it should solve your issues.
let list = [];
$('#breadcrumb li').each(function(i){
if(i !== 0) { list.push($(this).attr('title')); }
});
list.toString() //One,Two,Three,Four
The method you tried to use wont scale on a large list
Two minor remarks:
If you want to access the id=breadcrumb, you have to use #breadcrumb instead of .breadcrumb
There is no a tag in your HTML-code, therefore your querySelector won't give you any result
However, let's discuss a solution:
let listElements = document.querySelectorAll("#breadcrumbs li"); // get all list elements
listElements = Array.from(listElements); // convert the NodeList to an Array
listElements = listElements.filter((value, index) => index >= 1); // remove the first element
let titleAttributes = listElements.map(listElement => listElement.getAttribute("title")); // get the title attributes for every list elements. The result is an array of strings containing the title
console.log(titleAttributes.join(", ")); // concatenate the titles by comma
You can write the above statements in a single line:
Array.from(document.querySelectorAll("#breadcrumbs li"))
.filter((value, index) => index >= 1)
.map(listElement => listElement.getAttribute("title"))
.join(", ");
EDIT: I fix my answer, thanks to Barmar
something like that ?
const All_LI = [...document.querySelectorAll('#breadcrumb li')];
let a = ''
for (let i=1; i<All_LI.length; i++) { a += All_LI[i].title }
console.log('a-> ', a )
// .. or :
const b = All_LI.reduce((a,e,i)=>a+=(i>0)?e.title:'', '' )
console.log('b-> ', b )
<div id="breadcrumb">
<ul>
<li title="One">One</li>
<li title="Two">Two</li>
<li title="Three">Three</li>
<li title="Four">Four</li>
</ul>
</div>

How to convert html tree in to a customized json tree using jquery?

<ul id='parent_of_all'>
<li>
<span class='operator'>&&</span>
<ul>
<li>
<span class='operator'>||</span>
<ul>
<li>
<span class='operator'>&&</span>
<ul>
<li>
<span class='condition'>1 == 1</span>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<ul>
<li>
<span class='condition'>1 != 0</span>
</li>
</ul>
</li>
</ul>
to
{"&&":[{'||':[ {'&&':[ {"lhs": "1", "comparator": "==", "rhs":"1"} ]} ] } , {"lhs": "1", "comparator": "!=", "rhs":"0"}]}
As of now, I know the basics of jQuery, JavaScript. I need to know where to start thinking in order to accomplish the above conversion.
And the html tree could be more complex with more children.
You can do this with each and map
var obj = {}
var span = $('li > span').not('ul li span').text();
$('ul li span').each(function() {
var text = $(this).text().split(' ');
obj[span] = (obj[span]||[]).concat({lhs: text[0], comparator: text[1], rhs: text[2]});
});
console.log(obj)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<li>
<span>&&</span>
<ul>
<li>
<span>1 == 1</span>
</li>
</ul>
<ul>
<li>
<span>1 != 0</span>
</li>
</ul>
</li>
You will need a way to select the first level of li, I assumed you have a parent element with an id such as list. I wrote the following code using basic jquery so you can understand it.
var result = {};
var $all_li = $('#list').children('li'); // selecting the first level of li
for(var i in $all_li){ // iterating all_li using for (you may use forEach )
var $current_li = $( $all_li[i] ); // getting operator from first span
var operator = $current_li.children('span').html(); // the text of the operator
var $inner_spans = $current_li.find('>ul >li >span'); // getting list of children spans (from path $list>li>ul>li>span)
var li_spans = []; // an array where we will put the inner span objects
for(var j in $inner_spans){ // iterating the inner spans
var text = $($inner_spans[j]).html().split(" "); // splitting the html
li_spans.push({
lhs: text[0],
comparator: text[1],
rhs: text[2]
}); // adding the splitted html to an object. Note: error if text didn't have 2 white spaces
}
result[operator] = li_spans; // adding the operator key and li_spans value to the result json
}
This code will parse the html and construct the result json, it should work for the html format you provided. Keep in mind that it does not handle errors (such as bad tree format).
simmiar html formats.
Thanks #Alexandru and #Nenad for giving a start. I have been able to complete this on my own.
Below is the function that generates json.
function prepare_json(current_node){
var object = {}
var span = $(current_node).children('span')
if (span.hasClass('condition')){
var text = span.html().split(" ");
object = {lhs: text[0], comparator: text[1], rhs: text[2]}
}
else if(span.hasClass('operator')){
var operator = span.text()
object[operator] = (object[operator] || [])
var children = $(current_node).children('ul').children('li')
for(var i = 0; i < children.length; i++){
var child_pql = prepare_json([children[i]])
object[operator].push(child_pql)
}
}
return object
}
Below is the code that calls that function:
var parent_node = $('#parent_of_all').children('li')
var json = JSON.stringify(prepare_pql_json(parent_node), null, 2)

How do I get all the LI UL elements ID value and place them in a JavaScript array?

I have to embed some tracking code on my site. So I have a list of LI elements with an ID value that I want to place inside an array of the snippet. They should be numeric like, 123, 456, etc inside an object. I want to do it in pure JavaScript.
This is my code I have tried. My HTML:
<ul id="itemGrid">
<li class="item" id="1080"> product code </li>
<li class="item" id="1487"> product code </li>
<li class="item" id="1488"> product code </li>
...
</ul>
This is the JavaScript code
// Get all LI items and get the ID of them in the object viewList
var catId = document.getElementById('itemGrid').getElementsByTagName('li');
window.criteo_q = window.criteo_q || [];
window.criteo_q.push(
// SHOULD BE LIKE THIS
// { event: "viewList", item: ["First item id", "Second item id", "Third item id"] }
// My actual code
{ event: "viewList", item: [ catId[].id ] }
);
try this
var lis = document.getElementById('itemGrid').getElementsByTagName('li');
var idArray = [];
for ( var counter = 0; counter < lis.length; counter++)
{
idArray.push( lis[ counter ].id );
}
console.log( idArray );
You can use querySelectorAll to select all the matching elements passed as selector.
The selector '#itemGrid li[id]' will select all the <li> elements inside #itemGrid element having id attribute on it.
The querySelectorAll returns a collection of HTML elements. Iterate over this collection to get the individual element id.
var lis = document.querySelectorAll('#itemGrid li[id]');
var arr = [];
for (var i = 0; i < lis.length; i++) {
arr.push(+lis[i].id);
}
console.log(arr);
document.write('<pre>' + JSON.stringify(arr, 0, 4) + '</pre>');
<ul id="itemGrid">
<li class="item" id="1080">1080</li>
<li class="item" id="1487">1487</li>
<li class="item" id="1488">1488</li>
</ul>
<hr />
You can convert your HTMLCollection to an Array by passing it through slice, and you can then map that array:
catId = Array.prototype.slice.call(catId).map(function(li) { return li.id; });
var catId = document.getElementById('itemGrid').getElementsByTagName('li');
catId = Array.prototype.slice.call(catId).map(function(li) { return li.id; });
document.write(catId);
<ul id="itemGrid">
<li class="item" id="1080"> product code </li>
<li class="item" id="1487"> product code </li>
<li class="item" id="1488"> product code </li>
</ul>
var lis = document.getElementById('itemGrid').getElementsByTagName('li');
var arr = [];
// You need to iterate the list lis
[].forEach.call( lis, function(el){
arr.push( el.id );
});
// Making the object you want
var criteo_q = { event: "viewList", item: arr };
console.log(criteo_q);
To iterate the list of DOM elements, you can also use

create simple js object and store them into js array

Inside html dom I have following structure
<div id="myTitles">
<ul>
<li>
Title:<title>A</title><br>
Second title: <secTitle>B</secTitle><br>
Third title: <thirdTitle>3</thirdTitle>
</li>
<hr>
<li>
Title:<title>B</title><br>
Second title: <secTitle>C</secTitle><br>
Third title: <thirdTitle>9</thirdTitle>
</li>
</ul>
</div>
Number of list elements inside ul of myTitles div is unknown on runtime. So I'm taking var nrOfTitles = $('#myTitles ul li').length; to determine how many is there.
Now, I want to create some simple javascript object which will hold those values (stored inside this semantic tags (title, secTitle, thirdTitle)) and store them inside js array.
var items = $("#myTitles > ul > li");
var arr = [];
items.each(function(){
arr.push([$(this).find("title").text(),$(this).find("secTitle").text(),$(this).find("thirdTitle").text()])
})
With your current combination of <li>s it would contain:
[["A","B","3"],["B","C","9"]]
Or you can store the properties as objects:
var items = $("#myTitles > ul > li");
var arr = [];
items.each(function(){
arr.push({title:$(this).find("title").text(),secTitle:$(this).find("secTitle").text(),thirdTitle:$(this).find("thirdTitle").text()})
})
Then you would get
[{title:"A",secTitle:"B",thirdTitle:"3"},{title:"B",secTitle:"C",thirdTitle:"9"}]
You can use $.fn.map method to conveniently create an array of objects:
var obj = $('#myTitles ul li').map(function() {
return {
title: $(this).find('title').text(),
secTitle: $(this).find('secTitle').text(),
thirdTitle: $(this).find('thirdTitle').text()
};
}).get();
alert(JSON.stringify(obj, null, 4));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="myTitles">
<ul>
<li>
Title:<title>A</title><br>
Second title: <secTitle>B</secTitle><br>
Third title: <thirdTitle>3</thirdTitle>
</li>
<hr>
<li>
Title:<title>B</title><br>
Second title: <secTitle>C</secTitle><br>
Third title: <thirdTitle>9</thirdTitle>
</li>
</ul>
</div>
But since you are using custom tags in HTML, remember to create them for IE8, if you plan so support this browser, like document.createElement('title');.

What is the best way in jQuery to wrap elements based on its data attribute?

Given the following structure:
<ul>
<li data-conference="Conference1" >Spain</li>
<li data-conference="Conference1" >France</li>
<li data-conference="Conference1" >Germany</li>
<li data-conference="Conference1" >Italy</li>
<li data-conference="Conference2" >Austria</li>
<li data-conference="Conference2" >Poland</li>
<li data-conference="Conference3" >Russia</li>
<li data-conference="Conference3" >USA</li>
<li data-conference="Conference3" >China</li>
</ul>
what is the best way (with jQuery), considering performance, to rearrange this into this:
<ul>
<li>Spain</li>
<li>France</li>
<li>Germany</li>
<li>Italy</li>
</ul>
<ul>
<li>Austria</li>
<li>Poland</li>
</ul>
<ul>
<li>Russia</li>
<li>USA</li>
<li>China</li>
</ul>
Thanks!
I think the overall question (group elements by attribute) is good, you just should have put more effort into trying to solve it yourself.
Anyways, grouping elements by an attribute is quite simple. You can create an attribute value -> [element, ...] map, which can be done with an object:
var groups = {};
$('li[data-city]').each(function() {
var attr = $(this).attr('data-city'),
group = groups[attr];
if(!group) {
group = groups[attr] = [];
}
group.push(this);
});
Now you have a collection of lists of DOM elements. You can iterate over the collection and create the HTML lists accordingly.
For example:
for(var group in groups) {
var $list = $('<ul />');
$list.append(groups[group]);
// now append $list somewhere
}
Have a look at Working with Objects [MDN] to get more information about how to process objects.
It's also trivial to do this without jQuery, as long as you have references to the elements, for example as a NodeList. Instead of using .each you can then use a "normal" for loop to iterate that list.
Unless you have a insane amount of cities in those lists I wouldn't worry about performance. The only performance consideration I would take is to avoid repaint / reflows by minimizing writing to the DOM. I think code clarity is much more important in this use case.
That being said I'd implement this with something like this - http://jsfiddle.net/XWufy/.
Here you go:
(function () {
var $list = $( '#list' );
var lists = {};
var $newLists = $();
$list.children().each( function () {
var city = $( this ).data( 'city' );
if ( !lists[ city ] ) lists[ city ] = [];
lists[ city ].push( this );
});
$.each( lists, function ( city, items ) {
var $newList = $( '<ul />' ).append( items );
$newLists = $newLists.add( $newList );
});
$list.replaceWith( $newLists );
}());
Live demo: http://jsfiddle.net/rjt9W/6/
Btw, the code assumes that the list has an ID of "list". Replace the selector in this line
var $list = $( ... );
so that it properly selects your UL element.
Use the data attribute as an object property to sort them, then loop over them to construct the new html. this should get you started:
var list = {};
// for each item
list[item.data('city')] = item.text();
// for each property of list
var ul = $('<ul>');
// for each listItem in current list
var li = $('<li>').text(listItem);
ul.append(li);
try this:
<ul id="first"></ul>// you can create the ul tags by using JavaScript
$("li").each(function(){
data = $(this).attr("data");
if (data == "Conference1") {
txt = $(this).text();
$("<li>" + txt + "</li>").appendTo("ul#first");
}
})
Try this:
var list = [];
var $div = $('#my_container_div');
$('li[data-city]').each(function() {
var $this = $(this), data = $this.attr('data-city');
list[ data ] = list[ data ] || [];
list[ data ].push( $this.text() );
});
for(var data in list) {
var $ul = $div.append('<ul/>');
for(var li in list[data]) {
$ul.append('<li>' + list[data][li] + '</li>');
}
}

Categories