Implanting string into html page (Challenging) - javascript

I am attempting to create a method which achieves the following:
Implanting a string (For example: "TEST") into the body of an HTML page, every certain number of words (10 for instance). This problem is complicated by the fact that the word count must carry over between elements.
For example, the following HTML:
<body>
<h1>My Day at the Park</h1>
<p>I had a great day at the park today.</p>
<p>It was very fun, and I would like to go again soon.</p>
</body>
Should become:
<body>
<h1>My Day at the Park</h1>
<p>I had a great dayTEST at the park today.</p>
<p>It was very fun, and ITEST would like to go again soon.</p>
</body>
Accordingly, "TEST" was inserted after every 10th word ("day" and "I"). The first counted word is "My" and the last is "soon.".
A similar question has been asked in the past, though that question does not address the vital issue of continuity between elements (as mentioned in the comments by Oskar Lindberg).
My (failed) approach to solving this problem was by the following process:
1. Using jQuery $('body').text() to obtain all text in body. 2. Using .split(' ') to create array of all words. 3. Counting and recording every 10th word in a new array: cutoffWords[]. 4. Counting the number of times each cutoff word occurs before the occurance in which it is a cutoff word (10th word). 5. Running through $('body').html, and injecting the string after the cuttoff word on its cutoff occurance.
I can post the actual code, which is long, if requested, but this method suffers from an ihereint flaw, in that when running through $('body').html, text inside element tags are also counted, so that it can result in the misplacement of the string (or placement inside of an element tag).
As a novice JavaScript programmer I am at my whit's end, and will appreciate any advice.

I think the previous answer from Gerardo BLANCO is almost perfect. If you want to have some more control over the elements that are really handled (e.g., if a <script>-tag would appear in your code, it would also be counted) and if you want to also support nested elements, the following may help.
var validTypes = ['h1', 'p', 'div', 'span'];
var pos = 10;
var addedText = 'TEST';
var counter = 0;
var splitter = function ($el) {
$el.contents().each(function () {
var $innerEl = $(this);
if (this.nodeType === Node.TEXT_NODE) {
var text = $innerEl.text().trim();
if (text !== '') {
var content = text.replace(/ +/g, ' ').split(' ');
var newText = '';
for (var i = 0; i < content.length; i++) {
newText += ' ' + content[i];
if (++counter === pos) {
newText += addedText;
counter = 0;
}
}
this.nodeValue = newText;
}
} else if (this.nodeType === Node.ELEMENT_NODE &&
validTypes.indexOf(this.tagName.toLowerCase()) > -1) {
splitter($innerEl);
}
})
};
splitter($('body'));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div>
<h1>My Day at the Park</h1>
<script>// do nothing here just ignore me</script>
<p>I had a great day at <span style='color:red'>the</span> park today.</p>
<p>It was very fun, and I would like to go again soon.</p>
</div>
</body>

