How to give scope to a html element - javascript

<article class=​"product_summary" data-product-id=​"1" data-product-price=​"45.00">​
<img class=​"product_image" src=​"images/​cufflinks.jpg" alt=​"cufflinks">​
<h1 class=​"product_title">​Personalised cufflinks​</h1>​
<span class=​"product_price">​£45.00​</span>​
<a class=​"add_to_basket" href=​"/​add-to-basket?product_id=1">​Add to Basket​</a>​
</article>​
Say I have an element like the one above. how can i give it scope so that i can access the different elements inside it?
I essentially want to be able to get values from within it. i.e. price, title, etc

You have some zero-width spaces in that HTML snippet which will complicate things. That character occurs after every = and > and several other places. As it takes no horizontal space it is not easy to spot it.
This character badly affects the attribute values, so better remove it from the source.
Here is code that will list some of the content. I have removed those zero space characters from the HTML:
for (let article of document.querySelectorAll("article")) {
console.log('Article ID:', article.dataset.productId);
console.log('Price:', article.dataset.productPrice);
console.log('Title:', article.querySelector("h1").textContent);
}
<article class="productsummary" data-product-id="1" data-product-price="45.00">​
<img class="product_image" src="images/​cufflinks.jpg" alt="cufflinks">​
<h1 class="product_title">Personalised cufflinks</h1>
<span class="product_price">£45.00</span>
<a class="add_to_basket" href="/add-to-basket?product_id=1">Add to Basket</a>
</article>

Related

Why using .filter() together with .match() is only returning the first element matching the condition?

