jQuery selector based on deep children text content - javascript

This is an example of HTML I am working with.
<html>
<div class="parent-div">
<ul>
<li class="title">Color</li>
<li>Red</li>
<li class="title">Shape</li>
<li>Squared</li>
</ul>
</div>
</html>
I would like to convert the current HTML in the following list-item
Color: Red
Shape: Squared
And this is what I tried so far:
$("html > div.parent-div > ul > li:contains('Color')")
However it throws an exception:
Uncaught DOMException: Failed to execute 'querySelectorAll' on
'Document': html > div.parent-div > ul > li:contains('Color')'
is not a valid selector.

That's because html is the same as document. Leave the html out of the selector.
$("div.parent-div > ul > li:contains('Color')")
The inner working of the jQuery is that it uses document.querySelectorAll. Since document is the root, you don't need to specify it again inside the selector. If you do it will fail because HTML (document) doesn't contain an element called "HTML".
The following snippet will do what you desire.
$("document").ready(function(){
//select all the list elements with the class title
$("div.parent-div > ul > li.title").each(function(){
//concat the text of the title element and the next sibling into one string.
var text = $(this).text() + ": " + $(this).next().text();
$(this).html(text); //change the title element's html to set string
$(this).next().detach(); //delete the value element from the DOM.
})
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
<div class="parent-div">
<ul>
<li class="title">Color</li>
<li>Red</li>
<li class="title">Shape</li>
<li>Squared</li>
</ul>
</div>
</html>

no need to write html in jQuery selector
$("div.parent-div > ul > li:contains('Color')")

I dont know the reasoning of this, but you can get it with something like this:
var output = {};
var output_index = 0;
var last_title = "";
var allLi = $("#parent-div li");
var $this = null;
for(var i = 0; i < allLi.length; i++){
$this = $(this);
if($this).hasClass(title){
last_title = $this.html();
} else {
output[last_title] = $this.html();
}
})

Related

How to get the text content of html w/o Id or tagname?

I´m working on a chrome extension (Using Javascript) for personal use that scrapes some data and exports it to a csv file, right now I'm having problems extracting some text nodes because their selectors (chrome css selectors) change depending on how many labels with the same class but different content exist.
Here is an example of the Html:
<li class="sc-account-rows__row">
<div class="sc-account-rows__row__label">RTF</div> // Title Label
<div class="sc-account-rows__row__price--container">
<div class="sc-account-rows__row__price">-$ 1.485</div> // Price Label <- How to get This?
</div>
</li>
<li class="sc-account-rows__row">
<div class="sc-account-rows__row__label">some text</div> // Another Label
<div class="sc-account-rows__row__price--container">
<div class="sc-account-rows__row__price">-$ 2.418</div> // Another price which I don't need but has same class
</div>
</li>
In other words the selector for this particular label can be:
#root-app > div > div.sc-account-section > div.sc-account-section__container > div.sc-account-module > div:nth-child(3) > ul > li:nth-child(1) > div.sc-account-rows__row__price--container > div
or
#root-app > div > div.sc-account-section > div.sc-account-section__container > div.sc-account-module > div:nth-child(3) > ul > li:nth-child(9) > div.sc-account-rows__row__price--container > div
As you can see there is no Id or Name assigned to this particular label, I was using (successfully) this piece of code when the selector was always the same. (Notice this is inside an iframe)
var RTF_fee = "#root-app > div > div.sc-account-section > div.sc-account-section__container > div.sc-account-module > div:nth-child(3) > ul > li:nth-child(4) > div.sc-account-rows__row__price--container > div";
if (iframe_document.body.contains(iframe_document.querySelector(RTF_fee))) {
RTF_value = iframe_document.querySelector(RTF_fee).textContent;
console.log(RTF_value);
}
else {
RTF_value = "0";
console.log(RTF_value);
}
So, the question is: How to get the text content in the price label if I don't have a unique selector/Id for it?
I guess I could work with the fact the the price label class is always "sc-account-rows__row__price" and the label text before the price is always "RTF" but I don't know how to code this or if there is a better alternative.
I apologize for my poor "programming" language, I´m just a casual programmer)
Thanks in advance.
If I understand your question correctly, this is what you are looking for.
let labels = document.querySelectorAll(".sc-account-rows__row__label");
[...labels].forEach(label => {
if (label.innerText === "RTF") {
let price = label.parentElement.querySelector(".sc-account-rows__row__price").innerText;
console.log(price);
}
})
Console: "-$ 1.485"
If you need to use extra labels besides RTF:
let words = ["RTF", "something", "else"];
// change if condition to
if (words.includes[label.innerText]) {...}
Another way of doing this is using XPath, an old standard for navigating through XML that also works with HTML. In this case, you're looking for a div with the class "sc-account-rows__row__price" that has another div with the class "sc-account-rows__row__label" nearby that has the text "RTF".
The XPath for the div is
//div[#class="sc-account-rows__row__label"][text()="RTF"]/following-sibling::div[#class="sc-account-rows__row__price--container"]//div[#class="sc-account-rows__row__price"]
It's a bit complex because of the nested nature of the price--container/price divs.
To get the text, you'd use document.evaluate:
function getPriceByLabel(label) {
// change this to a parent element if you can; it will speed up the process.
const contextNode = document;
const priceResults = document.evaluate(`//div[#class="sc-account-rows__row__label"][text()="${label}"]/following-sibling::div[#class="sc-account-rows__row__price--container"]//div[#class="sc-account-rows__row__price"]`, contextNode, null, XPathResult.ANY_UNORDERED_NODE_TYPE, null);
const priceElement = priceResults.singleNodeValue;
const price = priceElement.textContent;
return price;
}
console.log(getPriceByLabel("RTF"));
console.log(getPriceByLabel("some text"));
<li class="sc-account-rows__row">
<div class="sc-account-rows__row__label">RTF</div>
<div class="sc-account-rows__row__price--container">
<div class="sc-account-rows__row__price">-$ 1.485</div>
</div>
</li>
<li class="sc-account-rows__row">
<div class="sc-account-rows__row__label">some text</div>
<div class="sc-account-rows__row__price--container">
<div class="sc-account-rows__row__price">-$ 2.418</div>
</div>
</li>
I put it in a function that takes a label and spits out the price.

