I have a modal in HTML, and I am trying to append some text to its InnerText
attribute, but it seems that one of the newlines gets ignored. For example, I want it to be this:
class Student{
}
class AG{
}
But instead, it is:
class Student{ }
class AG{
}
I tried adding more newlines, and removing some extra ones that could have been a problem, but nothing happened. I tried using the InnterHTML
attribute, but that didn't solve it either.
HTML:
<div id="buttons">
<button id="create-box">Create Class</button>
<button id="myBtn">View Code (C )</button>
</div>
<div id="box-container"></div>
<div id="myModal" >
<div >
<p></p>
</div>
</div>
<script src="main.js"></script>
JS:
var generatedCode = "";
var classNameCode = {};
document.getElementById("create-box").addEventListener("click", function() {
let box = document.createElement("div");
box.classList.add("box");
let title = document.createElement("h2");
title.innerHTML = "Class";
box.appendChild(title);
let classTitle = document.createElement('input');
classTitle.classList.add('class-name-form');
let classTitleBtn = document.createElement('button');
classTitleBtn.innerText = "Set Class Name";
classTitle.classList.add('class-name-btn');
box.appendChild(classTitle);
box.appendChild(classTitleBtn);
document.getElementById("box-container").appendChild(box);
let createSubclassButton = document.createElement("button");
createSubclassButton.innerHTML = "Add Method";
box.appendChild(createSubclassButton);
// Enable resizing and dragging
box.addEventListener("mousedown", startDrag);
box.addEventListener("mousemove", drag);
box.addEventListener("mouseup", endDrag);
box.addEventListener("mouseleave", endDrag);
let offset = [0, 0];
let isDown = false;
function startDrag(e) {
isDown = true;
offset = [
this.offsetLeft - e.clientX,
this.offsetTop - e.clientY
];
}
function drag(e) {
if (!isDown) return;
e.preventDefault();
this.style.left = (e.clientX offset[0]) 'px';
this.style.top = (e.clientY offset[1]) 'px';
}
function endDrag(e) {
isDown = false;
}
createSubclassButton.addEventListener("click", function() {
let subBox = document.createElement("div");
subBox.classList.add("subbox");
let subTitle = document.createElement("h2");
subTitle.innerText = "New Method";
subBox.append(subTitle);
document.getElementById("box-container").appendChild(subBox);
subBox.style.left = box.offsetLeft box.offsetWidth 10 'px';
subBox.style.top = box.offsetTop 'px';
// Enable resizing and dragging for the subbox
subBox.addEventListener("mousedown", startDrag);
subBox.addEventListener("mousemove", drag);
subBox.addEventListener("mouseup", endDrag);
subBox.addEventListener("mouseleave", endDrag);
});
classTitleBtn.addEventListener("click", function() {
console.log(classTitle.value);
// generatedCode = `class ${classTitle.value} {\n\t`
classNameCode[classTitle.value] = `\nclass ${classTitle.value} {\n`
});
});
// MODAL FOR CODE GENERATION
var modal = document.getElementById("myModal");
var btn = document.getElementById("myBtn");
var modalContent = document.querySelector('.modal-content');
btn.onclick = function() {
modalContent.innerText = "";
console.log(classNameCode);
for(var key in classNameCode){
var codeToAppend = `${classNameCode[key]} \n}`;
console.log(codeToAppend);
modalContent.innerText = codeToAppend;
}
modal.style.display = "block";
}
window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = "none";
}
}
CSS:
body {
background-size: 40px 40px;
background-image: radial-gradient(circle, #000000 1px, rgba(0, 0, 0, 0) 1px);
background-color: darkgray;
}
#create-box {
padding: 10px 20px;
background-color: #4CAF50;
border-radius: 5px;
border: none;
color: white;
}
#create-box:hover{
background-color: #5dc861;
cursor: pointer;
}
#create-box:active{
cursor:pointer
}
.box {
position: absolute;
width: 200px;
height: 200px;
background-color: #f1f1f1;
border: 1px solid #d3d3d3;
border-radius: 5px;
resize: both;
overflow: auto;
z-index: 1;
}
.box h2 {
margin: 0;
padding: 10px;
}
.subbox {
width: 100px;
height: 100px;
background-color: #f1f1f1;
border: 1px solid #d3d3d3;
position: absolute;
z-index: 1;
border-radius: 5px;
}
/* The Modal (background) */
.modal {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 1; /* Sit on top */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}
/* Modal Content/Box */
.modal-content {
background-color: #fefefe;
margin: 15% auto; /* 15% from the top and centered */
padding: 20px;
border: 1px solid #888;
width: 80%; /* Could be more or less, depending on screen size */
}
/* The Close Button */
.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
#myBtn{
padding: 10px 20px;
background-color: #4CAF50;
border-radius: 5px;
border: none;
color: white;
}
#myBtn:hover{
background-color: #5dc861;
cursor: pointer;
}
#myBtn:active{
cursor:pointer
}
#buttons{
text-align: center;
}
CodePudding user response:
By default, new lines in HTML are not interpreted as new lines to render on screen. You can either:
- Add
white-space: pre;
property to.modal-content
. This element will now respect carriage returns. - Use
innerHTML
and instead of appending carriage returns (\n
) append a<br/>
tag.
You also probably want to do \r\n
instead of just \n
or it may not work properly on windows browsers.
CodePudding user response:
Solutions
Your btn.onclick
handler should be modified.
Any of the following should work:
- Avoid reading
innerText
while the modal is hidden by collecting your code fragments in a variable and only after the loop set theinnerText
:console.log(classNameCode); let code = ''; for(var key in classNameCode){ var codeToAppend = `${classNameCode[key]} \n}`; console.log(codeToAppend); code = codeToAppend; } modalContent.innerText = code; // avoid reading innerText when modal is hidden modal.style.display = "block";
- Avoid surprising
innerText
read behavior for hidden elements by first making it visible. You can do so by movingmodal.style.display = "block";
to the start of thebtn.onclick
handler function:modal.style.display = "block"; modalContent.innerText = ""; console.log(classNameCode); for(var key in classNameCode){ var codeToAppend = `${classNameCode[key]} \n}`; console.log(codeToAppend); modalContent.innerText = codeToAppend; }
Reason
While writing a value containing a line break to innerText
always creates <br>
elements, reading it from a hidden element behaves differently: it ignores the descendant <br>
elements.
In your btn.onclick
handler, you wrote .innerText = ...
which means reading the current value and the writing in the next step.
In the second iteration of the loop, you are reading the existing innerText
which returns the text without the line break from the first iteration (although there is a <br>
in the DOM). You then write the concatenation of this and the the second codeToAppend
which terminally removes the line break from the first iteration from the modal's contents.
Research
I have copied your code into local files and reduced the example until I found the actual cause:
Having only the style .modal { display: none; }
active, the bad behavior can be observed. Without that style, the new lines are added as desired.
So I've found that the culprit is display: none
which seems to have an effect on how innerText
is working.
Looking at the HTML specification of innerText
seems to confirms this, as it mentions special handling for the case where the element is not being rendered.
In particular, it states:
If this is not being rendered or if the user agent is a non-CSS user agent, then return this's descendant text content.
The descendant text content
is defined as:
The descendant text content of a node node is the concatenation of the data of all the Text node descendants of node, in tree order.
Clearly, this does not include <br>
elements and that's why your innerText
does not contain the line breaks as you desire.
The innerText
specification mentions special handling for <br>
only in the case, the element is being rendered. In particular, it states that for each rendered descendant node, the rendered text collection steps are being executed and those contain:
If node is a
br
element, then append a string containing a single U 000A LF code point to items.