Lets say I had the following code:
<div class="post">
<h2 itemprop="name">
The Post Title
</h2>
<div class="details">
<span>
<em class="date">Jul 17, 2014 </em>
</span>
<span>
Category:
Staff Profile
</span>
</div>
How would I possibly get the values of "The Post Title" and "Staff Profile" using JavaScript without changing the HTML on the page at all? i.e. I couldn't use getElementbyID for example. I could use jQuery if I had to but would rather not if possible.
You can get these values using getElementsByTagName which returns an array
document.getElementsByTagName("a")[0].innerHTML // returns The Post Title
document.getElementsByTagName("a")[1].innerHTML // returns Staff Profile
If these links are the first ones you can use indexes 0 and 1, otherwise you should look for the right index
Update
Another way that may be simple is to select these links inside the div with the class post
var links = document.getElementsByClassName("post")[index].getElementsByTagName("a");
links[0].innerHTML; // returns The Post Title
links[1].innerHTML; // returns Staff Profile
This solution would be the best one if the index of the div with the class post doesn't change
For a jQuery based expression you can use this:
$('a').map(function() {
return [this.href, this.textContent];
}).get();
which should return:
[ [ 'http://www.example.com', 'The Post Title' ],
[ 'http://sitename/category/staff-profile/', 'Staff Profile' ] ]
Should you specifically want the original relative URLs instead of the normalised full URLs, use this.getAttribute(href) in place of this.href
For a pure (ES5) equivalent:
[].map.call(document.getElementsByTagName('a'), function (el) {
return [el.href, el.textContent];
});
Older browsers that don't support the W3C standard .textContent property may require the .innerText property instead, e.g.:
return [el.href, el.textContent || el.innerText];
You can do:
var posts = document.querySelector('.post');
for (var i = 0; i < posts.length; i++) {
var links = document.querySelectorAll('a');
var title = links[0].innerText || links[0].textContent;
var profile = links[1].innerText || links[1].textContent;
}
If you are using a more modern browser, you can use document.querySelectorAll() which takes in CSS style selector syntax.
var aList = document.querySelectorAll('.post a');
for (var i = 0; i < aList.length; ++i) {
alert(aList[i].innerHTML);
}
JSFiddle
I used '.post a' rather than just 'a' because I assume your page may have other 'a' tags in it that you don't necessarily want.
Related
I have some problems with my code. I want to create an XML Document with JQuery / JavaScript. I am now at the point, where I want to create a few Tags and populate them each with the same tags but different content inside the tags.
Here is the code for better understand
function setItems(xmlDoc, channelTag){
const itemList = [];
const itemTitle = xmlDoc.createElement("title");
const itemLink = xmlDoc.createElement("link");
const itemGuid = xmlDoc.createElement("guid");
const itemMediaContent = xmlDoc.createElement("media:content");
const itemMediaDescription = xmlDoc.createElement("media:description");
itemList.push(itemTitle, itemLink, itemGuid, itemMediaContent, itemMediaDescription);
for (var i = 0; i < jsonObj.length; i++){
var item = xmlDoc.createElement("item");
channelTag.appendChild(item);
//Populate the <item> with the tags from "itemList" and content from "jsonObj"
$.each(itemList, function(index) {
$(channelTag).children('item')[i].appendChild(itemList[index]).textContent = jsonObj[0].title;
})
}
}
The Output of the code looks like this:
<item></item>
<item></item>
<item>
<title>Something</title>
<guid>Something</guid>
<link>Something</link>
<media:content>Something</media:description>
<media:description>Something</media:description>
</item>
It always populates the last item-Tag but not the ones above. What I want is that every item-Tag has the same child-Tags (e.g. title, link, guid and so on). Is there something i am missing some unique tags or something like that?
Edited:
Here is some minimal HTML and XML. The values for the function "xmlDoc" and "channelTag" just contains some Document Elements, where my items should be appended, like so:
<rss>
<channel>
<title>SomeTitle</title>
<atom:link href="Link" rel="self" type="application/rss+xml"/>
<link>SomeLink</link>
<description>SomeDesc</description>
<item></item>
<item></item>
<item></item>
</channel>
</rss>
<div class="col-5 col-sm-5 col-lg-3 order-2 count">
<a class="guid1"><img class="card-img image1"></a>
</div>
<div class="col-7 col-sm-7 col-lg-5 order-2">
<div class="card-body">
<a class="guid1">
<h5 class="card-title title1 overflow-title"></h5>
</a>
<p class="card-text body1 text-body overflow-body"></p>
<div class="card-body subtitle">
</div>
</div>
</div>
There are several issues with your code but the area we mostly want to focus on is this:
for (var i = 0; i < jsonObj.length; i++){
var item = xmlDoc.createElement("item");
channelTag.appendChild(item); // you're adding a node here
$.each(itemList, function(index) {
$(channelTag).children('item')[i].appendChild(... // and here
})
}
Instead of appending nodes multiple times per iteration, you should create and populate your node before add it it to channelTag.
Here's a way your could do it:
// use a "$" sign as a variable name prefix, so you know it's a Document Element and not a regular javascript variable
var $item = xmlDoc.createElement("item");
// you don't need jQuery for this iteration
itemList.forEach(function (item, index) {
$item.appendChild(itemList[index]).textContent = jsonObj[0].title;
});
// if "channelTag" is a Document Element, rename it "$channelTag"
$channelTag.appendChild(item);
Couple things about the code above:
you don't need jQuery, use forEach instead
there is no way telling what type is channelTag. If it is a selector (of type string), use $(selector), but you are using the appendChild() method before, suggesting it's actually a Document Element. In that case you don't need to wrap it with $()
I don't have the context needed to test this code, so no guarantee it'll work out of the box. But try and re-read your code and go through it top-to-bottom. For each variable, describe its type and value. I found that to be helpful when I'm lost in code.
i'm trying to get a list of values of 'data-ctorig' property on all links marked with 'gs-title' class on this page.
i can get one the value of first link
document.querySelector('a.gs-title').getAttribute('data-ctorig')
but can have others.
also, i can get a list of nodelist (idk what is this) using
document.querySelectorAll('a.gs-title')
but i dont know how can i get a list of attribute values based in this node list.
also i've tried use :nth-of-type() but i got only null after first value
var x = document.querySelector("a.gs-title:nth-of-type(1)").getAttribute("data-ctorig")
i'm also using python with selenium webdriver to do this so if someone know how to do it on python 'll help me on the same way.
First use querySelectorAll to get all the .gs-title nodes.
var gstitles = document.querySelectorAll('a.gs-title')
You can use gstitles.length now to determine how many there are.
gstitles.length;
Next we need to setup a for loop.
Check out the code snippet below.
for (i=0;i<gstitles.length;i++)
{
console.log(gstitles[i].getAttribute('data-ctorig'));
}
var gstitles = document.querySelectorAll('a.gs-title')
for (i=0;i<gstitles.length;i++)
{
console.log(gstitles[i].getAttribute('data-ctorig'));
}
<div class="gs-title"><a class="gs-title" href="https://www.teses.usp.br/teses/disponiveis/27/27151/tde-19032008-183924/publico/AmandaTojal.pdf" target="_blank" dir="ltr" data-cturl="https://www.google.com/url?q=https://www.teses.usp.br/teses/disponiveis/27/27151/tde-19032008-183924/publico/AmandaTojal.pdf&sa=U&ved=0ahUKEwiWo7TB3-bhAhUF16wKHaWeCXoQFggEMAA&client=internal-uds-cse&cx=011662445380875560067:cack5lsxley&usg=AOvVaw2g3t_0fFH8wjhfjcku0DL3" data-ctorig="https://www.teses.usp.br/teses/disponiveis/27/27151/tde-19032008-183924/publico/AmandaTojal.pdf"><b>AMANDA</b> PINTO DA FONSECA TOJAL</a></div><div class="gs-title gsc-table-cell-thumbnail gsc-thumbnail-left"><a class="gs-title" href="https://www.teses.usp.br/teses/disponiveis/27/27151/tde-19032008-183924/publico/AmandaTojal.pdf" target="_blank" dir="ltr" data-cturl="https://www.google.com/url?q=https://www.teses.usp.br/teses/disponiveis/27/27151/tde-19032008-183924/publico/AmandaTojal.pdf&sa=U&ved=0ahUKEwiWo7TB3-bhAhUF16wKHaWeCXoQFggEMAA&client=internal-uds-cse&cx=011662445380875560067:cack5lsxley&usg=AOvVaw2g3t_0fFH8wjhfjcku0DL3" data-ctorig="https://www.teses.usp.br/teses/disponiveis/27/27151/tde-19032008-183924/publico/AmandaTojal.pdf"><b>AMANDA</b> PINTO DA FONSECA TOJAL</a></div><div class="gs-title"><a class="gs-title" href="https://www.google.com/url?q=http://www.teses.usp.br/teses/disponiveis/17/17153/tde-06012017-103806/publico/AmandaMizukamiDOCorrig.pdf&sa=U&ved=0ahUKEwiWo7TB3-bhAhUF16wKHaWeCXoQFggGMAE&client=internal-uds-cse&cx=011662445380875560067:cack5lsxley&usg=AOvVaw0Jdjapa8W60DfKRyUIAdoH" target="_blank" dir="ltr" data-cturl="https://www.google.com/url?q=http://www.teses.usp.br/teses/disponiveis/17/17153/tde-06012017-103806/publico/AmandaMizukamiDOCorrig.pdf&sa=U&ved=0ahUKEwiWo7TB3-bhAhUF16wKHaWeCXoQFggGMAE&client=internal-uds-cse&cx=011662445380875560067:cack5lsxley&usg=AOvVaw0Jdjapa8W60DfKRyUIAdoH" data-ctorig="http://www.teses.usp.br/teses/disponiveis/17/17153/tde-06012017-103806/publico/AmandaMizukamiDOCorrig.pdf"><b>AMANDA</b> MIZUKAMI</a></div>
You meant this? :)
var links = document.querySelectorAll('a.gs-title');
var list = [];
links.forEach((link) => {
list.push(link.getAttribute('data-ctorig'));
});
console.log(list);
<a class="gs-title" data-ctorig="Test1"><b>AMANDA</b> MIZUKAMI</a>
<a class="gs-title" data-ctorig="Test2"><b>AMANDA</b> MIZUKAMI</a>
<a class="gs-title" data-ctorig="Test3"><b>AMANDA</b> MIZUKAMI</a>
I am new to automated testing, Protractor, and angularJS. I have a list that I would like to count, copy to an array maybe, and verify the list text is present. For example The list shows Attractions, Capacity, and Content to the user so they know what privileges they have.
Below is the .html
<div class="home-info">
<div class="home-top home-section">
<h3>User Information</h3>
<div class="home-box">
<div class="property-group wide">
<span>
Change Phillips<br />
</span>
</div>
</div>
<div class="home-box">
<div class="property-group wide">
<div>Editors:</div>
<span>
<ul class="property-stack">
<li><span>Attractions</span>
</li>
<li><span>Capacity</span>
</li>
<li><span>Content</span>
</li>
<li><span>Media</span>
</li>
<li><span>Options</span>
</li>
<li></li>
<li></li>
<li><span>Upload CADs</span>
</li>
</ul>
</span>
</div>
</div>
</div>
Below is the code I have written. I can get the first item on the list however using .all isn't working for me.
var text = "";
browser.driver.findElement.all(By.xpath("//li/span")).count().then(function(count) {
initialCount = count;
console.log(initialCount);
});
browser.driver.findElement(By.xpath("//li/span")).getText().then(function(text) {
console.log(text);
});
I'm trying to avoid using xpath as I was told to try and avoid. To be honest Im lost. Thanks for the help in advance.
Code used for matching:
expect(myLists).toEqual(['Attractions', 'Capacity', 'Conent',
'Media', 'Options', 'Upload CADs'
]);
I am not sure what version of protractor you're using but you should be able to just call element without the browser or driver prefix. Using element.all should get you the array of of elements you're looking for.
If you want to access specific indexes within that array you can use the .get(index) suffix to the element.all
So below:
1. you get the array of the elements
2. you get the count of the array
3. we call a for loop to iterate through all the indexes of the array
4. each index of the array we call the getText() and print it to the console
var j = 0; // using this since the i iterator in the for loop doesn't work within a then function
var textList = [];
var text = "";
var myLists = element.all(by.css("li span"));
myLists.count().then(function(count) {
console.log(count);
for(int i = 0; i < count; i++){
myLists.get(i).getText().then(function(text) {
textList[j++] = text;
console.log(text);
});
}
});
EDIT:
In researching I actually found another way to iterate through the array of elements by using the .each() suffix to the element.all.
var j = 0; // using this since the i iterator in the for loop doesn't work within a then function
var textList = [];
var text = "";
var myLists = element.all(by.css("li span"));
myLists.count().then(function(count) {
console.log(count);
myLists.each(function(element, index) {
element.getText().then(function (text) {
textList[j++] = text;
console.log(index, text);
});
});
});
you should be able to use the textList array to match things.
expect(textList).toEqual(['Attractions', 'Capacity', 'Conent',
'Media', 'Options', 'Upload CADs'
]);
I have a div, #containerDiv, which contains elements related to users like first name, last name etc. in separate divs. I need to sort the contents of the container div based on the last name, first name etc. values.
On searching google the examples I got all are appending the sorted results and not changing the entire HTML being displayed. They are also not sorting by specific fields (first name, last name).
So please help me in sorting the entire content of #containerDiv based on specific fields and also displaying it.
The Page looks Like something as mentioned Below:
<div id="containerDiv">
<div id="lName_1">dsaf</div><div id="fName_1">grad</div>
<div id="lName_2">sdaf</div><div id="fName_2">radg</div>
<div id="lName_3">asdf</div><div id="fName_3">drag</div>
<div id="lName_4">fasd</div><div id="fName_4">gard</div>
<div id="lName_5">dasf</div><div id="fName_5">grda</div>
<div id="lName_6">asfd</div><div id="fName_6">drga</div>
</div>
On getting sorted by last name div values, the resulted structure of the container div should look like:
<div id="containerDiv">
<div id="lName_3">asdf</div><div id="fName_3">drag</div>
<div id="lName_6">asfd</div><div id="fName_6">drga</div>
<div id="lName_5">dasf</div><div id="fName_5">grda</div>
<div id="lName_1">dsaf</div><div id="fName_1">grad</div>
<div id="lName_4">fasd</div><div id="fName_4">gard</div>
<div id="lName_2">sdaf</div><div id="fName_2">radg</div>
</div>
Now I think you all can help me in a better way.
this is a sample example:
html:
<div id="containerDiv">
<div>2</div>
<div>3</div>
<div>1</div>
</div>
js
$(function() {
var container, divs;
divs = $("#containerDiv>div").clone();
container = $("#containerDiv");
divs.sort(function(divX, divY) {
return divX.innerHTML > divY.innerHTML;
});
container.empty();
divs.appendTo(container);
});
you may set your divs.sort function param depend on your goal.
jsFiddle.
and a jQuery Plugin is suitable
I suggest you read the div values so you get an array of objects (persons for example) or just names and perform a sort operation on that. Than...output the result to the initial div (overwriting the default values).
I have built a jQuery sort function in which you can affect the sort field.
(it rebuilds the html by moving the row to another location).
function sortTableJquery()
{
var tbl =$("#tbl tr");
var store = [];
var sortElementIndex = parseFloat($.data(document.body, "sortElement"));
for (var i = 0, len = $(tbl).length; i < len; i++)
{
var rowDom = $(tbl).eq(i);
var rowData = $.trim($("td",$(rowDom)).eq(sortElementIndex).text());
store.push([rowData, rowDom]);
}
store.sort(function (x, y)
{
if (x[0].toLowerCase() == y[0].toLowerCase()) return 0;
if (x[0].toLowerCase() < y[0].toLowerCase()) return -1 * parseFloat($.data(document.body, "sortDir"));
else return 1 * parseFloat($.data(document.body, "sortDir"));
});
for (var i = 0, len = store.length; i < len; i++)
{
$("#tbl").append(store[i][1]);
}
store = null;
}
Every time I need to sort lists I use ListJs.
It's well documented, has good performance even for large lists and it's very lightweight (7KB, despite being library agnostic).
I was wondering how I would go about finding and replacing some text in a div, but i want to find and replace the second occurrence of that text. For example:"You just added a item, please remove this item" so I would like to find the second "item" and replace it with whatever text I choose.
JS:
var compareCount = $('.compareWidget').find('.compareItem').length;
if (compareCount >= 2) {
$('.message').find('.count').text(compareCount);
$('message').html().replace('item', 'items');
}
$('.message').slideDown("Fast");
setTimeout(function () {
$('.message').slideUp("Fast");
}, 5000);
HTML:
<div id="alertMessage">
<div class="message">
<span>You just added a item to compare, you currently have <span class="count">1</span> item to compare</span>
</div>
</div>
"you currently have 1 item to compare"
You want to turn item to items?
You can do it with regular expressions, or you can wrap it into an element and grab that.
<span class="count">1</span> <span class="type">item</span> to compare</span>
and
$('.message').find('.type').text("items");
Using regular expressions you can
function replaceMatch(originalString, searchFor , replaceWith, matchNumber)
{
var match = 0;
return originalString.replace(searchFor, function(found){
match++;
return (match===matchNumber)?replaceWith:found;
},'g');
}
and call it like
var msg = $('.message');
msg.html( replaceMatch( msg.html(), 'item', 'items', 2) );
demo http://jsfiddle.net/gaby/crhvA/