0

Im struggling to write certain parts of my html code to my file in the middle of my script tag.

I have document.Write currently but it does not pass through my validator and need a solution that would. Ive tried creating a text node and appending, and also innerHTML but neither seem to work, any help?

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<!-- 
   New Perspectives on JavaScript, 2nd Edition
   Tutorial 3
   Case Problem 2

   Congressional Election Results
   Author: 
   Date:   

   Filename:         election.htm
   Supporting files: back.jpg, logo.jpg, results.css, votes.js

-->
   <head>
   <title>Congressional Races</title>
   <link href="results.css" rel="stylesheet" type="text/css" />
   <script src="votes.js" type="text/javascript"></script>
   <script src="barchart.js" type="text/javascript"></script>

   <script>
      function totalVotes(votes) {
         var total=0;
         for (var i=0; i<votes.length; i++) total+=votes[i];
         return total;
      }
      function calcPercent(item, sum) {
         return Math.round(100*item/sum);
      }

      function createBar(partyType, percent) {
         switch (partyType) {
            case "D": barText="<td class='dem'> </td>";break;
            case "R": barText="<td class='rep'> </td>";break;
            case "I": barText="<td class='ind'> </td>";break;
            case "G": barText="<td class='green'> </td>";break;
            case "L": barText="<td class='lib'> </td>";break;
         }

         for (var j=1; j <= percent; j++) document.write(barText);
      }

      function showResults(race, name, party, votes) {

         var totalV=totalVotes(votes);

         document.write("<h2>"+race+"</h2>");
         document.write("<table>");
         document.write("<tr><th>Candidate</th><th class='num'>Votes</th><th class='num'>%</th></tr>");

         for (var i=0; i < name.length; i++) {
            document.write("<tr>");
            document.write("<td>"+name[i]+" ("+party[i]+")</td>");
            document.write("<td class='num'>"+votes[i]+"</td>");

            var percent = calcPercent(votes[i],totalV);
            document.write("<td class='num'>("+percent+"%)</td>");
            createBar(party[i], percent);
            document.write("</tr>");
         }

         document.write("</table>");
      }
   </script>
</head>

<body>

   <div id="intro">
      <p><img src="logo.jpg" alt="Election Day Results" /></p>
      <a href="#">Election Home Page</a>
      <a href="#">President</a>
      <a href="#">Senate Races</a>
      <a href="#">Congressional Races</a>
      <a href="#">State Senate</a>
      <a href="#">State House</a>
      <a href="#">Local Races</a>
      <a href="#">Judicial</a>
      <a href="#">Referendums</a>
   </div>

   <div id="results">
      <h1>Congressional Races</h1>
      <script type="text/javascript">
            showResults(race[0],name1,party1,votes1);
            showResults(race[1],name2,party2,votes2);
            showResults(race[2],name3,party3,votes3);
            showResults(race[3],name4,party4,votes4);
            showResults(race[4],name5,party5,votes5);
         </script>

   </div>

</body>
</html>
uhhuh
  • 27
  • 3
  • 1
    Note *New Perspectives on JavaScript, 2nd Edition* is now 10 years old. You would be better off finding a more up to date resource as javascript has moved on a lot in those 10 years – Jon P Sep 04 '19 at 02:10
  • 1
    You say "innerHTML [does not] seem to work", can you elaborate? That is a very vague problem description. – Mr Lister Sep 04 '19 at 08:19

3 Answers3

2

I would suggest you don't use document.write, as each subsequent document.write overwrites the previous one. Instead I suggest you dig into template literals: https://www.youtube.com/watch?v=NgF9-pdTDGs and document.body.innerHTML.

const variableExample = 'Heading content';
document.body.innerHTML = `
<div>
  <h1>${variableExample}</h1>
  <p>I am some content!!</p>
</div>
`;
Devin Olsen
  • 832
  • 1
  • 7
  • 11
1

You've told the parser "this is a XML document, specifically one that follows the rules of XHTML; please treat it as such" by including the boilerplate at the top of the document:

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

But then you switch to an entirely different language that isn't legal XML:

