I need a function that finds text (child text nodes) inside some (for this example the div) elements and wraps the text in a paragraph.
Text1
<div>
<div>
Text2
<div>Text3</div>
</div>
<div>Text4</div>
<p>Text5</p>
</div>
<div>Text6</div>
<div>
Text7
<p>Text8</p>
Text9
</div>
tried did it like this:
const fn = (doc) => {
const coll = [...doc.childNodes];
coll.forEach(el => {
if (el.tagName === 'DIV' && el.childElementCount === 0) {
el.innerHTML = `<p>${el.innerHTML}</p>`;
}
if (el.childElementCount > 0) {
el.childNodes.forEach(item => {
if (item.nodeType === 3 && item.textContent.trim()) {
const content = item.textContent.trim();
item.innerHTML = `<p>${content}</p>`;
console.log('2: ', item.innerHTML);
}
});
fn(el);
}
});
}
but it works wrong - in if condition (el.childElementCount > 0) in console log, I got all needed nodes in p tags. but not in result. item.innerHTML = `<p>${content}</p>`; not apply it to document :(.
Can anyone help to fix it?
result i need:
Text1
<div>
<div>
<p>Text2</p>
<div><p>Text3</p></div>
</div>
<div><p>Text4</p></div>
<p>Text5</p>
</div>
<div><p>Text6</p></div>
<div>
<p>Text7</p>
<p>Text8</p>
<p>Text9</p>
</div>
You can use replaceWith() to replace the text node with the paragraph element
function filterTextNode(node) {
var textNodes = [];
for (node = node.firstChild; node; node = node.nextSibling) {
if (node.nodeType == 3 && node.textContent.trim()) textNodes.push(node);
else textNodes = textNodes.concat(filterTextNode(node));
}
return textNodes;
}
function wrapTextNode(text) {
var p = document.createElement('p');
p.innerHTML = text.textContent;
text.replaceWith(p);
}
const fn = (doc) => {
var textNodes = filterTextNode(doc);
textNodes.forEach(text => {
if (text.parentNode.tagName != 'P') {
wrapTextNode(text);
}
});
}
fn(document.body)
p {
color: red;
}
Text1
<div>
<div>
Text2
<div>Text3</div>
</div>
<div>Text4</div>
<p>Text5</p>
</div>
<div>Text6</div>
<div>
Text7
<p>Text8</p>
Text9
</div>
References
Find all text nodes in HTML page
https://developer.mozilla.org/en-US/docs/Web/API/ChildNode/replaceWith
By considering the childElementCount of the div we can get your desired result. Rewrite your function like below.
const fn = (document) => {
let elements = document.getElementsByTagName('div');
elements = Array.prototype.slice.call(elements);
elements.forEach(el => {
if (el.innerHTML && el.childElementCount === 0) {
el.innerHTML = `<p>${el.innerHTML}</p>`;
}
});
}
var elements = document.getElementsByTagName('div');
for(var i=0;i<elements.length;i++){
if(elements[i]===0){
console.log(elements[i].textContent);
}
else{
var y=elements[i].innerHTML
console.log(y)
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>testing result</title>
</head>
<body>
<div>Text1</div>
<div>
<div>Text2</div>
<p>Text3</p>
</div>
<div>Text4</div>
</body>
</body>
</html>
i tryed this but i cant get last two div(4 and 2) like this output in the console <div>text4</div> and <div>text2</div> i hope this my help and i dont know how to print last two div innerHTML
Related
This is where I enter a text:
Upon clicking on the COUNT button, it goes to this page:
My text and word count got displayed. But how do I get the word density of this text using vanilla JavaScript, and actually display it on this page?
Here's my HTML:
<!DOCTYPE html>
<html>
<head>
<title>Word Counter</title>
</head>
<body>
<div id="input-page">
<h1>Word Counter</h1>
<form action="">
<textarea id="text" type="text" rows="22" cols="60"></textarea>
<br />
</form>
<button onclick="displayText()">COUNT</button>
</div>
<div id="count-page" style="display: none;">
<h1>Your Text:</h1>
<p id="display-text"></p>
<div id="word-count"></div>
<div id="word-density">
<h1>Word Density:</h1>
</div>
</div>
</body>
<script src="app.js"></script>
</html>
JavaScript:
const displayText = () => {
const inputPage = document.getElementById("input-page");
const countPage = document.getElementById("count-page");
const text = document.getElementById("text");
const textValue = text.value;
if (text.value !== "") { // normal flow will continue if the text-area is not empty
inputPage.style.display = "none";
document.getElementById("display-text").innerText = textValue;
countPage.style.display = "block";
} else { // if the text-area is empty, it will issue a warning.
alert("Please enter some text first.")
}
const countWords = (str) => {
return str.split(" ").length;
};
const wordCount = (countWords(textValue));
const renderWordCount = () => {
const wordCountDiv = document.getElementById("word-count");
wordCountDiv.innerHTML = "<h1> Words Counted: " + wordCount + "</h1>";
};
renderWordCount();
};
For getting the word density like #SimoneRossaini told, simply use a list and save how many times you found each word. This ends up like this for example:
I modified your code and added the word density.
const displayText = () => {
const inputPage = document.getElementById("input-page");
const countPage = document.getElementById("count-page");
const text = document.getElementById("text");
const textValue = text.value;
if (text.value !== "") { // normal flow will continue if the text-area is not empty
inputPage.style.display = "none";
document.getElementById("display-text").innerText = textValue;
countPage.style.display = "block";
} else { // if the text-area is empty, it will issue a warning.
alert("Please enter some text first.")
}
const countWords = (str) => {
return str.split(" ").length;
};
const wordCount = (countWords(textValue));
const renderWordCount = () => {
const wordCountDiv = document.getElementById("word-count");
wordCountDiv.innerHTML = "<h1> Words Counted: " + wordCount + "</h1>";
};
const getWordDensity = (str) => {
let wordList = {};
str.split(/[\s\.,]+/).forEach(word => {
if(typeof wordList[word] == "undefined"){
wordList[word] = 1;
}
else{
wordList[word]++;
}
});
return wordList;
};
const wordDensity = (getWordDensity(textValue));
const renderWordDensity = () => {
const wordDensityDiv = document.getElementById("word-density");
let table = "<table>";
for(let word in wordDensity){
table += "<tr><td>" + word + "</td><td>" + wordDensity[word] + "</td></tr>";
}
table += "</table>";
wordDensityDiv.innerHTML = "<h1> Word Density: </h1>" + table;
};
renderWordCount();
renderWordDensity();
};
<!DOCTYPE html>
<html>
<head>
<title>Word Counter</title>
</head>
<body>
<div id="input-page">
<h1>Word Counter</h1>
<form action="">
<textarea id="text" type="text" rows="22" cols="60"></textarea>
<br />
</form>
<button onclick="displayText()">COUNT</button>
</div>
<div id="count-page" style="display: none;">
<h1>Your Text:</h1>
<p id="display-text"></p>
<div id="word-count"></div>
<div id="word-density"></div>
</div>
</body>
<script src="app.js"></script>
</html>
Given a DOM structure like this:
<div>
<div>
<span>
<img/>
<i>
<span></span>
<meter></meter>
</i>
<a><span></span></a>
</span>
</div>
<nav>
<form>
<input/>
<button></button>
</form>
</nav>
</div>
Wondering how you take that and then return a flat array of all the selectors:
[
'div > div > span > img',
'div > div > span > i > span',
'div > div > span > i > meter',
'div > div > span > a > span',
'div > nav > form > input',
'div > nav > form > button'
]
My attempt hasn't gotten anywhere:
function outputSelectors(array, node) {
var tag = node.tagName
array.push(tag)
for (var i = 0, n = node.children.length; i < n; i++) {
var child = node.children[i]
outputSelectors(array, child)
}
}
outputSelectors([], document.body.children[0])
Not sure where to go from here.
One possible, a non-recursive approach going from top (root, to be precise) to bottom:
function collectLeafNodePathes(root) {
const paths = [];
const selectorParts = [];
let el = root;
while (el) {
const tagName = el.tagName.toLowerCase();
if (el.childElementCount) {
selectorParts.push(tagName);
el = el.firstElementChild;
continue;
}
paths.push(selectorParts.concat([tagName]).join(' > '));
do {
if (el.nextElementSibling) {
el = el.nextElementSibling;
break;
}
el = el.parentNode;
selectorParts.pop();
if (el === root) {
el = null;
}
} while (el);
}
return paths;
}
const selectors = collectLeafNodePathes(document.getElementById('xxx'));
console.log(selectors);
<div id="xxx">
<div>
<span>
<img/>
<i>
<span></span>
<meter></meter>
</i>
<a><span></span></a>
</span>
</div>
<nav>
<form>
<input/>
<button></button>
</form>
</nav>
</div>
That last part (do-while loop) is a bit rough around the edges, though; open to any improvement.
I've used helper properties (childElementCount, firstElementChild, nextElementSibling) to skip checking for text nodes and stuff. If that's not an option (because of compatibility reasons), it's easy to either implement polyfills or just 'rewind' the loop on non-element nodes.
You can map all elements on a page using the getPath method from this answer.
Best try this in your own console, as the snippet takes some time to run, and the snippet's console doesn't seem to handle the output properly.
jQuery.fn.extend({
getPath: function () {
var path, node = this;
while (node.length) {
var realNode = node[0], name = realNode.localName;
if (!name) break;
name = name.toLowerCase();
var parent = node.parent();
var sameTagSiblings = parent.children(name);
if (sameTagSiblings.length > 1) {
allSiblings = parent.children();
var index = allSiblings.index(realNode) + 1;
if (index > 1) {
name += ':nth-child(' + index + ')';
}
}
path = name + (path ? '>' + path : '');
node = parent;
}
return path;
}
});
const allElements = $("*");
const allPaths = allElements.map((_, e) => $(e).getPath());
console.log(allPaths);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Here is a version without jQuery, if that's preferable:
function getPath (node) {
var path;
while (node.parentElement) {
var name = node.localName;
if (!name) break;
name = name.toLowerCase();
var parent = node.parentElement;
var sameTagSiblings = [...parent.children].filter(e => e.localName === name);
if (sameTagSiblings.length > 1) {
allSiblings = parent.children;
var index = [...allSiblings].indexOf(node) + 1;
if (index > 1) {
name += ':nth-child(' + index + ')';
}
}
path = name + (path ? '>' + path : '');
node = parent;
}
return path;
};
const allElements = document.querySelectorAll("*");
const allPaths = [...allElements].map(e => getPath(e));
console.log(allPaths);
Slightly modifying this solution to get path and this one to get leaf nodes.
function getPath(node)
{
var path;
while (node.parentNode )
{
name = node.nodeName;
if (!name) break;
var parent = node.parentNode;
path = name + (path ? ' > ' + path : '');
node = parent;
}
return path;
}
function getLeafNodes()
{
var allNodes = document.getElementsByTagName("*");
var leafNodes = Array.from( allNodes ).filter(function(elem) {
return !elem.hasChildNodes();
});
return leafNodes;
}
var leadNodes = getLeafNodes() ;
var output = leadNodes.map( s => getPath(s) );
console.log(output);
<div>
<div>
<span>
<img/>
<i>
<span></span>
<meter></meter>
</i>
<a><span></span></a>
</span>
</div>
<nav>
<form>
<input/>
<button></button>
</form>
</nav>
</div>
You can create recursive function and check if current element contains children using children() method.
const result = []
const getTag = (el) => el.prop('tagName').toLowerCase()
function print(el, prev = '') {
prev = prev.length ? prev : getTag(el)
const children = el.children();
if(!children.length) result.push(prev)
else {
children.each(function() {
let tag = getTag($(this))
let str = prev + (prev.length ? ' > ' : '') + tag;
print($(this), str)
})
}
}
print($('#start'))
console.log(result)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="start">
<div>
<span>
<img/>
<i>
<span></span>
<meter></meter>
</i>
<a><span></span></a>
</span>
</div>
<nav>
<form>
<input/>
<button></button>
</form>
</nav>
</div>
To get array of unique selectors you can use Set on final result to remove duplicates.
let result = []
const getTag = (el) => el.prop('tagName').toLowerCase()
function print(el, prev = '') {
prev = prev.length ? prev : getTag(el)
const children = el.children();
if(!children.length) result.push(prev)
else {
children.each(function() {
let tag = getTag($(this))
let str = prev + (prev.length ? ' > ' : '') + tag;
print($(this), str)
})
}
}
print($('#start'))
result = [...new Set(result)]
console.log(result)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="start">
<div>
<span>
<img/>
<i>
<span></span>
<meter></meter>
</i>
<a><span></span></a>
<a><span></span></a>
</span>
</div>
<nav>
<form>
<input/>
<button></button>
</form>
</nav>
</div>
How can I convert the characters of a div into spans?
For example, I'd like to convert this:
<div>
Hello World
</div>
Into this:
<div>
<span>H</span>
<span>e</span>
<span>l</span>
<span>l</span>
<span>o</span>
<span>W</span>
<span>o</span>
<span>r</span>
<span>l</span>
<span>d</span>
</div>
I've tried this StackOverflow suggestion, but that converts spaces into spans. What I need is to convert only characters to spans:
$("div").each(function (index) {
var characters = $(this).text().split("");
$this = $(this);
$this.empty();
$.each(characters, function (i, el) {
$this.append("<span>" + el + "</span");
});
});
You can use String#replace method and html() method with a callback to reduce the code.
$("div").html(function(index, html) {
return html.replace(/\S/g, '<span>$&</span>');
});
$("div").html(function(index, html) {
return html.replace(/\S/g, '<span>$&</span>');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
Hello World
</div>
You can try with this simple JavaScript.
(function() {
var div, i, span = "";
div = document.querySelectorAll("div")[0];
for (i = 0; i < div.innerText.length; i++) {
if (div.innerText[i] !== " ") {
span += "<span>";
span += div.innerText[i];
span += "</span>";
}
}
div.innerHTML = span;
}());
<div>
Hello World
</div>
I'd prefer to use regular expression:
var txt = $('#container').text();
var newTxt = txt.replace(/\w/g,function(c){
return '<span>'+c+'</span>';
})
$('#container').html(newTxt);
span {
display:inline-block;
background-color:#dfdfdf;
color:#aaa;
padding:3px;
margin:3px;
border-radius:3px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
Hello World
</div>
var textWrapper = document.querySelector('h1');
textWrapper.innerHTML = textWrapper.textContent.replace(/\S/g, "<span class='letter'>$&</span>");
<h1>Hello world</h1>
$("div").each(function (index) {
var characters = $(this).text().split("");
$this = $(this);
$this.empty();
$.each(characters, function (i, el) {
if(el != ' ')
$this.append("<span>" + el + "</span");
});
put a condition for space
try:
$("div").each(function (index) {
var characters = $(this).text().split("");
characters = characters.filter(v => v != '');
$(this).empty();
for(var i =0; i < characters.length; i++) {
$(this).append("<span>" + characters[i] + "</span");
}
});
tried to write as little as I can
html
<div>
HelloWorld
</div>
js
var d=$("div");
var text=d.text();
text=$.trim(text);
d.empty();
for(i=0;i<text.length;i++){
var span=$("<span></span>");
span.text(text[i]);
d.append(span)
}
My projec is a e-book reader application. There is only a VebView in screen and getting data from html file. It has only text content. I need to do get text in first line of current screen. In next step, I need to find the text in document and getting the text's position of screen. Finally, I need to scroll the found position. All of things necessary to solve that problem: (WebView Text Zoom Issue in Android)
html content:
<!DOCTYPE html>
<head>
<style type="text/css">
p{} p.x1{} p.x2{} p.x3{} p.x4{}
h2.x1{} h2.x2{} h2.x3{} h2.x4{}
</style>
</head>
<body>
//paragraph-1
<p class="x1">Title-1</p>
<p class="x2">Title-2</p>
<p class="x3">Title-3</p>
<p class="x4">Title-4</p>
<p>Text content.</p>
//paragraph-2
<h class="x1">Title-1</p>
<h class="x2">Title-2</p>
<p>Text content.</p>
//paragraph-3
<h class="x3">Title-1</p>
<h class="x4">Title-2</p>
<p class="x3">Title-3</p>
<p>Text content.</p>
//paragraph-4
<h class="x2">Title-1</p>
<p class="x3">Title-2</p>
<p>Text content.</p>
//...
</body>
</html>
I think you are searching for this?
function findTopObject (elements) {
var top = Number.POSITIVE_INFINITY;
var obj = null;
Array.from(elements).forEach(function (o) {
var t = o.getBoundingClientRect(o).top;
if (t > 0.0 && top > t) {
obj = o;
top = t;
}
});
return obj;
}
for (var i = 0; i < 1000; i++) {
var p = document.createElement("p");
p.innerText = "Line " + i;
document.body.appendChild(p);
}
var elements = document.querySelectorAll("p");
var obj = null;
window.onscroll = function() {
if (obj != null) {
obj.style.backgroundColor = "";
}
obj = findTopObject(elements);
if (obj != null) {
obj.style.backgroundColor = "red";
}
};
function findTopObject(elements) {
var top = Number.POSITIVE_INFINITY;
var obj = null;
Array.from(elements).forEach(function(o) {
var t = o.getBoundingClientRect(o).top;
if (t > 0.0 && top > t) {
obj = o;
top = t;
}
});
return obj;
}
<body></body>
There is no direct way to do what you want,
First get html content by doing
HttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet(url);
HttpResponse response = client.execute(request);
String html = "";
InputStream in = response.getEntity().getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder str = new StringBuilder();
String line = null;
while((line = reader.readLine()) != null)
{
str.append(line);
}
in.close();
html = str.toString();
Then use sub string conccept to get what you want, have look Get Html content
Or you can use Jsoap parser
to get value from specified tag in HTML
Document doc=Jsoup.connect("http://www.yahoo.com").get();
Elements elements=doc.select("img");
for(Element e:elements)
{
System.out.println(e.attr("src"));
}
I am currently using an add button to add input from a text box into a list. I am also binding a button to each of these list elements and then appending them to the unordered list. How do I remove the element onclick of the corresponding remove button? Pure JavaScript only.
window.onload = function() {
var elements = [];
var textInput;
document.getElementById("addButton").onclick = function() {
textInput = document.getElementById("inputBox").value;
if (textInput == "") {
alert("Make sure your input is not empty!");
} else {
var liNode = document.createElement('li');
var btnNode = document.createElement('button');
var btnText = document.createTextNode("Remove Item");
btnNode.appendChild(btnText);
var textNode = document.createTextNode(textInput);
liNode.appendChild(textNode);
liNode.appendChild(btnNode);
document.getElementById("myInputList").appendChild(liNode);
}
}
function addElementToList(element) {
if (element != "") {
elements.push(element);
} else {
alert("Make sure the input field is not empty!")
}
}
}
<!DOCTYPE html>
<html>
<body>
<head>
<script src="func.js"></script>
</head>
<input type="text" id="inputBox">
<br>
<button id="addButton">Add</button>
<br>
<br>
<ul id="myInputList"></ul>
</body>
</html>
Use addEventListener to register click event over created button.
Use .remove(), removes the object from the tree it belongs to.
Try this:
window.onload = function() {
var elements = [];
document.getElementById("addButton").onclick = function() {
var textInput = document.getElementById("inputBox").value;
if (textInput == "") {
alert("Make sure your input is not empty!");
} else {
var liNode = document.createElement('li');
var btnNode = document.createElement('button');
var btnText = document.createTextNode("Remove Item");
btnNode.appendChild(btnText);
var textNode = document.createTextNode(textInput);
liNode.appendChild(textNode);
liNode.appendChild(btnNode);
document.getElementById("myInputList").appendChild(liNode);
btnNode.addEventListener('click', removeHandler);
}
}
function removeHandler() {
this.parentNode.remove(); // this will be `button` element and `parentNode` will be `li` element
}
function addElementToList(element) {
if (element != "") {
elements.push(element);
} else {
alert("Make sure the input field is not empty!")
}
}
}
<input type="text" id="inputBox">
<br>
<button id="addButton">Add</button>
<br>
<br>
<ul id="myInputList"></ul>