document.write, Array.push and putting strings together - javascript

I want my code to be able to replace the current HTML page seen by the user via document.write(). And it does replace the HTML page, it just doesn't show the text that I want it to show. If I use my code to add an element via the addElement() function,
function addElement(a, b) {
var startTag, tagValue, endTag, finalTag;
// htmlify
if (b === undefined) {
startTag = "<p>";
endTag = "</p>";
} else {
startTag = "<" + b + ">";
endTag = "</" + b + ">";
}
finalTag = startTag + tagValue + endTag;
eio.push(finalTag);
};
it takes the parameters that the user has put in to create a valid tag, for example
addElement("This is a valid h1 tag.", "h1");
It then parses that into a HTML tag (variables filled out here),
var finalTag = "<" + "h1" + ">" + "This is a valid h1 tag." + "</" + "h1" + ">"
and pushes it into an array called eio (variables filled out again).
eio.push("<h1>This is a valid h1 tag.</h1>");
This array is not used until displayElements() is called, where I used a for loop to put all the different strings together. Here's how I did it:
function displayElements() {
finalString = "<!DOCTYPE html><html><head><title>Hello World!</title></head><body>";
for (i = 0; i === eio.length; i++) {
finalString += eio[i];
}
finalString += "</body></html>";
document.write(finalString);
};
Using our example again, finalString should look like this
finalString = "<!DOCTYPE html><html><head><title>Hello World!</title></head><body><h1>This is a valid h1 tag.</h1></body></html>"
when it we use it in the document.write() function. This should leave us with an HTML page where it says "This is a valid h1 tag." in big bold font, yet it only leaves us with a blank screen. One interesting thing however, is that the title does get updated when we call the function, but the page is still blank.
You can see the entire code here, and go to the example website here.

Two problems were already pointed out by sabithpocker and me. I will sum those up to make this answer.
Your addElement method takes an argument a and does nothing with it. Instead you are using a local variable tagValue. You could fix it by assigning the value of a to tagValue.
function addElement(a, b) {
var startTag, endTag, finalTag;
var tagValue = a;
// htmlify
if (b === undefined) {
startTag = "<p>";
endTag = "</p>";
} else {
startTag = "<" + b + ">";
endTag = "</" + b + ">";
}
finalTag = startTag + tagValue + endTag;
eio.push(finalTag);
};
The second problem is a typo in the for-loop of your displayElements method. It should be
for (i = 0; i < eio.length; i++) {
finalString += eio[i];
}

Related

JavaScript: Code says "cannot read property 'indexOf' of undefined" but I can't fix the problem

The code is used in a HTML document, where when you press a button the first word in every sentence gets marked in bold
This is my code:
var i = 0;
while(i < restOftext.length) {
if (text[i] === ".") {
var space = text.indexOf(" ", i + 2);
var tekststykke = text.slice(i + 2, space);
var text = text.slice(0, i) + "<b>" + tekststykke + "</b>" + text.slice(i + (tekststykke.length + 2));
var period = text.replace(/<b>/g, ". <b>");
var text2 = "<b>" + firstWord + "</b>" + period.slice(space1);
i++
}
}
document.getElementById("firstWordBold").innerHTML = text2;
}
It's in the first part of the code under function firstWordBold(); where it says there is an error with
var space1 = text.indexOf(" ");
Looks like you're missing a closing quote on your string, at least in the example you provided in the question.
Your problem is the scope of the text variable. In firstWordBold change every text to this.text, except the last two where you re-define text
Also, if you want to apply bold to the first word this is easier...
document.getElementById('test-div-2').innerHTML = '<b>' + firstWord + '</b>' + restOftext;
It now works for me, with no errors and it applies bold to the first word.
Here's how the function ended up,
function firstWordBold() {
console.log('bolding!');
var space1 = this.text.indexOf(' ');
var firstWord = this.text.slice(0, space1);
var restOftext = this.text.slice(space1);
document.getElementById('test-div-2').innerHTML = '<b>' + firstWord + '</b>' + restOftext;
}
To make every first word bold, try this...
function firstWordBold() {
let newHTML = '';
const sentences = this.text.split('.');
for (let sentence of sentences) {
sentence = sentence.trim();
var space1 = sentence.indexOf(' ');
var firstWord = sentence.slice(0, space1);
var restOftext = sentence.slice(space1);
newHTML += '<b>' + firstWord + '</b>' + restOftext + ' ';
}
document.getElementById('test-div-2').innerHTML = newHTML;
}
One last edit, I didn't notice you had sentences ending with anything other that a period before. To split on multiple delimiters use a regex, like so,
const sentences = this.text.split(/(?<=[.?!])\s/);