I have some HTML code where at the most nested level there is some text I'm interested in:
<div class="main">
<div class="container">
<div class="output_area">
<pre>WHITE 34</pre>
</div>
<div class="output_area">
<pre>RED 05</pre>
</div>
<div class="output_area">
<pre>WHITE 16</pre>
</div>
<div class="output_area">
<pre>BLACK</pre>
</div>
</div>
</div>
What I need to do is I need to return the output_area elements only when their nested <PRE> element contains a word + a number (for example WHITE 05, and not just BLACK).
So this is what I did:
I made an array from all output_area elements:
output_areas = Array.from(document.getElementsByClassName('output_area'));
I filtered the output_areas array to only return those output_area elements whose nested <PRE> satisfies my condition of a word + a number, using a regexp, like so:
output_areas.filter(el => el.textContent.match(/^WHITE \d+$/g));
Now, what happens is this function will only return the first matching result, so I will get an object of length 1 containing just :
<div class="output_area">
<pre>WHITE 34</pre>
</div>
and the output_area element containing <PRE> with "WHITE 16" is not returned.
As you can see at the end of the regular expression I put a "g" to request a global search and not just stop at the first result.
Not understanding why this did not work, I tried to verify what would happen if I would use includes() to perform a search:
output_areas.filter(el => el.textContent.includes('WHITE')
(let's just forget about the numbers now, it's not important)
And what happens? This will also return only the first output_area...
But why??? What am I doing wrong?
I am not ashamed to say I've been banging my head on this for the last couple of hours... and at this point I just want to understand what is not working.
The only clue I think I got is that if I simplify my search using just a == or !=, for example:
output_areas.filter(el => el.textContent != "")) // return all not empty elements
I get back all output_area elements and not just the first one!
So I suspect there must be some kind of problem when using together filter() & match(), or filter() & includes(), but with relation to that my google searches did not take me anywhere useful...
So I hope you can help!
You should use trim here to remove space before and after the text
output_areas.filter( el => el.textContent.trim().match( /^WHITE \d+$/g ))
const output_areas = Array.from(document.getElementsByClassName('output_area'));
const result = output_areas.filter(el => el.textContent.trim().match(/^WHITE \d+$/g));
console.log(result);
<div class="main">
<div class="container">
<div class="output_area">
<pre> WHITE 34 </pre>
</div>
<div class="output_area">
<pre> RED 05 </pre>
</div>
<div class="output_area">
<pre> WHITE 16 </pre>
</div>
<div class="output_area">
<pre> BLACK </pre>
</div>
</div>
</div>
Answering myself as for some reason it then begin to work without any changes from my side... Yes, just one of those typical IT cases we all know... :)
Jokes aside, I think for some reason the webpage (the DOM) got stuck...
Probably the Jupyter Runtime (which was serving the page) had crashed without me noticing, and this caused somehow the kind of inconsistency I was looking at.
Moral of the story: if you see weird behaviour in the interaction with a Python Notebook, always go check the Jupyter Runtime status before getting stupid at trying to fix impossible errors.
I'm not sure what the issue with the Jupyter notebooks is, but generally speaking - based only on the HTML in the question - what I believe you are trying to do can be achieved using xpath instead of css selectors:
html = `[your html above]
`
domdoc = new DOMParser().parseFromString(html, "text/html")
const areas = domdoc.evaluate('//div[contains(./pre," ")]', domdoc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for (let i = 0; i < areas.snapshotLength; i++) {
console.log(areas.snapshotItem(i).outerHTML)
}
The output should be the 3 divs meeting the condition.

How to increase character lengh in blogger snippet instead of using limited length, 'data:post.snippet'?

I'm currently using 'data:post.snippet' for blogger mobile snippet.
<div class='post-body' style='color:black;'>
<b:if cond='data:post.snippet'><data:post.snippet/></b:if>
</div>
But its character length(140) is too low, and it doesn't give a line break between the headings and paragraphs. When there's a heading at the very start line break is necessary. Can someone please suggest me a javascript code to replace above code to overcome those two issues.
You can utilize the data:post.longSnippet data tag which has the limit of upto 300-400 character
<div class='post-body' style='color:black;'>
<b:if cond='data:post.snippet'><data:post.longSnippet/></b:if>
</div>
Otherwise, if you want to control the exact amount of text that you want to be visible in the snippet, then you can utilize the newly launched operator in the new themes
<div class='post-body' style='color:black;'>
<b:eval expr='snippet(data:post.body, {length: 450, links: false})' />
</div>
snippet(string, options)
Produces a short snippet from an HTML string.
options: Object specifying the snippeting options, which are:
links: boolean for whether to preserve anchors/links in the snippet.
Defaults to true.
linebreaks: boolean for whether to preserve linebreaks (tags) in the
snippet. Defaults to true.
ellipsis: boolean for whether to append an ellipsis (…) to the end of
the snippet. Defaults to true.
length: Number specifying the maximum length of the snippet.
Change
<div class='post-body' style='color:black;'>
<b:if cond='data:post.snippet'><data:post.snippet/></b:if>
</div>
To
<div class='post-body' style='color:black;'>
<data:post.body/>
</div>
It will render your post body with proper HTML markup till any jump break (if exists) or otherwise the full post body.
Consider including jump breaks for all your posts, you can do it through the rich post editor or directly in the post HTML using the code <!--more--> in the exact place where you want the break.
Javascript approaches are not mandatory to retreive post contents, Blogger natively do it.
I hope this is what you are looking for,
<div class='post-body' style='color:black;'>
<b:eval expr='snippet(data:post.body, {length: 200, links: false, linebreaks: false})' />
</div>

Javascript code for alphabetizing divs

I'm editing a web page that has a list of doctors names and images wrapped in a div. I'm adding more to that list and my client wants all of the names in alphabetical order now. As apposed to manually doing that (I know my client will also be adding more doctors in the future)I tried writing a script to do the work for me. I wrapped each doctor in a div called "alphabetize," and set a span id of "lastName" around each doctor's last name.
<div class="alphabetize large-6 columns sameheight-wrap">
<div class="large-4 medium-4 columns sameheight PadT20"> <img src="../imgs/dev/crna-staff/John-Doe.jpg" alt=" John Doe, CRNA" class="pictureBottomBorder"> </div>
<div class="large-8 medium-8 columns contenttable sameheight PadT20">
<div class="border vmid text-center bgdblue PadB"> <span class="white medium"><strong>John<span id="lastName">Doe</span></strong><br>
</span> <span class="white PadT5"> MSN, Thomas Jefferson University School of Nurse Anesthesia</span> </div>
</div>
</div>
I placed the following script on that page;
<script>
var $divs = $("div.alphabetize");
$(function() {
var alphabeticallyOrderedDivs = $divs.sort(function (a, b) {
return $(a).find("#lastName").text() > $(b).find("#lastName").text();
});
$("#alpha").html(alphabeticallyOrderedDivs);
});
</script>
For some reason, the script is not working correctly. Doctors are out of order and i also need to add a variable to the code that sorts the last names with the first 3 letters. Can anyone help? Javascript is not my strong suit. Not sure if I missed something.
Below is a snippet that will show you how you can easily sort this. The major issue, however, is the following:
return $(a).find("#lastName").text() > $(b).find("#lastName").text();
The sort() function asks to return one of three values, 0 to maintain position, -1 to move it before the current element and 1 to move it after. That means that all you could ever return is after and not before, so your sort fails.
For the solution I would like to suggest using a data-attribute and no more HTML spans and styles that need to be rendered (and probably hidden afterwards), so here is my suggestion:
<div data-alphabetize="John Doe">John Does Content</div>
We can string together a couple of functions to get the correct output. We will need prototype.slice.call to convert the returned-by-querySelector NodeList to an Array, then we need to use sort to sort it alphabetically and finally we can use forEach to go through the array and insert the nodes in the correct position.
I am using vanilla JS - mostly to show how simple things can be done without loading up jQuery. You can, of course, do this with jQuery as well.
// Turn querySelectorAll NodeList into an Array
Array.prototype.slice.call(document.querySelectorAll('[data-alphabetize]'))
// Sort the array by data-alphabetize attribute (reverse order)
.sort(function(a, b){
return a.getAttribute('data-alphabetize') < b.getAttribute('data-alphabetize')
? 1 : -1;
})
// Insert every node in order
.forEach(function(v, i, a){
var parent = v.parentNode;
parent.removeChild(v);
parent.insertBefore(v, parent.childNodes[0]);
});
<div>
<div data-alphabetize="Beta">Joseph Alfred <strong>Beta</strong></div>
<div data-alphabetize="Alpha">Mark Unicode <strong>Alpha</strong></div>
<div data-alphabetize="Gamma">Graham <strong>Gamma</strong>-Python</div>
<div data-alphabetize="Omega">Matthew <strong>Omega</strong></div>
</div>

Get javascript accordion to work with 1 ID

I'm using a nice accordion script from TYMPANUS for my website though the difference is that I am using it multiple times on 1 page like:
<div id="st-accordion" class="st-accordion">
<ul>
<li>
Flowers <span class="st-arrow">View <b>Details</b></span>
<div class="st-content">
<p>She packed her seven versalia, put her initial into the belt and made
herself on the way. When she reached the first hills of the Italic Mountains, she had
a last view back on the skyline of her hometown Bookmarksgrove, the headline of
Alphabet Village and the subline of her own road, the Line Lane.</p>
</div>
</li>
</ul>
</div>
<div id="st-accordion" class="st-accordion">
<ul>
<li>
Flowers <span class="st-arrow">View <b>Details</b></span>
<div class="st-content">
<p>She packed her seven versalia, put her initial into the belt and made
herself on the way. When she reached the first hills of the Italic Mountains, she had
a last view back on the skyline of her hometown Bookmarksgrove, the headline of
Alphabet Village and the subline of her own road, the Line Lane.</p>
</div>
</li>
</ul>
</div>
Please see this FIDDLE 01
As you can see I have 2 separate elements (I need it to be separate) using that accordion but because the ID is the same, you can clearly see that the second accordion isn't working.
Javascript for this fiddle:
$(function() {
$('#st-accordion').accordion();
});
So to get it working, I created separate ID's for each element like in this FIDDLE 02
Javascript for this fiddle:
$(function() {
$('#st-accordion-01, #st-accordion-02').accordion();
});
BUT I don't like having to always create an extra / different ID, so it there a way to get FIDDLE 01 working without having to resort to FIDDLE 02? ... Or is this just not possible?
*The Javascripts posted above are at the very bottom of the javascript section in jsfiddle
There can not / must not be elements with the same id name in one page.
http://www.w3.org/TR/html5/elements.html#the-id-attribute
The id attribute specifies its element's unique identifier (ID). The
value must be unique amongst all the IDs in the element's home subtree
and must contain at least one character. The value must not contain
any space characters.
So to fix your problem, just remove the same ids and just leave the class st-accordion
http://jsfiddle.net/5h559dyj/3/
$(function() {
$('.st-accordion').accordion();
});

How to select certain class in jQuery

Here's the problem; I have a couple of divs like the one below, same class name, no id. I need to modify the <span> text below the <h4> tag, based on where's the mouse cursor on those 3 images. I do this using javascript, by using mouseenter() method. The problem is that the method changes every span text from whole web page, not only from the class with class name "parent" where the mouse cursor is at the moment.
<div class="parent">
<div class="parent.child">
<div class="parent.chil.child">
<div class="parent.chil.child.child">
<img src ="link1" data-price="a">
<img src ="link2" data-price="b">
<img src ="link3" data-price="c">
</div>
</div>
</div>
<h4>
text
</h4>
<p><span class = "spanClassName">text to be changed</span>some text</p>
<div class=child1"></div>
</div>
How do I select only the link where's the mouse, from the curent "parent" div, even if there are several div with same class name, "parent".
I hope I was understood, if not, please ask and I try to explain more.
You can use .closest() to find parent with .parent class
$('.parent\\.chil\\.child\\.child img').on('hover', function(){
$(this).closest('.parent').find('.spanClassName').text($(this).attr('data-price'))
});
DEMO
Additionally as per documents you need to escape . in your selectors
To use any of the meta-characters ( such as !"#$%&'()*+,./:;<=>?#[]^`{|}~ ) as a literal part of a name, it must be escaped with with two backslashes: \\. For example, an element with id="foo.bar", can use the selector $("#foo\\.bar").
If you already know the specific parent node, just use parent.find('.spanClassName'). If you don't have the parent node, but you know which link the mouse is on, you can use link.closest('.parent').find('.spanClassName').

Categories

Resources