I'm inserting an element with the following code:
var descriptions = document.querySelectorAll(".product-item-info");
function showDescription() {
descriptions.forEach(description => {
description.insertAdjacentHTML("beforeend", "<div class='description'>Some text</div>");
});
}
showDescription();
This works well. But how do I check if a child of .product-item-info contains specific text, and if so then not add the markup to said element?
I know how to do this with jQuery.
Edit: change afterend to beforeend
You do it with the DOM the same way you do it with jQuery, it's just you have to do a bit that jQuery does behind the scenes in your own code: Seeing if elements contain that text. There's no real shortcut, you just have to look at the text of the element:
const descriptions = document.querySelectorAll(".product-item-info");
function showDescription() {
for (const description of descriptions) {
if (!description.textContent.includes("Some text")) {
description.insertAdjacentHTML("beforeend", "<div class='description'>Some text</div>");
}
}
}
showDescription();
(The optional chaining handles the case where a description doesn't have a next element sibling.)
Related
I am trying to add a CSS class to each div on a page that contains the string Subject:
I tried
var elList = document.querySelectorAll("div");
elList.forEach(function(el) {
if (el.innerHTML.indexOf("Subject") !== -1) {
console.log(el);
el.setAttribute('class', "newClass");
}
});
but it didn't return any nodes. And also
var headings = document.evaluate("//*[contains(normalize-space(text()), 'Subject:')]", document, null, XPathResult.ANY_TYPE, null );
while(thisHeading = headings.iterateNext()){
thisHeading.setAttribute('class', "newClass");
console.log(thisHeading);
}
which returned an XPathResult that didn't seem to have any nodes as part of the object.
This is what the HTML looks like, although it is deeply nested inside the document body.
<div class="note-stream-header">Subject: Please Reply to This</div>
How can I select all nodes that contain a string and add a class to them with JS?
Your approach is fine, but since you are interested in the content of an element, use .textContent instead of innerHTML.
See additional comments inline.
// .forEach is not supported in all browsers on node lists
// Convert them to arrays first to be safe:
var elList = Array.prototype.slice.call(
document.querySelectorAll("div"));
elList.forEach(function(el) {
// Use .textContent when you aren't interested in HTML
if (el.textContent.indexOf("Subject") > -1) {
console.log(el);
el.classList.add("newClass"); // Use the .classList API (easier)
}
});
.newClass { background-color:#ff0; }
<div>The subject of this discussion is JavaScript</div>
<div>The topic of this discussion is JavaScript</div>
<div>The queen's royal subjects weren't amused.</div>
<div>Subject: textContent DOM property</div>
$(document).ready(function () {
var pageTitle = $('h3.title-hidden');
if (title === pageTitle) {
$('.content-hidden').remove();
}
});
Where is my mistake?
pageTitle is the jQuery object that wraps the h3 element. It doesn't contain the text of that element. If you want the text, use .text():
var pageTitle = $('h3.title-hidden').text();
That assumes, based on your code, that there's just one h3.title-hidden on the page, and that you want to remove all .content-hidden elements if that one element's text matches the document title.
With pure Javascript I want to create a tab effect to toggle content in a div. Content is the name of the class I want to add or remove the second class active from
<script>
function changeClass(element) {
if (classList !=='active') {
element.classList.add('active');
}
else { element.classList.remove('active'); }
}
</script>
<ul>
<li onclick = "changeClass("content")">
The error is that you're not selecting any elements (wonder why nobody caught this), but trying to change the classlist of a string ("content".classList...). Make sure you select the proper element first:
function changeClass(element) {
element = document.getElementsByClassName(element)[0]; // assuming here we're selecting the first one
if (!element.classList.contains('active')) { // had to fix this as variable classList wasn't defined
element.classList.add('active');
}
else {
element.classList.remove('active');
}
}
Also, as #Teemu suggested in comments, but refused to write it, feel free to use element.classList.toggle('active');.
So the whole code should be:
function changeClass(element) {
element = document.getElementsByClassName(element)[0]; // assuming here we're selecting the first one
element.classList.toggle('active');
}
If you want to simply toggle between two classes, you can do something like this:
function changeClass(element) {
element.classList.toggle('Content');
}
Though in this case you've to pass a reference to the element rather than it's className.
I'm generating a div dynamically and I've to check whether a dynamically generated div exists or not ? How can I do that?
Currently I'm using the following which does not detects the div generated dynamically. It only detects if there is already an element with the id contained in the HTML template.
$(function() {
var $mydiv = $("#liveGraph_id");
if ($mydiv.length){
alert("HHH");
}
});
How can I detect the dynamically generated div?
If mutation observes aren't an option due to their browser compatibility, you'll have to involve the code that's actually inserting the <div> into the document.
One options is to use a custom event as a pub/sub.
$(document).on('document_change', function () {
if (document.getElementById('liveGraph_id')) {
// do what you need here
}
});
// without a snippet to go on, assuming `.load()` for an example
$('#container').load('/path/to/content', function () {
$(this).trigger('document_change');
});
If it is added dinamically, you have to test again. Let's say, a click event
$("#element").click(function()
{
if($("#liveGraph_id").length)
alert("HHH");
});
How you inserting your dynamic generated div?
It works if you do it in following way:
var div = document.createElement('div');
div.id = 'liveGraph_id';
div.innerHTML = "i'm dynamic";
document.getElementsByTagName('body')[0].appendChild(div);
if ($(div).length > 0) {
alert('exists'); //will give alert
}
if ($('#liveGraph_id').length > 0) {
alert('exists'); //will give alert
}
if ($('#liveGraph_id_extra').length > 0) {
alert('exists'); //wont give alert because it doesn't exist.
}
jsfiddle.
Just for interest, you can also use a live collection for this (they are provided as part of the DOM). You can setup a collection of all divs in the page (this can be done in the head even before the body is loaded):
var allDivs = document.getElementsByTagName('div');
Any div with an id is available as a named property of the collection, so you can do:
if (allDivs.someId) {
// div with someId exists
}
If the ID isn't a valid identifier, or it's held in a variable, use square bracket notation. Some play code:
<button onclick="
alert(!!allDivs.newDiv);
">Check for div</button>
<button onclick="
var div = document.createElement('div');
div.id = 'newDiv';
document.body.appendChild(div);
">Add div</button>
Click the Check for div button and you'll get false. Add the div by clicking the Add div button and check again—you'll get true.
is very simple as that
if(document.getElementById("idname")){
//div exists
}
or
if(!document.getElementById("idname")){
// don't exists
}
I have the following HTML snippet:
<span class="target">Change me <a class="changeme" href="#">now</a></span>
I'd like to change the text node (i.e. "Change me ") inside the span from jQuery, while leaving the nested <a> tag with all attributes etc. intact. My initial huch was to use .text(...) on the span node, but as it turns out this will replace the whole inner part with the passed textual content.
I solved this with first cloning the <a> tag, then setting the new text content of <span> (which will remove the original <a> tag), and finally appending the cloned <a> tag to my <span>. This works, but feels such an overkill for a simple task like this. Btw. I can't guarantee that there will be an initial text node inside the span - it might be empty, just like:
<span class="target"><a class="changeme" href="#">now</a></span>
I did a jsfiddle too. So, what would be the neat way to do this?
Try something like:
$('a.changeme').on('click', function() {
$(this).closest('.target').contents().not(this).eq(0).replaceWith('Do it again ');
});
demo: http://jsfiddle.net/eEMGz/
ref: http://api.jquery.com/contents/
Update:
I guess I read your question wrong, and you're trying to replace the text if it's already there and inject it otherwise. For this, try:
$('a.changeme').on('click', function() {
var
$tmp = $(this).closest('.target').contents().not(this).eq(0),
dia = document.createTextNode('Do it again ');
$tmp.length > 0 ? $tmp.replaceWith(dia) : $(dia).insertBefore(this);
});
Demo: http://jsfiddle.net/eEMGz/3/
You can use .contents():
//set the new text to replace the old text
var newText = 'New Text';
//bind `click` event handler to the `.changeme` elements
$('.changeme').on('click', function () {
//iterate over the nodes in this `<span>` element
$.each($(this).parent().contents(), function () {
//if the type of this node is undefined then it's a text node and we want to replace it
if (typeof this.tagName == 'undefined') {
//to replace the node we can use `.replaceWith()`
$(this).replaceWith(newText);
}
});
});
Here is a demo: http://jsfiddle.net/jasper/PURHA/1/
Some docs for ya:
.contents(): http://api.jquery.com/contents
.replaceWith(): http://api.jquery.com/replacewith
typeof: https://developer.mozilla.org/en/JavaScript/Reference/Operators/typeof
Update
var newText = 'New Text';
$('a').on('click', function () {
$.each($(this).parent().contents(), function () {
if (typeof this.tagName == 'undefined') {
//instead of replacing this node with the replacement string, just replace it with a blank string
$(this).replaceWith('');
}
});
//then add the replacement string to the `<span>` element regardless of it's initial state
$(this).parent().prepend(newText);
});
Demo: http://jsfiddle.net/jasper/PURHA/2/
You can try this.
var $textNode, $parent;
$('.changeme').on('click', function(){
$parent = $(this).parent();
$textNode= $parent.contents().filter(function() {
return this.nodeType == 3;
});
if($textNode.length){
$textNode.replaceWith('Content changed')
}
else{
$parent.prepend('New content');
}
});
Working demo - http://jsfiddle.net/ShankarSangoli/yx5Ju/8/
You step out of jQuery because it doesn't help you to deal with text nodes. The following will remove the first child of every <span> element with class "target" if and only if it exists and is a text node.
Demo: http://jsfiddle.net/yx5Ju/11/
Code:
$('span.target').each(function() {
var firstChild = this.firstChild;
if (firstChild && firstChild.nodeType == 3) {
firstChild.data = "Do it again";
}
});
This is not a perfect example I guess, but you could use contents function.
console.log($("span.target").contents()[0].data);
You could wrap the text into a span ... but ...
try this.
http://jsfiddle.net/Y8tMk/
$(function(){
var txt = '';
$('.target').contents().each(function(){
if(this.nodeType==3){
this.textContent = 'done ';
}
});
});
You can change the native (non-jquery) data property of the object. Updated jsfiddle here: http://jsfiddle.net/elgreg/yx5Ju/2/
Something like:
$('a.changeme3').click(function(){
$('span.target3').contents().get(0).data = 'Do it again';
});
The contents() gets the innards and the get(0) gets us back to the original element and the .data is now a reference to the native js textnode. (I haven't tested this cross browser.)
This jsfiddle and answer are really just an expanded explanation of the answer to this question:
Change text-nodes text
$('a.changeme').click(function() {
var firstNode= $(this).parent().contents()[0];
if( firstNode.nodeType==3){
firstNode.nodeValue='New text';
}
})
EDIT: not sure what layout rules you need, update to test only first node, otherwise adapt as needed