Create UL with data from a set of divs

I have a selection of divs with the exact markup below and I want to create an unordered list above those divs that will provide the ground work for a 'tab' system - all without modifying the below markup.
<div id="category_1">
<h3 class="maintitle">
<a class="toggle">..</a>
Cat 1 Title
</h3>
<div>
...
</div>
</div>
<div id="category_2">
<h3 class="maintitle">
<a class="toggle">..</a>
Cat 2 Title
</h3>
<div>
...
</div>
</div>
<div id="category_3">
<h3 class="maintitle">
<a class="toggle">..</a>
Cat 3 Title
</h3>
<div>
...
</div>
</div>
And I want to create with jQuery or just pure JS if easy enough:
<ul>
<li>Cat 1 Title</li>
<li>Cat 2 Title</li>
<li>Cat 3 Title</li>
</ul>
The rel would be the ID of the div so I know which tabs to show / hide later on.
The value of the LI item would be the text of the second anchor within the H3 of the original code.
Cheers.
Try this:
$(function () {
var $uls = $('<ul/>');
$('[id^=category]').each(function () { // Provide a container that hold the div as context.
var anch = $(this).find('h3 a').eq(1).clone(); //clone your anchor the second one in h3
anch[0].rel = this.id; // set the rel with the id
$('<li/>').append(anch).appendTo($uls); // append to temp ul
});
$('body').append($uls); // Append anywhere you want
});
http://jsfiddle.net/XmarF/
If you don't want to clone your anchor then you can try this too..
$(function () {
var $uls = $('<ul/>');
$('[id^=category]').each(function () {
$('<li/>').append($('<a/>', {
href: '#',
rel: this.id,
text: $(this).find('h3 a').eq(1).text()
})).appendTo($uls);
});
$('body').append($uls);
});
$('body').prepend($("<ul class='menu'>"));
$('div a[href]').each(function () {
var el = $(this).clone().attr('rel', $(this).closest('div').attr('id'))
el = $('<li></li>').append(el);
$('ul.menu').append(el);
});
Demo ---> http://jsfiddle.net/ht3Y7/
You could do something like this:
$('div[id^="category_"]').each(function(){
var id = $(this).attr('id');
var title = $(this).children('h3').find('a').eq(1).text();
var listItem = $("<li><a href='#'></a></li>");
listItem.find('a').attr('rel',id).text(title);
$('ul').append(listItem);
});
Fiddle
You could try using jQuery's .replaceWith() method. It will replace a current HTML with one you desire. So if your divs were in a wrapper with a name you could do something like:
$("#wrapperID > div").each(function(i) {
var li = $("<li />").html($(this).html());
$(this).replaceWith(li);
});
var ul = $("<ul />", { id: 'wrapperID ' }).html($("#wrapperID ").html());
$("#wrapperID ").replaceWith(ul);
|OR| if you're looking to convert to that exact markup, again I assume all divs in some type of wrapper with ID (for simplicity):
$("#wrapperID > div").each(function(i) {
var li = $("<li />"),
a = $("<a />", { href: "#", rel: $(this).prop("id"), text: $(this).find("a").last().text() }).appendTo(li);
$(this).replaceWith(li);
});
var ul = $("<ul />", { id: 'wrapperID ' }).html($("#wrapperID ").html());
$("#wrapperID ").replaceWith(ul);
See Working Example Here
With no jQuery, this will work in IE8 and up.
var divs = document.querySelectorAll("div[id^=category_]");
var ul = divs[0].parentNode.insertBefore(document.createElement("ul"), divs[0]);
for (var i = 0; i < divs.length; i++) {
ul.appendChild(document.createElement("li"))
.appendChild(divs[i].querySelector("a[href='#']").cloneNode(true))
.rel = divs[i].id
}
DEMO: http://jsfiddle.net/ENvNq/1/
You can do it in three lines (and maybe less):
var html='';
$('div[id^="category"]').each(function () {html += '<li>' + $(this).find('a.toggle').next().text() + '</li>';});
$('#category_1').before('<ul>'+html);
jsFiddle example

