Check if field contains numbers, wrap the numbers with container and class? - javascript

If you have something simple like this in HTML:
<div class="main">Item 7,000</div>
How to make javascript apply an html element and a class for the 7,000 part (because its numeric) on page load? To something like this:
<div class="main">Item <span class="wrap">7,000</span></div>
Or maybe just an html element, if with class not possible.
I apologies I don't have any code to share right now. I'm still browsing other questions.
Maybe it should be something with jQuery if $.isNumeric() is true then apply element?

There will be edge case but accomplishes your goal
$(function () {
$(".main").each(function (index, element) {
var $element = $(element);
var wrapped = $element.text().replace(/(.+?)((\d{1,3},?)+)/g, '$1<span class="wrap">$2</span>');
$element.html(wrapped);
});
});
.wrap {
background-color: yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="main">Item 7,000</div>
<div class="main">Item 960</div>
<div class="main">Item 7,000,000</div>

If you use node.textContent you can get the string inside the div
string = document.querySelector('#main').textContent // --> "Item 7,000"
From here you can use Array#split to separate each word:
array = string.split(' '); --> ['Item','7,000']
Now remove the comma and check isNaN for each, return an array w:
newNode = array.map((e)=> isNaN(e.replace(/,/g,"")) ? e : {element:'span', content: e})
Now you have ['Item', { element: 'span', content: '7,000'}] which you can use to generate the contents of the div element in your example...
There might be way better ways to do this, I am just trying to help :)

Use javascript regex find a matching number and replace with span
var regex =/\b(\d+)\b/g
var regex =/\b(\d+)\b/g
var text = document.getElementById("main").innerHTML;
document.getElementById("main").innerHTML = text.replace(regex,'<span class="wrap">$1</span>')
span.wrap{
color:red;
}
<p id="main">Item : 7000</p>

Related

How does Javascript filter out HTML tags while selecting words using regular expressions?

