Alright guys, I am making a testing software. Right now, I am making a question page that displays answer choices.
I am displaying the answer choices as li elements. Sometimes, a couple of the questions that I ask have super long answer choices and it overflows the li element. My current solution is to make the li big enough to contain the largest answer choices, but now it looks goofy for the 2 word, 3 word answer choices.
So now, I want the height of the li element to change based on the number of lines of text of the answer option basically (instead of overflow, I want it to grow to put it in laymans terms).
Here are the relevant snippets.
<ul>
<li className={(this.state.submitted) && (answerkey[question]==='A' ) ? 'correct': (answers[question]==='a' ) && (answerkey[question] !=='A' ) && (this.state.submitted) ? 'wrong': ''}>
<input type='radio' value='a' id='a-option' name='selector' checked={response==='a' } onChange={this.onChange} />
<label for='a-option'>
<div className='adjust-reading-text'>
{answerchoices[question][0]}
</div>
</label>
<div className='check'></div>
</li>
<li className={(this.state.submitted) && (answerkey[question]==='B' ) ? 'correct': (answers[question]==='b' ) && (answerkey[question] !=='B' ) && (this.state.submitted) ? 'wrong': ''}>
<input type='radio' value='b' id='b-option' name='selector' checked={response==='b' } onChange={this.onChange} />
<label for='b-option'>
<div className='adjust-reading-text'>
{answerchoices[question][1]}
</div>
</label>
<div className='check'></div>
</li>
<li className={(this.state.submitted) && (answerkey[question]==='C' ) ? 'correct': (answers[question]==='c' ) && (answerkey[question] !=='C' ) && (this.state.submitted) ? 'wrong': ''}>
<input type='radio' value='c' id='c-option' name='selector' checked={response==='c' } onChange={this.onChange} />
<label for='c-option'>
<div className='adjust-reading-text'>
{answerchoices[question][2]}
</div>
</label>
<div className='check'></div>
</li>
<li className={(this.state.submitted) && (answerkey[question]==='D' ) ? 'correct': (answers[question]==='d' ) && (answerkey[question] !=='D' ) && (this.state.submitted) ? 'wrong': ''}>
<input type='radio' value='d' id='d-option' name='selector' checked={response==='d' } onChange={this.onChange} />
<label for='d-option'>
<div className='adjust-reading-text'>
{answerchoices[question][3]}
</div>
</label>
<div className='check'></div>
</li>
</ul>
Don't worry about the react state stuff it's not relevant to the question, just know that {answerchoices[question][3]} loads the text of the answer choices.
Here is the css of the stuff that needs changed.
.container-reading ul{
list-style: none;
margin: 0;
padding: 20px;
}
.container-reading ul li{
color: black;
position: relative;
display: inline;
width: 95%;
font-size: 16px;
border-bottom: 1px solid #333;
padding-bottom: 5px;
}
I thought perhaps a hidden element that acts as a clone as it is tough determining the length of text in an element in JS, so we just place the list items text in an element that grows with the amount of text that is present. This will allow us to get the width of the container and give us an accurate length in pixels that the text is taking up. We compare that to the clientWidth of your list items and find which elements are overflowing.
I thought it might be easier to style a flexbox elements children, so I wrapped the text in a span tag and placed it after the radio input. Then we add some styling to the selector and add the selector to the parent element. Place an align-items: center
to vertically center the input and text.
const answers = document.querySelectorAll('.container-reading li')
function contOverflown(element) {
let clone = document.createElement('DIV')
let output = false
clone.textContent = element.textContent
clone.style.visibility = 'hidden'
clone.style.padding = 0;
clone.style.border = 'none';
clone.style.position = 'absolute'
document.body.append(clone)
element.clientWidth < clone.clientWidth ?
output = true :
null
return output;
}
function resetLayoutForOverFlown(elements) {
elements.forEach(answer => {
if (contOverflown(answer) !== false) {
let span = document.createElement('SPAN')
span.style.width = '90%'
span.textContent = answer.textContent
let npt = answer.children[0]
answer.textContent = ''
answer.append(npt, span)
answer.style.display = 'flex'
answer.classList.add('overflown')
}
})
}
resetLayoutForOverFlown(answers)
.container-reading ul {
list-style: none;
padding: 20px;
margin: 0;
}
.container-reading ul li {
color: black;
position: relative;
width: 95%;
font-size: 16px;
border-bottom: 1px solid #333;
padding-bottom: 5px;
}
.container-reading ul li input {
padding-right: 2rem;
}
.overflown {
display: flex;
justify-content: flex-start;
align-items: center;
}
.overflown span {
margin-left: .3em;
flex-wrap: wrap;
flex: 2;
max-width: calc(100% - 2em);
}
<div class='container-reading'>
<ul>
<li class="answer-item">
<input type="radio" name="check"> This answer is a far too long for our list items content. We will wrap its text in a span tag and flex the input and span for a more cohesive layout
</li>
<li class="answer-item"><input type="radio" name="check"> This answer is ok</li>
<li class="answer-item"><input type="radio" name="check"> This answer is also ok</li>
<li class="answer-item"><input type="radio" name="check"> This answer is ok</li>
<li class="answer-item">
<input type="radio" name="check"> Yet another answer that is a far too long for our list items content, so JS will determine its length and write a new layout and style for its content
</li>
</ul>
</div>