I'm trying to achieve an underline on some wrapped text that fits to the width of the bottom row of text whilst only appearing underneath that bottom line. Figure 1 illustrates the desired effect
Figure 1
Using this HTML:
<h2><span class="inline-block">optatur, volendit inum simolor</span></h2>
and setting the span to display:inline; I can get the underline to fit perfectly with the width of the text but it underlines all of the text.
Or, setting the span to display:inline-block; I can get the underline to only appear under the bottom line but it then fills the whole width of the parent.
See this JSfiddle for the above examples: http://jsfiddle.net/PWDV7/1/
Is there any way to achieve the result of figure 1?
With a good leg up from this answer to a questions about finding line wraps, I managed to come up with this solution (in short, it involves using JS to find where a line wrap has occurred and wraps a span around all the text that sits on the last line):
function underLineText () {
$underlined_text.each(function(){
var $this = $(this);
var text = $this.text();
var originalText = text;
var breakWords = new Array();
//split the words into individual strings
var words = text.split(' ');
//find the height of the first word
$this.text(words[0]);
var height = $this.height();
//if there is more than one word
if (words.length > 1) {
//loop through all the words
for(var i = 1; i < words.length; i++){
$this.text($this.text() + ' ' + words[i]);
//check if the current word has a different height from the previous word
//if this is true, we have witnessed a word wrap!
if($this.height() > height){
height = $this.height();
//add the first word after the wrap to a predefined array
breakWords.push(words[i]);
}
//on the last iteration on the loop...
if (i === words.length - 1) {
if (breakWords.length > 0) {
//select the last word of the breakWords array
//(this is to handle cases where there there are more than one line or word wraps)
var breakStartAt = breakWords[breakWords.length - 1];
//add a span before the last word
var withSpan = '<span>'+breakStartAt;
//replace the last occurrence of this word with the span wrapped version
//(this is to handle cases where there are more than one occurrences of the last word)
originalText = originalText.replaceLast(breakStartAt,withSpan);
//close the last line with a span
originalText += "</span>";
}
}
}
}
//if there are no word wraps, wrap the whole text in spans
else {
originalText = '<span>'+originalText+'</span>';
}
//replace the original text with the span-wrapped mod
$(this).html(originalText);
});
}
You can see it working here: http://jsfiddle.net/PWDV7/5/
Change the code like so:
HTML
<h2>optatur, volendit <span>inum simolor</span></h2>
CSS
h2 {
width:200px;
text-align:center;
}
h2 span {
border-bottom:3px solid black;
width:100%;
}
All I changed was the position of the <span> to wrap the text that you want the border on.
JsFiddle
Related
I have a 20 line script, and I want to make sure that each paragraph is indented exactly once.
function myFunction() {
/*
This function turns the document's format into standard MLA.
*/
var body = DocumentApp.getActiveDocument().getBody();
body.setFontSize(12); // Set the font size of the contents of the documents to 9
body.setForegroundColor('#000000');
body.setFontFamily("Times New Roman");
// Loops through paragraphs in body and sets each to double spaced
var paragraphs = body.getParagraphs();
for (var i = 3; i < paragraphs.length; i++) { // Starts at 3 to exclude first 4 developer-made paragraphs
var paragraph = paragraphs[i];
paragraph.setLineSpacing(2);
// Left align the first cell.
paragraph.setAlignment(DocumentApp.HorizontalAlignment.LEFT);
// One indent
paragraph.editAsText().insertText(0, "\t"); // Adds one tab every time
}
var bodyText = body.editAsText();
bodyText.insertText(0, 'February 3, 1976\nMrs. Smith\nYour Name Here\nSocial Studies\n');
bodyText.setBold(false);
}
The code I have tried doesn't work. But my expected results are that for every paragraph in the for loop in myFunction(), there are exactly 4 spaces before the first word in each paragraph.
Here is a sample: https://docs.google.com/document/d/1sMztzhOehzheRdqumC6PLnvk4qJgUCSE0irjTZ0FjTQ/edit?usp=sharing
If the user uses Autoformat, but already has the paragraphs indented...
Update
I have investigated use of the Paragraph.setIndentFirstLine() method. When I set it to four, it sets it to 1 space. Now I realize this is because points and spaces are not the same thing. What number do I need to multiply by to get four spaces in points?
Let us consider a few basic identing operations: manual and by script.
The following image shows how to indent current paragraph (cursor stays inside this one).
Please note, the units are centimetres. Also note, that the paragraph does not include leading spaces or tabs, we have no need of them.
Suppose we would like to get the indent values in the script and apply them to the next paragraph. Look at the code below:
function myFunction() {
var ps = DocumentApp.getActiveDocument().getBody().getParagraphs();
// We work with the 5-th and 6-th paragraphs indeed
var iFirst = ps[5].getIndentFirstLine();
var iStart = ps[5].getIndentStart();
var iEnd = ps[5].getIndentEnd();
Logger.log([iFirst, iStart, iEnd]);
ps[6].setIndentFirstLine(iFirst);
ps[6].setIndentStart(iStart);
ps[6].setIndentEnd(iEnd);
}
If you run and look at the log, you will see something like this: [92.69291338582678, 64.34645669291339, 14.173228346456694]. No surprise, we have typographic points instead of centimetres. (1cm=28.3465pt) So we can measure and modify any paragraph indent values precisely.
Addition
For some reasons you might want to control spaces number at the beginning of the paragraph. It is also possible by scripting, but it has no effect on the paragraph's "left" or "right" indents.
Sample code below is for similar task: count leading spaces number of the 5-th paragraph and make the same number of spaces at the beginning of the next one.
function mySpaces() {
var ps = DocumentApp.getActiveDocument().getBody().getParagraphs();
// We work with the 5-th and 6-th paragraphs indeed
var spacesCount = getLeadingSpacesCount(ps[5]);
Logger.log(spacesCount);
var diff = getLeadingSpacesCount(ps[6]) - spacesCount;
if (diff > 0) {
ps[6].editAsText().deleteText(0, diff - 1);
} else if (diff < 0) {
var s = Array(1 - diff).join(' ');
ps[6].editAsText().insertText(0, s);
}
}
function getLeadingSpacesCount(p) {
var found = p.findText("^ +");
return found ? found.getEndOffsetInclusive() + 1 : 0;
}
We have used methods deleteText() and insertText() of the class Text for proper corrections and findText() to locate the spaces if any. Note, the last method argument is a string, representing a regular expression. It matches "all leading spaces", if they exist. See more details about regular expression syntax.
I'm trying to auto-number each line that will be generated while displaying a
<p> element.
Perhaps using counters in CSS?
I'm looking for something along the lines of p:first-line, except for every line of the <p> element
something like:
p:each-line {
counter-increment line_num;
}
p:each-line:before {
counter(line_num) " " ACTUAL-LINE;
}
Can I do this with simple CSS code? How else could I achieve this?
I have an element called message, and I don't know in advance how many lines
of actual text will be formatted using that element style. If I change the
max-width for example and that forces more/fewer lines, I'd like this to automatically
number correctly the actual lines in the element.
/* set up the speech bubbles */
p.message {
position:relative;
padding:5px 10px;
border:2px solid rgb(74,77,82);
border:2px solid rgba(74,77,82,.5);
-moz-border-radius:10px;
-webkit-border-radius:10px;
border-radius:10px;
max-width: 70%;
}
Such a task is a little much for CSS alone to handle. It isn't too hard in javascript.
It sounded like a nice little distraction so I played around a bit in jsfiddle. Perhaps this will help even though it's not pure css and uses some jquery.
http://jsfiddle.net/rSFUB/2/
Notice that I wrapped the <p> text in a div and added a line number div within that absolutely positioned. The javascript is:
$(document).ready(function () {
$(".message").each(function () {
var self = $(this);
var numbering = self.find(".lineNumbering").first();
var messageText = self.find("p").first();
var lineHeight = numbering.text("...").height();
var lines = messageText.height() / lineHeight;
var lineNumberingHtml = "";
for(var i = 1; i <= lines; i++) {
lineNumberingHtml = "" + lineNumberingHtml + i + "<br />";
}
numbering.html(lineNumberingHtml);
});
});
I tested in IE10, Chrome, and Firefox. The only difference between this code in the various versions is the padding on the .lineNumber div in order for it to line up with the text. Note this assumes that the line number div text and the paragraph is the same line-height.
I wish to do the following within div tags:
The words will be coloured differently using spans.
I will be given some text in a text box and via JavaScript I will need to dynamically update to div to show something like the above.
What is the best way to do this?
Will it involve a monospaced font?
Will it involve writing "hidden" text?
I wish to do entire paragraphs in this manner.
This might seem weird but the research I'm doing requires me present certain words from a given text with multiple colours and I think this might be a nice way of conveying this information.
Updating the text in the text box will update the following variables, and in turn I will need to convert these two variables into something like the image above.
text = "I am under the text above me and there is lots more text to come./n I am even moving onto a new line since I have more text"
color_per_word_position = {0:green, 1: red, 2: cyan, 4: yellow, 5: red, ...}
You will have to use a monospaced font for this.*
I basically see two options: 1. use whitespace 2. margins.
Option 1
Your text will look like
I•am•under•the•text•above
••am•under•••••text•above
where • denotes a space character. Pretty straight-forward in terms of CSS, since you don't have to worry about the spacing. The browser does it all for you. Example: http://jsfiddle.net/PYXdr/
*well, it may be possible with any font, using a lot of JS, but I guess it's not worth it.
Option 2
Since you probably don't want whitespace in between your spans, you may prefer this:
I•am•under•the•text•above
am•under text•above
Now, the spacing needs to be taken care of manually. Each span should get a margin-left that pushes it to the desired position. But before we can do that, we need to know the width of one character (using JS, since CSS does not provide that). Okay, pretty easy:
var el = document.createElement('pre');
el.style.display = 'inline-block';
el.innerHTML = ' ';
document.body.appendChild(el);
var width = parseFloat(getComputedStyle(el).width);
document.body.removeChild(el);
Now let's go ahead and move the spans:
span1.style.marginLeft = (2 * width) + 'px';
span2.style.marginLeft = (5 * width) + 'px';
Example: http://jsfiddle.net/JC3Sc/
Putting it all together
Now here's a basic example of how this might work:
var text = "I am under the text above me and there is lots more text to come.\nI am even moving onto a new line since I have more text"
var highlightBorders = [[2, 3, 4, 6], [6, 7]]; // YOUR TASK: implement the logic to display the following lines
var color_per_word_position = {0:'lime', 1: 'red', 2: 'cyan', 3:'orange', 4: 'yellow', 5: 'red'}
/* generate CSS */
var style = document.createElement('style');
for (var i in color_per_word_position) {
style.innerHTML += '.hl' + i + '{background:' + color_per_word_position[i] + '}';
}
document.head.appendChild(style);
/* generating the text */
text = text.split('\n');
var pre = document.createElement('pre');
text.forEach(function (line, i) {
var div = document.createElement('div');
var words = line.split(' ');
var result = [];
highlightBorders[i].forEach(function (len, j) {
var span = document.createElement('span');
span.innerHTML = words.splice(0, len).join(' ');
span.className = 'hl' + j;
if (j) {
span.style.marginLeft = width + 'px' // YOUR TASK: implement the logic
}
div.appendChild(span);
});
pre.appendChild(div);
});
document.body.appendChild(pre);
This is not a complete solution, since a) I don't really see which parts exactly you want to highlight and b) I don't want to spoil all the fun. But you get the idea.
Example: http://jsfiddle.net/tNyqL/
Using padding this is possible but also have absolute control by assigning text to a selector such as "p" for the class: fiddle http://jsfiddle.net/3NDs3/1/
.one {
width:200px;
}
.one p {
font: normal 14px Futura, sans-serif;
text-align:left;
padding-left:130px;
}
.two {
width:200px;
}
.two p {
text-align:center;
font: normal 14px Futura, sans-serif;
}
.three {
width:200px
}
.three p {
text-align:left;
font: normal 14px Futura, sans-serif;
padding-left:35px;
}
<div class="one">
<p>above me</p>
</div>
<div class="two">
<p>i am under the text above me</p>
</div>
<div class="three">
<p>under</p>
</div>
I want to have to floated columns side-by side. If second column has contents then the first column should resize its content based on the second column, is that possible with CSS? Widths are not defined.
It's possible using javascript but not with straight CSS. Here are some ways to do it:
Using javascript:
var rightDiv = document.getElementById("straightJ2");
var rightWidth = rightDiv.clientWidth;
var leftDiv = document.getElementById("straightJ1");
if($('#straightJ2').text() || rightDiv.hasChildNodes()) {
leftDiv.style.width = rightWidth + "px";
}
Or more minimal (and possibly overwhelming/hard to follow) javascript:
if($('#minimal2').text() || $('#minimal2').firstChild) {document.getElementById("minimal1").style.width = document.getElementById("minimal2").clientWidth + "px";}
Using jQuery:
if($('#secondColumnId').html() != '')
{
$('#firstColumnId').width($('#secondColumnId').width());
}
Each of these check the second div for text or a child element and change the width of the first if one of those conditions is met
All examples can be found used in this jsFiddle
I have a span inside the contenteditable div and I need to figure out if the cursor is in the last position on the span tag. I looked in the Selection and Range solutions but couldn't figure out a straight forward way to achieve that.
<html>
<script>
var selection = window.getSelection();
var range = selection.getRangeAt(0);
//this is what I am trying to achive
var selection_target = range.selectNodeContents(document.getElementById('target'));
var length = selection_target.toString().length;
</script>
<body>
<div id='target' contenteditable='true'>
<span>some text(figure cursor position here)</span>
</div>
</body>
</html>
For the case in the question where the span only has one child that is a text node, you can check whether the caret is at the end of it as follows:
Demo: http://jsfiddle.net/Wm5Hz/
Code:
var span = document.getElementById("target").getElementsByTagName("span")[0];
var spanTextNode = span.lastChild;
var sel = window.getSelection();
var isCaretAtEndOfSpan = (sel.isCollapsed
&& sel.focusNode == spanTextNode
&& sel.focusOffset == spanTextNode.data.length);
Things are slightly more complicated in the general case: the same visual position can be represented in different ways (e.g. after the last character in the text node, after the text node itself, after the end of the span), plus if the span could contain elements then you'd need to find the last text node within the span and compare the selection against it. Also, none of this works in IE < 9, which has a completely different API.