Noticeļ¼š
I'm not parsing HTML with regex,
here I only use it for plain text.
It's just that it goes beyond plain text and affects other html tags
Why does everyone say I should use DOM instead of regular expressions?
DOM obviously cannot select all words on a web page based on an array of words.
before I used document.createTreeWalker() to filter all text labels, it was too complicated and caused more errors.
So I want to do it with simple regex instead. Do you have a better way?
I think just 'filter out all text inside "<>"' with very simple regex syntax wouldn't it work? Why make it so complicated?
I need to select the words from the page based on an array of words, and wrap the words around 'span' tags (keeping the original HTML tags).
The problem with my code is that it replaces the attribute values of the HTML tag as well.
I need regular expressions to filter out HTML tags and select words.
I added a condition to the regular expression :(^<.*>), but it didn't work and broke my code.
How to do?
My code:
code Error: The <div id="text"> should not be wrapped around the SPAN tag
<!DOCTYPE html>
<html>
<head>
<style>span{background:#ccc;}</style>
<script>
//wrap span tags for all words
function add_span(word_array, element_) {
for (let i = 0; i < word_array.length; i++) {
var reg_str = "([\\s.?,\"\';:!()\\[\\]{}<>\/])"; // + "^(<.*>)"
var reg = new RegExp(reg_str + "(" + word_array[i] + ")" + reg_str, 'g');
element_ = element_.replace(reg, '$1<span>$2</span>$3');
}
return element_;
}
window.onload = function(){
console.log(document.body.innerText);
// word array
var word_array = ['is', 'test', 'testis', 'istest', 'text']
var text_html = add_span(word_array, document.body.innerHTML);
document.body.innerHTML = text_html;
console.log(text_html);
}
</script>
</head>
<body>
<div id="text"><!--Error: The class attribute value here should not be wrapped around the SPAN tag-->
is test testis istest,
is[test]testis{istest}testis(istest)testis istest
</div>
</body></html>
I had fun with this one and learned a few things too. You could replace the traversal implementation with TreeWalker if you'd like. I added a nested div#text2 to demonstrate how it works with arbitrary tree depth. I tried to keep the same general approach you were using, but needed to make some modifications to the regex and add tree traversal. Hope this helps!
function traverse(tree) {
const queue = [tree];
while (queue.length) {
const node = queue.shift();
if (node.nodeType === Node.TEXT_NODE) {
const textContent = node.textContent.trim();
if (textContent) {
const textContentWithSpans = textContent
.replaceAll(/\b(is|test|testis|istest|text)\b/g, '<span>$&</span>');
const template = document.createElement('template');
template.innerHTML = textContentWithSpans;
const fragment = template.content;
node.parentNode.replaceChild(fragment, node);
}
}
for (let child of node.childNodes) {
queue.push(child);
}
}
}
traverse(document.getElementById('demo-wrapper'));
<div id="demo-wrapper">
<div id="text">
is test testis istest,
is[test]testis{istest}testis(istest)testis istest
<div id="text2">
foo bar test istest
</div>
</div>
</div>

How do I get a string that's the text() of an element, but with spaces added after divs?

JSFiddle here
Hi! I'm trying to output a string from the .contents().text() of an element... but with spaces between the content of each div (without changing the actual DOM).
HTML:
<!-- I don't have control over how many divs are in .myTextArea, or what text. It's really dynamic. There are also lists, etc.--tons of different types of elements. -->
<div class="myTextArea">
<div>Hey there!</div><div>I like turtles.</div><div>Do you like them?</div>
</div>
jQuery:
var myTextDescription = $(".myTextArea").contents().text();
console.log(myTextDescription);
Currently, it outputs:
Hey there!I like turtles.Do you like them?
...and this is what I want it to output: The same thing, but with spaces after the content of each div:
Hey there! I like turtles. Do you like them?
Note: Other answers on SO make you change the actual DOM (AKA, they add actual spaces after the elements on the page), and then they just grab the text() string. I don't want to change the DOM.
Also, I can't use .html() instead and try to strip away stuff, because there will be wayyyyyy too many types of elements to worry about.
JSFiddle here
You're almost there. Replace .text() with:
//get text content of all nodes
.map((i,d) => d.textContent).get()
//remove white space
.filter(t => !!t.trim())
//join the text from all nodes with a space
.join(' ');
Check out the demo below:
var myTextDescription = $(".myTextArea").contents().map((i,d) => d.textContent).get().filter(t => !!t.trim()).join(' ');
console.log(myTextDescription);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="myTextArea">
<div>Hey there!</div><div>I like turtles.</div><div>Do you like them?</div>
</div>
In case you needed to exclude text in a div, say with a class exclude you can use the :not() psedo selector like so:
... .contents(':not(".exclude")') ....
..as in the demo below:
var myTextDescription = $(".myTextArea").contents(':not(".exclude")').map((i,d) => d.textContent).get().filter(t => !!t.trim()).join(' ');
console.log(myTextDescription);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="myTextArea">
<div>Hey there!</div><div class="exclude">Please exclude this!</div><div>I like turtles.</div><div>Do you like them?</div><div class="exclude">Please exclude this too!</div>
</div>
One way to solve this is to use JavaScript's querySelectorAll method to return a list of all the DIVs in your myTextArea element. You can then run through each element in the list, get its innerText and place a space after each one:
var myTexts = document.querySelectorAll('.myTextArea > div');
var show = document.querySelector('#show');
var output = "";
for (var x = 0; x < myTexts.length; x++) {
if (output == "") { // Skip adding space before first string
output = myTexts[x].innerText;
} else { // Add space before each appended string
output += " " + myTexts[x].innerText;
}
}
show.innerText = output;
<div class="myTextArea">
<div>Hey there!</div>
<div>I like turtles.</div>
<div>Do you like them?</div>
</div>
<div id="show"></div>

Select element by tag/classname length

I'd like to select an element using javascript/jquery in Tampermonkey.
The class name and the tag of the elements are changing each time the page loads.
So I'd have to use some form of regex, but cant figure out how to do it.
This is how the html looks like:
<ivodo class="ivodo" ... </ivodo>
<ivodo class="ivodo" ... </ivodo>
<ivodo class="ivodo" ... </ivodo>
The tag always is the same as the classname.
It's always a 4/5 letter random "code"
I'm guessing it would be something like this:
$('[/^[a-z]{4,5}/}')
Could anyone please help me to get the right regexp?
You can't use regexp in selectors. You can pick some container and select its all elements and then filter them based on their class names. This probably won't be super fast, though.
I made a demo for you:
https://codepen.io/anon/pen/RZXdrL?editors=1010
html:
<div class="container">
<abc class="abc">abc</abc>
<abdef class="abdef">abdef</abdef>
<hdusf class="hdusf">hdusf</hdusf>
<ueff class="ueff">ueff</ueff>
<asdas class="asdas">asdas</asdas>
<asfg class="asfg">asfg</asfg>
<aasdasdbc class="aasdasdbc">aasdasdbc</aasdasdbc>
</div>
js (with jQuery):
const $elements = $('.container *').filter((index, element) => {
return (element.className.length === 5);
});
$elements.css('color', 'red');
The simplest way to do this would be to select those dynamic elements based on a fixed parent, for example:
$('#parent > *').each(function() {
// your logic here...
})
If the rules by which these tags are constructed are reliably as you state in the question, then you could select all elements then filter out those which are not of interest, for example :
var $elements = $('*').filter(function() {
return this.className.length === 5 && this.className.toUpperCase() === this.tagName.toUpperCase();
});
DEMO
Of course, you may want initially to select only the elements in some container(s). If so then replace '*' with a more specific selector :
var $elements = $('someSelector *').filter(function() {
return this.className.length === 5 && this.className.toUpperCase() === this.tagName.toUpperCase();
});
You can do this in vanilla JS
DEMO
Check the demo dev tools console
<body>
<things class="things">things</things>
<div class="stuff">this is not the DOM element you're looking for</div>
</body>
JS
// Grab the body children
var bodyChildren = document.getElementsByTagName("body")[0].children;
// Convert children to an array and filter out everything but the targets
var targets = [].filter.call(bodyChildren, function(el) {
var tagName = el.tagName.toLowerCase();
var classlistVal = el.classList.value.toLowerCase();
if (tagName === classlistVal) { return el; }
});
targets.forEach(function(el) {
// Do stuff
console.log(el)
})

getElementById with changing IDs

I need to hide all the elements that have the string "replies-36965584" anywhere in their IDs.
HTML:
<div id="replies-36965584_1">aaaa</div>
<div id="replies-36965584_2">aaaa</div>
<div id="replies-36965584_3">aaaa</div>
<div id="replies-36965584_4">aaaa</div>
<div id="replies-36222224_2">nnnn</div>
JavaScript:
document.getElementById("replies-36965584").style.display="none"
How can I modify this JS to select the first four elements?
You can do this with CSS and attribute selectors.
[att^=val]
Represents an element with the att attribute whose value begins with the prefix "val". If "val" is the empty string then the selector does not represent anything.
Source: http://www.w3.org/TR/css3-selectors/#attribute-substrings
jsfiddle
CSS
[id^="replies-36965584_"] {
display: none;
}
Is using jQuery an option? If so, this is dead simple:
$(document).ready(function(){
$('div[id^="replies-36965584"]').hide();
});
If you're unfamiliar with jQuery, here's a link to get started: http://learn.jquery.com/javascript-101/getting-started/
EDIT: Fixed syntax error.
EDIT: Added jsFiddle: http://jsfiddle.net/xbVp9/
If you don't know certain literal values but you know the general pattern and only the number will change, then I will consider some matching with regular expresiion.
You can do it the painful way:
var o = document.getElementsByTagName("div");
for (var i=0;i<o.length;i++) {
if(o[i].id.indexOf('replies-36965584') == 0) {
o[i].style.display = 'none';
}
}
The only way to do this with vanilla javascript that I know of, is to fetch all the divs on the page, and test the id's for the ones you want.
var divs = document.getElementsByTagName('div');
for (var i = 0; i < divs.length; ++i) {
var div = divs[i];
if (/replies-36965584/.test(div.id)) {
div.style.display = 'none';
}
}

Method that gets an element's child text, regardless of whether in <p> tag

I'm building a scraper in Node.js and have come up against a slight problem. I'm trying to build a function which gets an element's text, regardless of whether it's embedded in a <p> tag, in a <span> or just a <div> with text inside.
The following currently works ONLY for text contained in <p> tags:
function getDescription(product){
var text =[];
$('.description *')
.each(function(i, elem) {
var dirty = $(this).text();
var clean = sanitize(dirty).trim();
if (clean.length){
text.push(clean);
}
});
text.join(',');
sanitize(text).trim();
return text;
}
This works for code like this:
<div class="description">
<p>Test test test</p>
</div>
But doesn't work for this:
<div class="description">
Test test test
</div>
For reference, the sanitize and trim functions are part of Node Validator, but that's not particularly relevant to my problem - they just take a string and remove whitespace from it.
Any ideas on what I can do to make the one function work for BOTH instances? To add insult to injury, I'm slightly more limited as node uses the cheerio library to replicate some functions of jQuery, but not all of them.
Use .contents() instead of *
function getDescription(product){
var text =[];
$('.description').contents()
.each(function(i, elem) {
var dirty = $(this).text();
var clean = sanitize(dirty).trim();
if (clean.length){
text.push(clean);
}
});
text.join(',');
sanitize(text).trim();
return text;
}
Use $(".description").contents() (docs).
The * only selects element nodes, but not text nodes.
You can use innerText:
var text =[];
$('.description').each(function(i, elem) {
var dirty = elem.innerText;
var clean = sanitize(dirty).trim();
if (clean.length){
text.push(clean);
}
});

Categories