<script>
  function totalVotes(votes) {
    var total=0;
    for (var i=0; i<votes.length; i++) total+=votes[i];

The XML parser hums along (noting that you're missing the required type attribute in your <script> tag) until it sees i<votes.length;. It thinks <votes.length is the start of a tag, gets very confused, and starts throwing out errors left and right. To tell the parser "just ignore this part; it's not for you", add CDATA tags (and comment them out for when your page gets served up as HTML instead of XHTML):

<script type="text/javascript">
  /*<![CDATA[*/
  function totalVotes(votes) {
    var total=0;
    for (var i=0; i<votes.length; i++) total+=votes[i];
    return total;
  }
  /*]]>*/
</script>

(This will validate, but not work, because document.write doesn't work in XML for compliance reasons. That's okay, because you really shouldn't be using document.write to begin with. Just use your innerHTML solution.)


The better way

Dealing with XML and XHTML is a headache, and unless you specifically need to (which you don't), you're better off using modern HTML5. The boilerplate gets a lot shorter:

<!doctype html>

<html>

That's it. No DTDs, no namespaces, no CDATAs. It just works.

AuxTaco
  • 4,883
  • 1
  • 12
  • 27
  • Note that only changing the doctype declaration does not change a HTML file into an XML file or vice versa. Only the file type (MIME type) can change that. Now the validator problems that the OP runs into are because the validator does not see the MIME type, only the content (from which it concludes it's supposed to be XML). – Mr Lister Sep 04 '19 at 08:13
  • @MrLister If you can edit this to be more technically correct, please do. I gave up trying to wrap my head around this stuff seven years ago, and diving back in has been... less than pleasant. – AuxTaco Sep 04 '19 at 10:24
  • Well, everything you said is correct, except that the bit about the parser happens only on the side of the W3C validator. The browsers will treat it as HTML (I'm pretty sure of that, otherwise it wouldn't even display anything!) and they wouldn't have problems with `document.write` - which is deprecated, but still in working order - but the OP's main problem is getting around the validator, and for that your solution works. – Mr Lister Sep 04 '19 at 10:30
0

Unless you are writing a fully fledged javascript web component, keep your HTML as HTML. When you want to change layout it's easier to deal with HTML in an HTML editor when it isn't buried in javascript.

I'm going to use the <template> element. Note that if you need to support IE you will need to use a pollyfill or <script type="text/template>, which requires a little extra work.

function showResults(race, name, party, votes) {
  //Clone the template
  var clone = document.importNode(document.getElementById("templateRace").content, true);
  //To update the clone we need to move it to a temporary item
  var holder = document.createElement('div');
  holder.appendChild(clone);
  //replace the simple data
  holder.innerHTML = holder.innerHTML
    .replace(/{{race}}/g, race);  
  var totalVotes = votes.reduce(function(acc, curr) {
    return acc + curr
  });
  
  //Add Rows
  for (var i = 0; i < name.length; i++) {
    //Clone the row template
    var rowClone = document.importNode(document.getElementById("templateRow").content, true);
    var percent = 100 * votes[i]/totalVotes;
    var partyClasses = {
      "D": "dem",
      "R": "rep",
      "I": "ind",
      "G": "green",
      "L": "lib"
    }
    //To update the clone we need to move it to a temporary item
    var rowHolder = document.createElement('tbody');
    
    rowHolder.appendChild(rowClone);
    //replace the simple data
    rowHolder.innerHTML = rowHolder.innerHTML
      .replace(/{{name}}/g, name[i])
      .replace(/{{party}}/g, party[i])
      .replace(/{{votes}}/g, votes[i])
      .replace(/{{percent}}/g, percent)
      .replace(/{{percWidth}}/g, parseInt(percent * 2, 10))
      .replace(/{{partyClass}}/g, partyClasses[party[i]]);
      
      //Add the row to the tbody
      holder.querySelector("tbody").appendChild(rowHolder.querySelector("tr"));
  }

  //Add the full details
  document.getElementById("res").appendChild(holder.querySelector("section"));
  
}


showResults("Race 1", ["Bill", "Ben", "Brian", "Betty"], ["D", "R", "I", "G"], [15, 25, 10, 50]);
//showResults(race[0],name1,party1,votes1);
/*showResults(race[1],name2,party2,votes2);
showResults(race[2],name3,party3,votes3);
showResults(race[3],name4,party4,votes4);
showResults(race[4],name5,party5,votes5);*/
.dem {background-color: red;}
.rep {background-color: blue;}
.ind {background-color: yellow;}
.green {background-color: green;}
.lib {background-color: black;}
.party {display:inline-block;}
<body>

  <div id="intro">
    <p><img src="logo.jpg" alt="Election Day Results" /></p>
    <a href="#">Election Home Page</a>
    <a href="#">President</a>
    <a href="#">Senate Races</a>
    <a href="#">Congressional Races</a>
    <a href="#">State Senate</a>
    <a href="#">State House</a>
    <a href="#">Local Races</a>
    <a href="#">Judicial</a>
    <a href="#">Referendums</a>
  </div>

  <div id="results">
    <h1>Congressional Races</h1>
    <div id="res">
    </div>

    <template id="templateRace">
        <section>
        <h2>{{race}}</h2>
        <table>
          <thead>
            <tr>
            <th>Candidate</th><th class='num'>Votes</th><th class='num'>%</th>
            </tr>
          </thead>
          <tbody>            
          </tbody>
        </table>
        </section>
      </template>
    <template id="templateRow">      
        <tr>
          <td>{{name}} ({{party}})</td>
          <td class="num">{{votes}}</td>
          <td class="num">{{percent}}</td>
          <td class="party {{partyClass}}"><div style="width:{{percWidth}}px; height:100%"> </div> </td>
        </tr>
      </template>


  </div>
Jon P
  • 19,442
  • 8
  • 49
  • 72