set array content to title of span tag with JQuery

i have an array like this:
var name = ["cat","shark","tiger","snake"];
i want set array content to <span> title.
HTML CODE:
<ul id="select">
<li class="ui-state-default"><span>CAT</span></li>
<li class="ui-state-default"><span>SHARK</span></li>
<li class="ui-state-default"><span>TIGER</span></li>
<li class="ui-state-default"><span>SNAKE</span></li>
</ul>
jquery code:
for(var i=0; i<=name.length; i++){
$('#select li > span').each(function() {
this.title = name[i];
});
};
but this code don't work.
title of span is be undifined.
If by title you mean the text of the span, I'd suggest:
$('#select li span').text(function(i){
return name[i];
});
JS Fiddle demo.
Or if you're trying to set the title attribute, I'd suggest:
$('#select li span').attr('title',function(i){
return name[i];
});
JS Fiddle demo.
It doesn't make sense to loop over the spans and the array. Also the i <= name.length means that you will always loop past the array (should be <). This means that you were actually setting each span's title to an array element that did not exist.
$('#select li > span').each(function(i) {
$(this).attr('title', name[i]);
});
You may also want to check that name[i] exists.
http://jsfiddle.net/wtbE5/
Try:
$('#select li span').each(function(i){
$(this).html(name[i]);
});
Sample