InnerHTML doesn't work - Advanced function

I'm beginner in JS. But, after many hours, i'm really close to the wanted result.
I declare my JS Function in head part
function getPrice(price) {
var tabPrice = price.split("");
var html = "";
var virguleIndex = null;
for (var index = 0; index < tabPrice.length; ++index) {
var priceNumber = tabPrice[index];
if (priceNumber == ',') {
virguleIndex = index;
html += "<span class='p-c'>" + priceNumber + "</span>";
} else if (priceNumber == '-') {
html += "<span class='p-d'>" + priceNumber + "</span>";
} else if (index > virguleIndex && virguleIndex != null) {
html += "<span class='p-" + priceNumber + " p-small'>" + priceNumber + "</span>";
} else {
html += "<span class='p-" + priceNumber + "'>" + priceNumber + "</span>";
}
}
var div = document.getElementsByClassName('price');
div[0].innerHTML = html;
}
and my div in body part
<div class="price"></div>
I made some test - And my function getPrice works perflectly
https://image.noelshack.com/fichiers/2018/02/4/1515663887-functionwork.jpg
Some, the only fail (I think) is that the innerHTML don't work and don't write de var html content in div class price.
I haven't idea yet after many (many) hours of looking.
Can you help me ?
Thanks in advance,
Ludovic
By going through your image it is clear your DOM is not ready. So Please call your function inside this Block.
(function() {
// your page initialization code here
// the DOM will be available here
//Call Your function inside this block. it will work ex. getPrice("100");
})();
Some news (I worked on this few hours), I check for the dom charging. I tried (thank for you answer) the call function / transpose the code and the end / forcing the function after the dom loading (with document.addEventListener("DOMContentLoaded", function(event) {)
Console log still working. But my div still empty :/
Thanks again !

Bad html formatting with jQuery.html()

I have something that I can't understand and i'm struggling with that for 2 days.
For the story, I'm using VICOPO api to get zipcode/city (France only I think).
The thing is that the code I'm generating is not really good interpreted by jQuery (or maybe I'm doing it wrong)
Here is the code:
$('#postcode').val($('#postcode').val().toUpperCase());
if ($('#postcode').val().length == 5)
{
var $ville = $('#postcode');
$.vicopo($ville.val(), function (input, cities) {
if(input == $ville.val() && cities[0]) {
if (cities.length == 1)
$('#city').val(cities[0].city);
else
{
var html = '';
html += '<div style=\'text-align:center\'>';
for (var i=0; i<cities.length; i++)
{
var v = cities[i].city;
// --- HERE IS MY PROBLEM ---
html += '<p onclick=\'alert(\'' + v + '\');\'>' + v + '</p>';
}
html += '</div>';
console.log(html);
$('#multi_ville').html(html);
}
}
});
When I inspect the elements in the multi_div this is what I get:
<p onclick="alert(" billey');'>BILLEY</p>
<p onclick="alert(" flagey-les-auxonne');'>FLAGEY-LES-AUXONNE</p>
etc ....
And when I inspect the console log, all looks correct:
<p onclick='alert('BILLEY');'>BILLEY</p>
<p onclick='alert('FLAGEY-LES-AUXONNE');'>FLAGEY-LES-AUXONNE</p>
<p onclick='alert('VILLERS-LES-POTS');'>VILLERS-LES-POTS</p>
etc ....
If someone have an idea or what I'm doing wrong, it would cool.
(may I mention, this code is in a smarty tpl file surrounded with the {literal} tag)
Try to create self closed tags via jquery and then append them to #multi_ville, here is an example:
// create div element
var div = $('<div/>', {
'style' : 'text-align:center'
});
for (var i=0; i<cities.length; i++)
{
var v = cities[i].city;
// create p element with click event and then append it to div
$('<p/>').on('click', function() {
alert(v);
}).text(v).appendTo(div);
}
$('#multi_ville').append(div);
EDIT It seems that my code above always alert the last city when we click on a element, that's because alert takes the value that v variable has at the time it runs, to solve this we can use let statement:
let v = cities[i].city;
Or a function:
for (var i=0; i<cities.length; i++) {
var v = cities[i];
createPTag(v, div);
}
function createPTag(v, div) {
$('<p/>').on('click', function() {
alert(v);
}).text(v).appendTo(div);
}
Instead of
html += '<p onclick=\'alert(\'' + v + '\');\'>' + v + '</p>';
try this:
html += '<p onclick="alert(\'' + v + '\');">' + v + '</p>';
Here's some info on when and how to use double/single quotes.
EDIT:
Also, check the else on this if statement:
if (cities.length == 1)
You need a closing curly bracket (}) to close in the else. It should be added directly after this line:
$('#multi_ville').html(html);

Creating and deleting divs using javascript

I have a few JavaScript functions designed to add and remove HTML divs to a larger div. The function init is the body's onload. New lines are added when an outside button calls NewLine(). Divs are removed when buttons inside said divs call DeleteLine(). There are a few problems with the code though: when I add a new line, the color values of all the other lines are cleared, and when deleting lines, the ids of the buttons, titles, and line boxes go out of sync. I've gone through it with the Chrome debugger a few times, but each time I fix something it seems to cause a new problem. I would greatly appreciate some input on what I'm doing wrong.
function init()
{
numOfLines = 0; //Keeps track of the number of lines the Artulator is displaying
}
function NewLine()
{
var LineBoxHolder = document.getElementById("LineBoxHolder");
numOfLines += 1;
LineBoxCode += "<div class = 'Line Box' id = 'LineBox" + numOfLines + "'>" //The code is only split onto multiple lines to look better
+ " <h6 id = 'Title " + numOfLines + "' class = 'Line Box Title'>Line " + numOfLines + "</h6>";
+ " <p>Color: <input type = 'color' value = '#000000'></p>"
+ " <input type = 'button' value = 'Delete Line' id = 'DeleteLine" + numOfLines + "' onclick = 'DeleteLine(" + numOfLines + ")'/>"
+ "</div>";
LineBoxHolder.innerHTML += LineBoxCode;
}
function DeleteLine(num)
{
deletedLineName = "LineBox" + num;
deletedLine = document.getElementById(deletedLineName);
deletedLine.parentNode.removeChild(deletedLine);
num++;
for ( ; num < numOfLines + 1 ; )
{
num++;
var newNum = num - 1;
var changedLineName = "LineBox" + num;
var changedHeaderName = "Title" + num;
var changedButtonName = "DeleteLine" + num;
var changedButtonOC = "DeleteLine(" + newNum + ")";
var changedLine = document.getElementById(changedLineName);
var changedHeader = document.getElementById(changedHeaderName);
var changedButton = document.getElementById(changedButtonName);
var changedLine.id = "LineBox" + newNum;
var changedHeader.innerHTML = "Line" + newNum;
var changedHeader.id = "Title" + newNum;
var changedButton.setAttribute("onclick",changedButtonOC);
var changedButton.id = "DeleteLine" + newNum;
}
num--;
numOfLines = num;
}
You are having a hard time debugging your code because of your approach. You are "marking" various elements with the IDs you construct, and using the IDs to find and address elements. That means that when things change, such as line being deleted, you have to go back and fix up the markings. Almost by definition, the complicated code you wrote to do something like that is going to have bugs. Even if you had great debugging skills, you'd spend some time working through those bugs.
Do not over-use IDs as a poor-man's way to identify DOM elements. Doing it that way requires constructing the ID when you create the element and constructing more IDs for the sub-elements. Then to find the element again, you have to construct another ID string and do getElementById. Instead, use JavaScript to manage the DOM. Instead of passing around IDs and parts of IDs like numbers, pass around the DOM elements themselves. In your case, you don't need IDs at all.
Let's start off with DeleteLine. Instead of passing it a number, pass it the element itself, which you can do my fixing the code inside your big DOM string to be as follows:
<input type='button' value='Delete Line' onclick="DeleteLine(this.parentNode)"/>
So we have no ID for the line element, no ID for the element, and no ID within the onclick handler. DeleteLine itself can now simply be
function DeleteLine(line) {
{
line.parentNode.removeChild(line);
renumberLines();
}
We'll show renumberLines later. There is no need to adjust IDs, rewrite existing elements, or anything else.
Since we no longer need the ID on each line or its sub-elements, the code to create each element becomes much simpler:
function NewLine()
{
var LineBoxHolder = document.getElementById("LineBoxHolder");
numOfLines += 1;
var LineBoxCode = "<div class='LineBox'>" +
+ " <h6 class='LineBoxTitle'>Line " + "numOfLines + "</h6>"
+ " <p>Color: <input type='color' value='#000000'></p>"
+ " <input type='button' value='Delete Line' onclick= 'DeleteLine(this.parentNode)'/>"
+ "</div>";
LineBoxHolder.innerHTML += LineBoxCode;
}
The only remaining work is to fix up the titles to show the correct numbers. You can do this by just looping through the lines, as in
function renumberLines() {
var LineBoxHolder = document.getElementById("LineBoxHolder");
var lines = LineBoxHolder.childElements;
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
var h6 = line.querySelector('h6');
h6.textContent= "Line " + (i+1);
}
}
I voted to close because the question is too broad, but will answer anyway on a few points to... well, point in the right direction.
var changedButton.setAttribute("onclick",changedButtonOC); This is not a variable declaration. Omit the var.
for ( ; num < numOfLines + 1 ; ) { num++; ... The correct form here would be simply for (; num < numOfLines + 1; num++) { ....
Instead of incrementing (num++) then decrementing (num--) around the loop, why not just use the right math?
See:
for (; num < numOfLines; num++) {
...
}

Javascript attach new property to element

I have an object, X, and some code that creates a div and assigns id = X.ID. After the html is created, I assign the object to the div, like this:
document.getElementById(X.ID).XValue = X;
If I set a break after that statement, I can evaulate document.getElementById(X.ID).XValue and see all the properties of X.
While I was creating the html, I added onmouseup="MOUSE_UP(event)".
var aProp = {};
aProp.ThisValue = "This";
aProp.ThatValue = "That";
aProp.Id = 5;
var html = '<div id="' + aProp.Id + '"';
var func = 'MOUSE_UP';
html += ' onmouseup="' + func + '(event) ">';
html += '</div>';
document.getElementById("test").innerHTML += html;
document.getElementById(aProp.Id).XVALUE = aProp;
function MOUSE_UP(event) {
alert(event.currentTarget.XValue.ThisValue);
}
Now, when I set a break at MOUSE_UP, event.currentTarget is my div (event.currentTarget.id == X.ID), but event.currentTarget.XValue is undefined.
Why is XValue undefined here when it was defined earlier?
Looks like setting innerHTML of #test would wipe out all custom properties from its children. You can check this in the jsFiddle. When you'll run the fiddle as it is, you'll notice NewProp of #1 will become undefined after adding more content with test.innerHTML += ... If you log tabIndex instead of NewProp, you'll get the correct values.
This happens because += operator is just a shortcut for a statement like a = a + b, which can also be written a += b.
Basicly you create a string from the inner HTML of #test, then add another string to it, and finally replace the original innerHTML of #test with this new string. All previous elements in #test are replaced with new ones, which don't have the custom properties set.
When setting id property for an element, also id attribute is added to the HTML, hence they are a part of innerHTML of #test, and are added to the newly created HTML too.
If you use proper DOM manipulation instead of setting innerHTML, you'll get the results you want. The code below uses createElement() and appendChild() methods instead of setting innerHTML.
function myMouseUp(e) {
alert("at MouseUp " + e.currentTarget.NewProp.ThisValue);
}
function buildOneDiv(aProp) {
var html = document.createElement('div');
aProp.ThisValue = 'This is ' + aProp.id;
aProp.ThatValue = 'That is ' + aProp.id;
html.id = aProp.id;
html.addEventListener('mouseup', myMouseUp, false);
html.innerHTML = 'Test ' + aProp.id;
return html;
}
function buildDivs(x) {
var html = buildOneDiv(x);
document.getElementById("test").appendChild(html);
document.getElementById(x.id).NewProp = x;
}
window.onload = function () {
var aProp, i;
for (i = 1; i < 4; i++) {
aProp = {};
aProp.id = i;
buildDivs(aProp);
}
};
A live demo at jsFiddle.
This is not so much an answer as it is a clarification and a work-around.
Given this html
<div id="test"></div>
and this code
function myMouseUp(e) {
alert("at MouseUp " + e.currentTarget.NewProp.ThisValue);
}
function buildOneDiv(aProp) {
aProp.ThisValue = "This";
aProp.ThatValue = "That";
var html = '<div id="' + aProp.id + '"';
var func = 'myMouseUp';
html += ' onmouseup="' + func + '(event) ">';
html += 'Test ' + aProp.id + '</div>';
return html;
}
function buildDivs(x) {
var html = buildOneDiv(x);
document.getElementById("test").innerHTML += html;
document.getElementById( x.id ).NewProp = x;
}
window.onload = function () {
for (var i = 1; i < 4; i++) {
var aProp = {};
aProp.id = i;
buildDivs(aProp);
}
};
The end result is that only the LAST div whose onmouseup is defined will have a legitimate value for NewProp at myMouseUp. For each other div, this property is undefined. This is why I got some comments indicating that "It does work." It works for ONE, which is all I had in my example. (This is the clarification.)
My workaround is to add a global object to be an associative array and change two statements:
var myDivs = {}; // global
Replace
document.getElementById( x.id ).NewProp = x;
in buildDivs with
myDivs[x.id] = x;
and replace
alert("at MouseUp " + e.currentTarget.NewProp.ThisValue);
in myMouseUp with
alert(myDivs[e.currentTarget.id].ThisValue );.
I'd still like to know why the original approach doesn't work.

Categories