2

I want to append a paragraph to the page and add each word of the paragraph a specific id (I'm wrapping each word in a span with an id so that we can control the color of each word later)

The code works fine but there is an issue I can't fix without a hand.

I want a max font size of 5vw and I want to decrease the font size in order to fit the whole paragraph inside the paragraphContainer.

I tried my best but unfortunately, it wasn't enough!

Using let fontSize = getFontSize(paragraphOriginal); I tried to get the height of the multiline appended text but I think it just takes the height of only a single line! If I could get the height of the multiline...

Here is the code:

// our paragraph
let paragraphOriginal = "During a discussion that popped up over this, Adam pointed out there is a new CSS property that is (as I understand it) specifically for this. This removes the need for three elements. Technically you only need one, the inline element, but it's likely you'll be doing this on a header so you'll probably end up with a block-parent anyway, which is best for spacing. LAST WORDS";

// Convert above paragraph to array of words
let paragraphWords = paragraphOriginal.replace(/\s+/g, " ").toLowerCase().replace(/\,|\?|\!|\:|\./g, '').trim().split(' ');


//dimensions of the paragraphContainer container
let height = document.getElementsByClassName("paragraphContainer")[0].clientHeight;

let text_Height;


// Add paragraph to page and assign each word an specific id 
generateParagraph();


function generateParagraph() {

  let paragraph = document.getElementById("paragraph");
  let answerPrototype = '';

  for (let i = 0; i < paragraphWords.length; i++) {
    paragraphWords[i] = ' ' + paragraphWords[i];
  }
  //paragraphWords[0] = paragraphWords[0].trim();

  let paragraphSpans = '';

  for (let i = 0; i < paragraphWords.length; i++) {

    paragraphSpans += `<span class='spans' id='spanID${i}'>${paragraphWords[i]}</span>`;

  };

  //modify the font size based on text length

  let fontSize = getFontSize(paragraphOriginal); // I think here is the problem

  console.log(fontSize);

  paragraph.style.fontSize = `${fontSize}vw`;

  paragraph.innerHTML = `${paragraphSpans}`;

};




function getFontSize(paragraph) {

  let default_size = 5;

  do {
    default_size -= 0.1;
    text_Height = getTextHeight(paragraph, `bold ${default_size}vw Open Sans`);
  }

  while (text_Height > height);

  return default_size.toFixed(2);

};


//Start Of Text Height Function
function getTextHeight(text, font) {
  let canvas = document.createElement("canvas")
  let context = canvas.getContext("2d");

  let sourceWidth = canvas.width;
  let sourceHeight = canvas.height;

  context.font = font;

  // place the text somewhere
  context.textAlign = "left";
  context.textBaseline = "top";
  context.fillText(text, 25, 5);

  // returns an array containing the sum of all pixels in a canvas
  // * 4 (red, green, blue, alpha)
  // [pixel1Red, pixel1Green, pixel1Blue, pixel1Alpha, pixel2Red ...]
  let data = context.getImageData(0, 0, sourceWidth, sourceHeight).data;

  let firstY = -1;
  let lastY = -1;

  // loop through each row
  for (let y = 0; y < sourceHeight; y++) {
    // loop through each column
    for (let x = 0; x < sourceWidth; x++) {
      //var red = data[((sourceWidth * y) + x) * 4];
      //var green = data[((sourceWidth * y) + x) * 4 + 1];
      //var blue = data[((sourceWidth * y) + x) * 4 + 2];
      let alpha = data[((sourceWidth * y) + x) * 4 + 3];

      if (alpha > 0) {
        firstY = y;
        // exit the loop
        break;
      }
    }
    if (firstY >= 0) {
      // exit the loop
      break;
    }

  }

  // loop through each row, this time beginning from the last row
  for (let y = sourceHeight; y > 0; y--) {
    // loop through each column
    for (let x = 0; x < sourceWidth; x++) {
      var alpha = data[((sourceWidth * y) + x) * 4 + 3];
      if (alpha > 0) {
        lastY = y;
        // exit the loop
        break;
      }
    }
    if (lastY >= 0) {
      // exit the loop
      break;
    }

  }

  return lastY - firstY;

}; //End Of Text Height Function
html {
  overflow: hidden;
  background-color: transparent;
}

.paragraphContainer {
  position: absolute;
  overflow: hidden;
  left: 2.45vw;
  top: 25vh;
  height: 55vh;
  width: 95vw;
  outline: 0.1vw dashed orange;
}

.paragraph-class {
  position: absolute;
  white-space: pre-wrap;
  font-family: 'Open Sans', sans-serif;
  font-weight: 400;
  color: #595959;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  flex-wrap: wrap;
  padding: 0vh 1vh 20vh 1vh;
  justify-content: left;
  align-items: left;
}
<!DOCTYPE html>
<html>

<head>

  <link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700&display=swap" rel="stylesheet">
  <link rel="stylesheet" type="text/css" href="style.css">

</head>

<body>

  <div class="paragraphContainer">
    <div id="paragraph" class="paragraph-class"></div>
  </div>


  <!-- <button id="myBtn">Try it</button>

<script>
document.getElementById("myBtn").addEventListener("click", function(){
  Read();
});
</script> -->


  <script src="script.js"></script>
</body>

</html>
Penny Liu
  • 15,447
  • 5
  • 79
  • 98
foxer
  • 811
  • 1
  • 6
  • 16

1 Answers1

2

I did my best to find a workaround for you so I'll explain what I did and I hope it helps.

First of all I removed getFontSize(paragraph) and getTextHeight(text, font) because they're returning the same font size every time regardless of the text height and removed some CSS rules that are useless.

Next, I added scaleFontVW() function where I scale both the font-size and the line-height of each span as you need to do both.

Next, while(paragraph.scrollHeight > paragraphContainer.clientHeight) basically I'm comparing the overflowed content height with the container's height and looping till both become one.

Next, Each span size is getting scaled down by subtracting the value of fontSize (by trial and error I found 5 is working good), and the line-height of the paragraph as well, The line height formula is pretty straight forward: font-size + font-size * 20%.

Answers that helped: Paul's and micnic's

// our paragraph
let paragraphOriginal = "During a discussion that popped up over this, Adam pointed out there is a new CSS property that is (as I understand it) specifically for this. This removes the need for three elements. Technically you only need one, the inline element, but it's likely you'll be doing this on a header so you'll probably end up with a block-parent anyway, which is best for spacing. LAST WORDS";



// Convert above paragraph to array of words
let paragraphWords = paragraphOriginal.replace(/\s+/g, " ").toLowerCase().replace(/\,|\?|\!|\:|\./g,'').trim().split(' ');


//dimensions of the paragraphContainer container
let height = document.getElementsByClassName("paragraphContainer")[0].clientHeight;

let text_Height;


// Add paragraph to page and assign each word an specific id 
generateParagraph();

// Scale the font to fit
scaleFontVW();

function generateParagraph() {

    let paragraph = document.getElementById("paragraph");
    let answerPrototype = '';

    for(let i = 0; i < paragraphWords.length; i++){
        paragraphWords[i] = ' ' + paragraphWords[i];
    }
    //paragraphWords[0] = paragraphWords[0].trim();

    let paragraphSpans = '';

    for (let i = 0; i < paragraphWords.length; i++) {

      paragraphSpans += `<span class='spans' id='spanID${i}'>${paragraphWords[i]}</span>`;

    };

    //modify the font size based on text length

    //let fontSize = getFontSize(paragraphOriginal ); // I think here is the problem

    //console.log(fontSize);
    
    //paragraph.style.fontSize = `${fontSize}vw`;
     
    paragraph.innerHTML = `${paragraphSpans}`;

};

function scaleFontVW() {
  let paragraph = document.getElementById("paragraph");
  let paragraphContainer = document.getElementById("pc")
  let spans = document.getElementsByTagName("span");

  let style = window.getComputedStyle(spans[0], null).getPropertyValue('font-size');
  let fontSize = parseFloat(style); 

  while(paragraph.scrollHeight >= paragraphContainer.clientHeight) {
    fontSize -= 5;
    for(let i = 0; i < spans.length; i++) {
      spans[i].style.fontSize = fontSize+"px";
      }
    paragraph.style.lineHeight = fontSize*0.2 + fontSize + "px";
}
}

function scaleFontVW_2() {
  let paragraph = document.getElementById("paragraph");
  let paragraphContainer = document.getElementById("pc")

  let style = window.getComputedStyle(spans[0], null).getPropertyValue('font-size');
  let fontSize = parseFloat(style); 

  while(paragraph.scrollHeight >= paragraphContainer.clientHeight) {
    fontSize -= 1;
    paragraph.style.fontSize = fontSize+"px";
    paragraph.style.lineHeight = fontSize*0.2 + fontSize + "px";
}
}
html{
  background-color: transparent;
}

.paragraphContainer {
    position: absolute;
    left: 2.45vw;
    top: 25vh;
    height: 55vh;
    width: 95vw;   
    outline: 0.1vw dashed orange;
}

.paragraph-class {
    font-size: 5vw;
    position: absolute;
    white-space: pre-wrap; 
    font-family: 'Open Sans', sans-serif;  
    font-weight:400;
    color: #595959;
}
<!DOCTYPE html>
<html>
<head>

  <link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700&display=swap" rel="stylesheet">
  <link rel="stylesheet" type="text/css" href="style.css"> 

</head>

<body> 

<div id = "pc" class="paragraphContainer"> 
<div id="paragraph" class="paragraph-class"></div>
</div>


<!-- <button id="myBtn">Try it</button>

<script>
document.getElementById("myBtn").addEventListener("click", function(){
  Read();
});
</script> -->


<script src="script.js"></script>
</body>

</html>
Mohamed Wagih
  • 1,246
  • 6
  • 17
  • 1
    Glad it helped. Also, note that I've just added now a new function called function `scaleFontVW_2()` which works better with very long (I mean really really long) strings and with your normal string. I found out changing the `paragraph` fontSize is instead of each `span`'s better when longer strings are provided and works well too with normal string length like the one you provided. Check it out and tell me what is the better version for your case. Cheers! – Mohamed Wagih Mar 05 '20 at 23:23
  • Thanks for the update...yeah... it's more clear and efficient... for those who may have same problem please add `let spans = document.getElementsByTagName("span");` to the `scaleFontVW_2()` – foxer Mar 06 '20 at 09:08