Select only one of two selectors

Given a containing jQuery object $c that has multiple elements in it that contain elements with classnames tmpSelected and selected, I would like to select only one element from $c for each classname, preferably tmpSelected. The markup is a complex version of this:
<ul id="a">
<li class="selected">Foo</li>
<li>Bar</li>
<li>Baz</li>
<li>Biz</li>
</ul>
<ul id="b">
<li>Woo</li>
<li>War</li>
<li class="tmpSelected">Waz</li>
<li>Wiz</li>
</ul>
<ul id="c">
<li class="selected">Xuu</li>
<li class="tmpSelected">Xur</li>
<li>Xuz</li>
<li>Xyz</li>
</ul>
In this case what I want to end up with is $("#a > .selected, #b > .tmpSelected, #c > .tmpSelected") – I want to avoid the .selected element if it has a sibling of .tmpSelected, and I don't want to select more than one child element for each member of $c where $c = $("#a, #b, #c").
So this is what I came up with:
var $c = $("#a, #b, #c");
var $selected = $c.map(function (idx, el) {
var $el = $(el);
var $tmpSel = $el.children(".tmpSelected");
return $tmpSel.length ? $tmpSel : $el.children(".selected");
});
Is there a reasonable way to do this without explicit looping? (P.S. - It's fine to return an empty selector when no .tmpSelected or .selected child exists.)
Here is a selector but it is pretty messy. I believe it gives the correct solution:
$("ul > li.tmpSelected, ul:not(:has(li.tmpSelected)) > li.selected");
First off you look for and .tmpSelected elements. Then you look for any ul that only have .selected elements. The :has selector looks for the child and I use the :not selector to find ul elements. Then I simply grab the children selected elements.
jsFiddle
I would suggest this which is pretty similar in concept to what you already had except this guarantees that it only ever returns a single item for each parent.
var selected = $("#a, #b, #c").map(function() {
var item = $(this).find(".tmpSelected");
if (!item.length) {
item = $(this).find(".selected");
}
return(item.get(0));
});

jQuery cant access element with its attr href // simple tabs structure

I can't access "a" element with its href. Like this example, i need add class to element which got href="#one"
Here is jsFiddle.
jQuery:
//tabs part is working fine
$('.tabs').hide();
$('.tabs:first').show();
$('ul li a').click(function(){
var target = $(this).attr('href');
$('.tabs').hide();
$(target).fadeIn(500);
$('ul li a').removeClass('selected');
$(this).addClass('selected');
});
//problem starts when i try to reach href
var link1 = '#one';
var link2 = '#two';
var link3 = '#three';
var takE = $('ul li a').each();
var finD = take.attr('href');
finD.addClass('thisisLink1');​
html:
<ul>
<li>Home</li>
<li>Posts</li>
<li>About</li>
</ul>
<div class="tabs" id="one">Home Container</div>
<div class="tabs" id="two">Posts Container</div>
<div class="tabs" id="three">About Container</div>​
I have tried each method but it didn't work. I need to access elements with their attr href and set the object to addClass or animate. This way returns me as a string.
Use attribute selector like so:
$('a[href="#one"]').addClass(...)
Also note that you defined a takE variable and the you wrote take: probably you may want to write
var takE = $('ul li'),
finD = takE.find('a[href="#one"]');
finD.addClass('thisisLink1');
$('ul li a').each(function() {
if(this.href == link1) $(this).addClass('thisisLink1');
if(this.href == link2) $(this).addClass('thisisLink2');
if(this.href == link3) $(this).addClass('thisisLink3');
});
You can also use an object mapping:
var links = {
'#one': 'thisislink1',
'#two': 'thisislink2',
'#three': 'thisislink3',
};
$.each(links, function(key, val) {
$('ul li a[href="' + key + '"]').addClass(val);
});
DEMO

Categories