Javascript/Jquery get child element, substract string and substitute - javascript

I looked a lot, but I couldn't find a solution to my problem :c I'm not a JS expert, but this might be an easy one for programmers.
I have a list, a nav list:
<ul id="menu-navbar">
<li id="menu-item-270-en" class="lang-item">
<a href="site.com/en">
English
</a>
</li>
<li id="menu-item-270-pt" class="lang-item">
<a href="site.com/">
Português
</a>
</li>
</ul>
Two things:
I need to get the text (English) from <a> inside the <li>, subtract and return only the first 2 letters. I want the site to show En.
For the Português text, I want it to show Pt instead. A replace(); function should do, shouldn't it?
Can't insert neither id nor class into <a>, because it's generated by a plugin in Wordpress.
For the full code, the site is http://yogmel.com and the list on the upper right, on the navigation bar.
Note: The English site is not fully functional yet.
Thank you so much!

For you requirement, its better to maintain a mapping object instead.
jQuery
var langListMap = [{
text: "English",
shortName: "En"
}, {
text: "Português",
shortName: "Pt"
}]
$(document).ready(function(){
$.each($("a"), function(i,a){
var sName = getObject($(a).text());
if(sName){
$(a).text(sName.shortName);
}
});
})
function getObject(text){
console.log(text)
return langListMap.filter(function(a){
return a.text === text.trim();
})[0]
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.1/jquery.min.js"></script>
<ul id="menu-navbar">
<li id="menu-item-270-en" class="lang-item">
<a href="site.com/en">
English
</a>
</li>
<li id="menu-item-270-pt" class="lang-item">
<a href="site.com/">
Português
</a>
</li>
<li id="menu-item-270-pt" class="lang-item">
<a href="site.com/">
Test
</a>
</li>
</ul>
Pure JS
var langListMap = [{
text: "English",
shortName: "En"
}, {
text: "Português",
shortName: "Pt"
}]
function getObject(text) {
return langListMap.filter(function(a) {
return a.text === text.trim();
})[0]
}
(function() {
var a = document.getElementsByTagName("a");
for (var i = 0; i < a.length; i++) {
var sName = getObject(a[i].innerHTML);
if (sName) {
a[i].innerHTML = sName.shortName;
}
}
})();
<ul id="menu-navbar">
<li id="menu-item-270-en" class="lang-item">
<a href="site.com/en">
English
</a>
</li>
<li id="menu-item-270-pt" class="lang-item">
<a href="site.com/">
Português
</a>
</li>
<li id="menu-item-270-pt" class="lang-item">
<a href="site.com/">
Test
</a>
</li>
</ul>

To use plain JavaScript for this, rather than using the jQuery library, I'd suggest:
'use strict'
// declaring the variable 'abbreviationsMap' as a 'constant'
// to prevent rebinding later in the script (although it can
// still be updated, eg:
// abbreviationsMap.English = 'xy', or
// abbreviationsMap.Welsh = 'cy'
const abbreviationsMap = {
'English': 'En',
'Português': 'Pt'
};
// named function to handle the creation of abbreviations,
// with user-supplied options:
function abbreviations(opts) {
// declaring variables using 'let' to limit the
// variables to the scope of the block in which
// they're declared:
let settings = {
// the CSS selector by which the elements whose
// content should be abbreviated should be selected:
'selector': '.toAbbreviate',
// the map of full-words-to-abbreviations to use:
'abbreviations': abbreviationsMap
};
// iterating over the properties declared in the
// opts Object to update the settings:
for (let property in opts) {
// if the opts Object has a named property
// not inherited from its prototype equal to
// the current property-name:
if (opts.hasOwnProperty(property)) {
// we update the same property of the
// settings Object to match the value
// from the opts Object:
settings[ property ] = opts[ property ];
}
}
// finding the elements from the document that match the
// CSS selector:
let elements = document.querySelectorAll( settings.selector );
// converting that NodeList to an Array, using Array.from():
let elementsArray = Array.from( elements );
// iterating over the Array:
elementsArray.forEach(function(abbr) {
// 'abbr' the first-named argument of the anonymous function
// is automatically supplied by the function itself, and is
// a reference to the array-element of the array over which
// we're iterating.
// setting the title property of the array-element to
// offer an explanation of what the abbreviation means,
// removing any leading/trailing white-space using
// String.prototype.trim():
abbr.title = abbr.textContent.trim();
// updating the textContent of the element to match the
// abbreviation held in the settings.abbreviation Array,
// or, if that's a falsy value, simply returns the title
// of the element:
abbr.textContent = settings.abbreviations[abbr.title] || abbr.title;
});
}
// calling the function, and modifying the CSS selector
// to be used within the function:
abbreviations({
'selector': '.lang-item a'
});
'use strict'
const abbreviationsMap = {
'English': 'En',
'Português': 'Pt'
};
function abbreviations(opts) {
let settings = {
'selector': '.toAbbreviate',
'abbreviations': abbreviationsMap
};
for (let property in opts) {
if (opts.hasOwnProperty( property )) {
settings[property] = opts[ property ];
}
}
let elements = document.querySelectorAll( settings.selector );
let elementsArray = Array.from( elements );
elementsArray.forEach(function( abbr ) {
abbr.title = abbr.textContent.trim();
abbr.textContent = settings.abbreviations[ abbr.title ] || abbr.title;
});
}
abbreviations({
'selector': '.lang-item a'
});
<ul id="menu-navbar">
<li id="menu-item-270-en" class="lang-item">
<a href="site.com/en">
English
</a>
</li>
<li id="menu-item-270-pt" class="lang-item">
<a href="site.com/">
Português
</a>
</li>
<li id="menu-item-270-cy" class="lang-item">
<a href="site.com/cy">
Welsh
</a>
</li>
</ul>
JS Fiddle demo.
Alternatively, another approach is:
'use strict'
// again, a named function to convert the textContent
// of relevant items into an abbreviated form:
function abbreviations(opts) {
let settings = {
// the CSS selector with which the elements should
// be selected
'selector': '.toAbbreviate',
// the property of the element, or its ancestor element,
// from which the abbreviation should be derived:
'property': 'id',
// the regular expression by which the abbreviation
// should be derived,
// this is looking for a sequence of lower-case
// alphabetical characters in the range from 'a' to 'z'
// ('[a-z]') inclusive, that's two characters in length
// ('{2}') and occurs at the end of the property-string
// ('$'):
'regexp': /[a-z]{2}$/
};
for (let property in opts) {
if (opts.hasOwnProperty(property)) {
settings[property] = opts[property];
}
}
let elements = document.querySelectorAll(settings.selector);
let elementsArray = Array.from(elements),
text;
elementsArray.forEach(function(abbr) {
// finding the closest ancestor element to the current
// element that has the property identified in the
// settings.property value, using CSS attribute-selector
// syntax (identifying an 'id' property/attribute by '[id]'
// for example;
// accesing the property identified by the settings.property
// value of the found element, retrieving the 'id' by:
// HTMLElement[ id ],
// and retrieving the portion of the property-value
// that matches the settings.regexp property-value,
// (or null if there is no match):
text = abbr.closest('[' + settings.property + ']')[settings.property].match(settings.regexp);
// because null is a possible result, we check
// that the text variable exists:
if (text) {
// if it does, then we retrieve the match
// held at the first index of the returned
// Array:
text = text[0];
} else {
// otherwise we simply use the trimmed text-content:
text = abbr.textContent.trim();
}
// setting the title of the element:
abbr.title = abbr.textContent.trim();
// capitalising the first character of the String and
// concatenating that with the rest of the String:
abbr.textContent = text.charAt(0).toUpperCase() + text.slice(1);
});
}
abbreviations({
'selector': '.lang-item a'
});
'use strict'
function abbreviations(opts) {
let settings = {
'selector': '.toAbbreviate',
'property': 'id',
'regexp': /[a-z]{2}$/
};
for (let property in opts) {
if (opts.hasOwnProperty(property)) {
settings[property] = opts[property];
}
}
let elements = document.querySelectorAll(settings.selector);
let elementsArray = Array.from(elements),
text;
elementsArray.forEach(function(abbr) {
text = abbr.closest('[' + settings.property + ']')[settings.property].match(settings.regexp);
if (text) {
text = text[0];
} else {
text = abbr.textContent.trim();
}
abbr.title = abbr.textContent.trim();
abbr.textContent = text.charAt(0).toUpperCase() + text.slice(1);
});
}
abbreviations({
'selector': '.lang-item a'
});
<ul id="menu-navbar">
<li id="menu-item-270-en" class="lang-item">
<a href="site.com/en">
English
</a>
</li>
<li id="menu-item-270-pt" class="lang-item">
<a href="site.com/">
Português
</a>
</li>
<li id="menu-item-270-cy" class="lang-item">
<a href="site.com/cy">
Welsh
</a>
</li>
</ul>
JS Fiddle demo.

Related

Javascript to replace or strip inner html

I need to extract just V1 BAE from this html:
<ul class="checkout-bar">
<li> Info for V1 BAE </li>
</ul>
So far I have this:
function() {
var element = document.querySelector('ul.checkout-bar > li').textContent;
return element;
}
But it returns this:
\n\t\t\t\tInfo for V1 BAE\t\t\t
I need to just have the value V1 BAE so need to strip out all the '\n\t\t\t\tInfo for ' and '\t\t\t'.
You can use a combination of String#replace and String#trim :
.replace('Info for','').trim();
Demo:
var element=document.querySelector('ul.checkout-bar > li').textContent.replace('Info for','').trim();
console.log(element);
<ul class="checkout-bar">
<li> Info for V1 BAE </li>
</ul>
Use replace to replace Info for with null and then trim() the result to remove preceding and succeeding white spaces:
function test() {
var element=document.querySelector('ul.checkout-bar > li').textContent;
var res = element.replace('Info for', '').trim();
return res;
}
console.log(test());
<ul class="checkout-bar">
<li> Info for V1 BAE </li>
</ul>
You can use regex to capture things out of a string. The below example returns a collection containing all matches of this regex, and you can select which one you wish to return by using square brackets, eg: result[0]. This will also capture the required text even if the string is preceded or followed by anything.
function test() {
let element = document.querySelector('ul.checkout-bar > li').textContent;
let regex = /.+Info\s+for\s+(\w+\s\w+).+/;
let result = regex.exec(element);
return result;
}
console.log(test()[0]);
console.log(test()[1]);
<ul class="checkout-bar">
<li> Info for V1 BAE </li>
</ul>
Note that if you specifically want to capture the string V1 BAE, you can replace (\w+\s\w+) with (V1\sBAE), if there's a chance that the string will be different which you don't want to capture:
function test() {
let element = document.querySelector('ul.checkout-bar > li').textContent;
let regex = /.+Info\s+for\s+(V1\sBAE).+/;
let result = regex.exec(element);
return result;
}
console.log(test()[0]);
console.log(test()[1]);
<ul class="checkout-bar">
<li> Info for V1 BAE </li>
</ul>
Hope this helps.

Convert list of HTML element into Javascript list of object

I'm trying to convert something like this HTML snippet:
<ul>
<li><span>Frank</span><img src="pic.jpg"></li>
<li><span>Steve</span><img src="pic2.jpg"></li>
</ul>
into a JavaScript objects that contain the name and the image's url. How can I do that?
Use map() method
var res = $('ul li').map(function() { // select all li and iterate
// return as required format of array elemnt
return {
name: $('span', this).text(), // get text in span
src: $('img', this).attr('src') // get src attribute
}
}).get(); // get array from jquery object
console.log(res);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<ul>
<li><span>Frank</span>
<img src="pic.jpg">
</li>
<li><span>Steve</span>
<img src="pic2.jpg">
</li>
</ul>
UPDATE : If you want to generate an object which has key as span text and value as src attribute then use each() method and iterate over elements and generate object.
var res = {};
$('ul li').each(function() { // select all li and iterate
res[$('span', this).text().trim()] = $('img', this).attr('src');
})
console.log(res);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<ul>
<li><span>Frank</span>
<img src="pic.jpg">
</li>
<li><span>Steve</span>
<img src="pic2.jpg">
</li>
</ul>
var objs = document.querySelectorAll('.to-js-obj li');
var objs_arr = [];
if (objs) {
for (var i = 0; i < objs.length; i++) {
var name = objs[i].querySelector('span').innerText;
var url = objs[i].querySelector('img').src;
objs_arr.push({
name: name,
src: url
});
}
console.log(JSON.stringify(objs_arr));
}
<ul class="to-js-obj">
<li><span>Frank</span>
<img src="pic.jpg">
</li>
<li><span>Steve</span>
<img src="pic2.jpg">
</li>
</ul>
Using jQuery
var $list = $('ul'), // get the list (ideally, add an ID)
$listItems = $list.find('li'); // find list items
if( $listItems.length > 0 ) { // if list items exist
var images = []; // create empty array to store objects
$.each( $listItems, function( index ) { // loop through the list items
var $item = $( $listItems[index] ); // save item as jQuery element
var name = $item.find('span').text(); // Get name from span
var imageSrc = $item.find('img').attr('src'); // Get img src
images[index] = {}; // Create new object in array
images[index].name = name; // Add name
images[index].imageSrc = imageSrc; // Add source
});
}
Returns
[Object {
imageSrc: "pic.jpg",
name: "Frank"
}, Object {
imageSrc: "pic2.jpg",
name: "Steve"
}]
You can use this:
var image_pairs = [];
$("ul li").each(function() {
image_pairs.push({
name: $(this).find("span").text(),
url: $(this).find("img").attr("src")
});
});
console.log(image_pairs);
<ul>
<li><span>Frank</span><img src="pic.jpg"></li>
<li><span>Steve</span><img src="pic2.jpg"></li>
</ul>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

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

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

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]

Categories