Awesome challenge
Ok lets start,
First of all i need to insert everything in a div because the scipts counted as the body children. If this doesnt work for you, we can make a work around.
I made a var current this var will keep count of how many words we have gone through
I get all the childrens div and loop between them. Then i split the childrens word and count them
If the count + current is bigger than 10, then we need to add an element.
If not, then current gets the count added
Hope this helps :)
let current = 0;
$('div').children().each(function() {
let words = $(this).text().split(" ");
if (current + words.length < 10) {
current += words.length;
} else {
let diff = 10 - current;
words[diff - 1] = words[diff - 1].concat("TEST");
this.innerHTML = words.join(" ");
current = words.length - diff;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div>
<h1>My Day at the Park</h1>
<p>I had a great day at the park today.</p>
<p>It was very fun, and I would like to go again soon.</p>
</div>
</body>

Watch and Learn
//<![CDATA[
/* external.js */
var doc, bod, M, I, S, Q, addTextAfterEvery, old = onload; // for use on other loads
onload = function(){
if(old)old(); // change old var name if using technique on other pages
doc = document; bod = doc.body;
M = function(tag){
return doc.createElement(tag);
}
I = function(id){
return doc.getElementById(id);
}
S = function(selector, within){
var w = within || doc;
return w.querySelector(selector);
}
Q = function(selector, within){
var w = within || doc;
return w.querySelectorAll(selector);
}
addTextAfterEvery = function(text, wordCount, withinSelector){
var wi = withinSelector || 'body';
var all = Q(wi+' *');
for(var i=0,e,aa=[],l=all.length; i<l; i++){
e = all[i]; aa.push(e.innerHTML.split(/(?!:\S)\s+(?!:\S)/));
}
for(var i=0,w=0,a,r=[],l=aa.length; i<l; i++){
a = aa[i];
for(var n=0,q=a.length; n<q; n++){
if(++w%wordCount === 0)a[n] += text;
}
r.push(a.join(' '));
}
for(var i=0; i<l; i++){
all[i].innerHTML = r[i];
}
}
addTextAfterEvery('TEST', 10, '.main');
}
//]]>
/* external.css */
html,body{
padding:0; margin:0;
}
body{
background:#000; overflow-y:scroll;
}
.main{
width:940px; background:#ccc; padding:20px; margin:0 auto;
}
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>
<head>
<meta http-equiv='content-type' content='text/html;charset=utf-8' />
<meta name='viewport' content='width=device-width' />
<title>addTextAfterEvery</title>
<link type='text/css' rel='stylesheet' href='external.css' />
<script type='text/javascript' src='external.js'></script>
</head>
<body>
<div class='main'>
<h1>My Day at the Park</h1>
<p>I had a great day at the park today.</p>
<p>It was very fun, and I would like to go again soon.</p>
</div>
</body>
</html>

Related

I want to replace . with <br>tag using javascript

I've tried this
<html>
<head>
<title>None</title>
</head>
<body>
<p id="text">just some random text. random text</p>
<button type="button" onclick="strReplace();">Replace</button>
<script>
function strReplace(){
var myStr = document.getElementById("text");
var mySte = myStr.textContent;
console.log(mySte);
</script>
</body>
and I want this following outcome
just some random text
random text
You can use this regex to find and replace string without breaking html: /(?!<[^>]+)\.(?![^<]+>)/g
[myattr]
{
background-color: pink;
}
[myattr]:after
{
content: "this element's attribute is: " attr(myattr);
background-color: lightgreen;
margin: 1em;
}
p > span
{
background-color: lightblue;
}
<html>
<head>
<title>None</title>
</head>
<body>
<p id="text">just some. random text. <span myattr="text.with.dots">nested.html</span> end.
<span>i < 40. But i is also > 30. What are valid values of i?</span>
</p>
<button type="button" onclick="strReplace();">Replace</button>
<script>
function strReplace(){
var myStr = document.getElementById("text");
myStr.innerHTML = myStr.innerHTML.replace(/(?!<[^>]+)\.(?![^<]+>)/g, "<br>");
console.log(myStr.innerHTML);
}
</script>
</body>
To directly answer the question:
var outputHtml = inputText.replace(/\./g, '<br />');
The Javascript replace method will by default replace the first instance of something (the first . in this case), so the g regex modifier tells it to replace them all. The \. in the regex is because . is a special character in regexes. This will replace every dot in the text.
What about ellipsis?
This technique won't work well on text that contains literal ellipsis, like this:
Hello world...
If your text is likely to contain this, and you want that to be ignored, then the following regex is more appropriate:
/[^\.](\.)[^\.]/g
This'll match any . which is not surrounded by other .
var outputHtml = inputText.replace(/[^\.](\.)[^\.]/g, '<br />');
Handling HTML
In the question here, the input is actually coming from the DOM. We can't replace . on its innerHTML as it would replace content inside HTML tags as well. So, if your input text is coming from the DOM like this (and not, say, a textarea), then the safest route is to apply the replacement only to text nodes. That is done like this:
document.getElementById("start").addEventListener("click", () => {
var inputNode = document.getElementById("text");
replace(inputNode);
});
function replace(node) {
if(!node) {
return;
}
if (node.nodeName == '#text') {
// We've found a text node. Apply the regex to it now.
// Note that you can use either of the above regexes here.
var inputText = node.textContent;
var lines = inputText.split(/\./g);
if(lines.length > 1) {
// It had at least one dot in it.
// Swap in this new set, each with a <br /> between them.
var parent = node.parentNode;
var nextSibling = node.nextSibling;
parent.removeChild(node);
lines.forEach((line, i) => {
if(i != 0){
// insert the <br>
var br = document.createElement('br');
parent.insertBefore(br, nextSibling);
}
var textNode = document.createTextNode(line);
parent.insertBefore(textNode, nextSibling);
});
}
} else {
// Loop through each child node.
// We go backwards such that completed replacements don't affect the loop.
for(var i=node.childNodes.length - 1; i>=0; i--) {
replace(node.childNodes[i]);
}
}
}
<html>
<head>
<title>None</title>
</head>
<body>
<p id="text">just some random text. random text</p>
<button type="button" id="start">Replace</button>
</body>
</html>
If you're starting from a HTML string inside a browser, you can then use the above replace method and the browsers internal parsing to safely only affect the actual text:
function replaceHtmlString(html) {
var div = document.createElement('div');
div.innerHTML = html;
replace(div);
return div.innerHTML;
}

Multiple Pages in one HTML file/page, with more than 2 pages

I know that the <script> element can have function show(shown, hidden) on it. but with the 2 pages ({document.getElementById(shown).style.display='block'; document.getElementById(hidden).style.display='none'; return false;) in that, I can't figure out how to make that page count more. Any help?
P.S. I am open to almost anything. I can't guarantee your answers will help, but I might be able to figure it out using your suggestions.
I have tried more things on the function show(shown, hidden, hidden, hidden) but that does not help.
I am stuck. I have researched anything I could find. I can't figure it out.
Please help me.
My specific code I want suggestions on is this:
<script>
function show(shown, hidden) {
document.getElementById(shown).style.display='block';
document.getElementById(hidden).style.display='none';
return false;
}
</script>
with some <div>s.
I know this is probably not helping you figure out how to help me, but I need to know. (I hate full-on JavaScript!)
<!doctype html>
<html lang="en">
<head>
<title>Multi but Single Page</title>
<meta charset="utf-8">
<style>
.templates {
display: none;
}
</style>
<script>
// we save all templates in an global Variable
var templateStack = [];
// https://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
function getParameterByName(name, url) {
url = url || window.location.href;
name = name.replace(/[\[\]]/g, '\\$&');
var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, ' '));
}
window.addEventListener('load', function (e) {
// get all hidden template elements
var templates = document.getElementsByClassName('templates');
for (var i = 0, v; v = templates[i]; i++) {
// each Child Node is a new Page
for (var j = 0, x; x = v.childNodes[j]; j++) {
// at least if it's an element
if (x.nodeType === Node.ELEMENT_NODE) {
templateStack.push(x);
}
}
}
// uri support ?page=1 loads Page 2 and ?page=0 loads Page 1, default is 0
var pageIndex = getParameterByName('page') || '0';
// so we can test it with a Browser by just insert 'loadPage(1)'
loadPage(pageIndex);
});
function loadPage(index) {
// only valid indexes
if (index >= templateStack.length || index < 0) {
document.body.innerText = '404 Page not found';
return;
}
// clean everything in our page
document.body.innerHTML = '';
// append our fetched Page out of our Global Variable
document.body.appendChild(templateStack[index]);
}
</script>
</head>
<body>
<div class="templates">
<div>
<h3>Page 1</h3>
<p>
Welcome to Page 1
</p>
Load Page 2
</div>
<div>
<h1>Page 2</h1>
<p>
Our Page 2
</p>
Back to Page 1
</div>
</div>
</body>
</html>
I understand that you can use it with 2 pages but when you want to make more pages like 4-5 pages?
First you need an clear function (it will hide all the pages)
In the clear function get the body in dom and get all the childrens then make a foreach loop hiding all of them
Second you need an show function which will use the page as an parameter like "show('page1');" it will first call the clear function and then show the page1

how to reverse the order of words of one language using css when there are 2 languages used?

I create a website with the possibility of using either English or Arabic language. When the language is Arabic, I need some words to remain English.
For Arabic, I use
direction: rtl;
As an example for the following sentence,
مرحبا: Prabhashi (NEW) أهلا بك
the result by rtl is as follows,
But I need the result to be,
أهلا بك (NEW) Prabhashi :مرحبا
Please help me with this.
Here is a way to solve your problem. It iterates over the string, adds all the words in the strings separated by a " " into an array and then reverses it
function reverse_sentence(str){
str += " ";
var words = [];
var last_break = 0,word_length = 0;
for(var i = 0; i < str.length; i++){
if(str[i] == " "){
var word = str.substr(last_break, word_length);
words.push(word);
last_break = i + 1;
word_length = 0;
}else{
word_length++;
}
}
return words.reverse();
}
alert(reverse_sentence("Hi my name is Albin"));
I think as per the link: https://www.w3.org/TR/WCAG20-TECHS/H34.html
You will have to add this character after your brackets to tell the browser that it should be LTR. Like the example below:
<!DOCTYPE html>
<head>
<meta charset="unicode">
</head>
<body>
<div dir="rtl">
مرحبا: Prabhashi (NEW)‎ أهلا بك
</div>
</body>
</html>
I think this will be the simplest answer for your problem.

Split string and in some cases remove

I've got a product title which I'm splitting and inserting a linebreak using javascript like this:
<script>
function myFunction() {
var str = "How are you - doing today?";
var res = str.split("-").join('<br>');
document.getElementById("demo").innerHTML = res;
}
</script>
This works for most case scenarios, however in some cases I will need to remove the second line completely. So everything after the - will need to be removed. Only within that element though, so if I've got this for example
<h3>This is a product - title</h3>
the result should be
<h3>This is a product</h3>
Again this only needs to apply to elements with a certain class. Anybody got any idea ow to do this?
Why not us a simple replace,
string = string.replace(/-/g, '<br>');
or for complete deletion, take
string = string.replace(/-.*$/g, '');
Check className of the element:
function myFunction() {
const str = `How are you - doing today?`
const first = str.split(`-`)[0]
const all = str.split(`-`).join(`<br/>`)
const el = document.getElementById(`demo`)
const el.innerHTML = el.className === `any-name` ? first : all
}
Try this:
(function() {
// For splitted titles
var split = document.querySelectorAll(".dash-split");
var splits = [];
split.forEach(function(spl) {
splits.push(spl.innerHTML.split("-").join("<br>"));
});
console.log(splits); // Outputs ["This is <br> split!"]
// For removed titles
var removedEls = document.querySelectorAll(".dash-split");
var removed = [];
removedEls.forEach(function(rem) {
removed.push(rem.innerText.split("-")[0].trim());
});
console.log(removed); // Outputs ["This is"]
})();
<!DOCTYPE html>
<html>
<head>
<title>Welcome!</title>
</head>
<body>
<div>
<h1 class="dash-split">This is - split!</h1>
<h1 class="dash-remove">This is - removed!</h1>
</div>
</body>
</html>
This should get you what you want, provided the script runs at the end of the document. For wrapping, it keys off of the class title-wrap.
<html>
<head></head>
<body>
<h3>This is a product - title</h3>
<h3 class="title title-wrap">This is a product - with a wrapped title</h3>
<h3>This is a product - with another title</h3>
<script>
(function() {
var titles = document.querySelectorAll('h3');
titles.forEach(function(o, i) {
var title = o.innerHTML;
if (/title-wrap/ig.test(o.className)) {
o.innerHTML = title.replace(/-/g, '<br />');
} else {
o.innerHTML = title.replace(/-.*$/g, '');
}
});
})();
</script>
</body>
</html>

how to generate words onclick?

I am trying to create an html page that contains a title, and when we click on the title it generates words underneath it. But the code I have is only working for the first click, and it's also deleting the title. So my question is, how can I make it generate words under the title without deleting it?
<!DOCTYPE html>
<html>
<head>
<title>function JavaScript</title>
<script>
var k = 0;
function bla(){
var ph = ["red ","blue","black","green","yellow"];
if(k <= ph.length ){
document.write(ph[k]);
k++;
}
}
</script>
</head>
<body>
<h1 onclick="bla();">Click here</h1>
</body>
</html>
Any document.write statement that runs after the page finishes loading will create a new page and overwrite all of the content of the current page. This is almost certainly not what you intend to have happen. You should therefore avoid using document.write in situations such as this
so try add a element and then write into them like this
<h1 onclick="bla();">Click here</h1>
<span id="test"></span>
JS:
if(k <= ph.length ){
//document.write(ph[k]);
document.getElementById("test").innerText+=" "+ ph[k];;
k++;
}
JS Fiddle Example
This may be what you need:
var k = 0;
function bla() {
var ph = ["red", "blue", "black", "green", "yellow"];
if (k < ph.length) {
var p = document.createElement('p');
p.innerHTML = ph[k];
document.body.appendChild(p);
k++;
}
}
http://jsfiddle.net/p5rLX/
It will write each word into its own paragraph.

Categories

Resources