jQuery.insertBefore is not respecting the line break - javascript

I have a simple html with a div limited by width. Inside I have many span tags that are configured with nowrap. My css:
.content {
width: 50%;
margin: 10px;
padding: 10px;
border: 1px solid black;
}
.adder {
color: blue;
cursor: pointer;
}
.added {
position: relative;
white-space: nowrap;
}
And my html:
<div class="content">
<span class="added">A text just a little large 1.</span>
<span class="added">A text just a little large 2.</span>
<span class="added">A text just a little large 3.</span>
<span class="added">A text just a little large 4.</span>
<span class="added">A text just a little large 5.</span>
<span class="adder">Add</span>
</div>
As expected, the text is broken when there is no more space in the line to be placed. Then the entire span is rendered on the next line. Now I added some javascript code:
$(function() {
$(".adder").click(function() {
$(document.createElement("span"))
.addClass("added")
.html("A custom text to be add,")
.insertBefore(this);
});
});
So, now a new span is placed before the Add text for every time I click on Add.
But the problem is this, when I click a few more times in the Add, there comes a point where the end of the line size is reached, but rather to break the line as the other part of the text does, the new span is simply rendered in the same line overlapping the edge of the div.
Why this happen? How to avoid it?
I'm testing this page in Google Chrome 42.0.2311.135.
The whole html can be viewed in jsfinddle.

Your original list of "added" <span> elements have whitespace between them. The ones you add with the JavaScript code don't. Therefore, the browser can't insert line breaks between them — it will only do that at whitespace boundaries.
You can fix that in a few different ways; one simple way is to add this to your "click" handler:
$("<span/>", { text: " " }).insertBefore(this);
Another way to fix it is with pure CSS:
.added::after {
white-space: normal;
content: "\00200B";
}
No JavaScript changes would be necessary with that approach.

When you use insertBefore there is no space added between yoru elements. Simply add a space yourself and they will wrap as you expect:
$(function() {
$(".adder").click(function() {
$(document.createElement("span"))
.addClass("added")
.html("A custom text to be add,")
.insertBefore(this)
.after(" "); // add a single space
});
});
http://jsfiddle.net/gtoh8hzp/

Related

Clicking outside a contenteditable div stills give focus to it?

For some reason I need to use contenteditable div instead of normal text input for inputting text. (for some javascript library) It works fine until I found that when I set the contenteditable div using display: inline-block, it gives focus to the div even if I click outside the div!
I need it to be giving focus to the div only when the user clicks right onto the div but not around it. Currently, I found that when the user clicks elsewhere and then click at position that is the same row as the div, it gives focus to it.
A simple example to show the problem:
HTML:
<div class="outside">
<div class="text-input" contenteditable="true">
Input 1
</div>
<div class="text-input" contenteditable="true">
Input 2
</div>
<div class="unrelated">This is some unrelated content<br>
This is some more unrelated content
This is just some space to shows that clicking here doesn't mess with the contenteditable div
but clicking the side mess with it.
</div>
</div>
CSS:
div.outside {
margin: 30px;
}
div.text-input {
display:inline-block;
background-color: black;
color: white;
width: 300px;
}
The JSFiddle for displaying the problem
Is there a way (CSS or javascript are both acceptable) to make the browser only give focus to div when it is clicked instead of clicking the same row?
P.S. I noticed that there are similar problem (link to other related post), but the situation is a bit different and the solution provided is not working for me.
Explanation (if you don't care, skip to the Workarounds below)
When you click in an editable element, the browser places a cursor (a.k.a. insertion point) in the nearest text node that is within the clicked element, on the same line as your click. The text node may be either directly within the clicked element, or in one of its child elements. You can verify this by running the code snippet below and clicking around in the large blue box.
.container {width: auto; padding: 20px; background: cornflowerblue;}
.container * {margin: 4px; padding: 4px;}
div {width: 50%; background: gold;}
span {background: orange;}
span > span {background: gold;}
span > span > span {background: yellow;}
<div class="container" contenteditable>
text in an editable element
<div>
text in a nested div
</div>
<span><span><span>text in a deeply nested span</span></span></span></div>
Notice that you can get an insertion point by clicking above the first line or below the last. This is because the "hitbox" of these lines extends to the top and bottom of the container, respectively. Some of the other answers don't account for this!
The blue box is a <div> with the contenteditable attribute, and the inner orange/yellow boxes are nested child elements. Notice that if you click near (but not in) one of the child elements, the cursor ends up inside it, even though you clicked outside. This is not a bug. Since the element you clicked on (the blue box) is editable and the child element is part of its content, it makes sense to place the cursor in the child element if that's where the nearest text node happens to be.
The problem is that Webkit browsers (Chrome, Safari, Opera) exhibit this same behavior when contenteditable is set on the child instead of the parent. The browser shouldn't even bother looking for the nearest text node in this case since the element you actually clicked on isn't editable. But Webkit does, and if that text node happens to be in the editable child, you get a blinking cursor. I'd consider that a bug; Webkit browsers are doing this:
on click:
find nearest text node within clicked element;
if text node is editable:
add insertion point;
...when they should be doing this:
on click:
if clicked element is editable:
find nearest text node within clicked element;
add insertion point;
Block elements (such as divs) don't seem to be affected by the bug, which makes me think #GOTO 0's answer is correct in implicating text selection-- at least insofar as it seems to be governed by the same logic that controls insertion point placement. Multi-clicking outside an inline element highlights the text within it, but not so for block elements. It's probably no coincidence that you also don't get an insertion point when you click outside a block. The first workaround below makes use of this exception.
Workaround 1 (nested div)
Since blocks aren't affected by the bug, I think the best solution is to nest a div in the inline-block and make it editable instead. Inline-blocks already behave like blocks internally, so the div should have no effect on its behavior.
div.outside {
margin: 30px;
}
div.text-input {
display:inline-block;
background-color: black;
color: white;
width: 300px;
}
<div class="outside">
<div class="text-input">
<div contenteditable>
Input 1
</div>
</div>
<div class="text-input">
<div contenteditable>
Input 2
</div>
</div>
<div class="unrelated">This is some unrelated content<br>
This is some more unrelated content
This is just some space to shows that clicking here doesn't mess with the contenteditable div
but clicking the side mess with it.
</div>
</div>
Workaround 2 (invisible characters)
If you must put the contenteditable attribute on the inline-blocks, this solution will allow it. It works by surrounding the inline-blocks with invisible characters (specifically, zero-width spaces) which shield them from external clicks. (GOTO 0's answer uses the same principle, but it still had some problems last I checked).
div.outside {
margin: 30px;
}
div.text-input {
display:inline-block;
background-color: black;
color: white;
width: 300px;
white-space: normal;
}
.input-container {white-space: nowrap;}
<div class="outside">
<span class="input-container">​<div class="text-input" contenteditable>
Input 1
</div>​</span>
<span class="input-container">​<div class="text-input" contenteditable>
Input 2
</div>​</span>
<div class="unrelated">This is some unrelated content<br>
This is some more unrelated content
This is just some space to shows that clicking here doesn't mess with the contenteditable div
but clicking the side mess with it.
</div>
</div>
Workaround 3 (javascript)
If you absolutely can't change your markup, then this JavaScript-based solution could work as a last resort (inspired by this answer). It sets contentEditable to true when the inline-blocks are clicked, and false when they lose focus.
(function() {
var inputs = document.querySelectorAll('.text-input');
for(var i = inputs.length; i--;) {
inputs[i].addEventListener('click', function(e) {
e.target.contentEditable = true;
e.target.focus();
});
inputs[i].addEventListener('blur', function(e) {
e.target.contentEditable = false;
});
}
})();
div.outside {
margin: 30px;
}
div.text-input {
display:inline-block;
background-color: black;
color: white;
width: 300px;
}
<div class="outside">
<div class="text-input">
Input 1
</div>
<div class="text-input">
Input 2
</div>
<div class="unrelated">This is some unrelated content<br>
This is some more unrelated content
This is just some space to shows that clicking here doesn't mess with the contenteditable div
but clicking the side mess with it.
</div>
</div>
I was able to reproduce this behavior only in Chrome and Safari, suggesting that this may be a Webkit related issue.
It's hard to tell what's going on without inspecting the code but we can at least suspect that the problem lies in some faulty mechanism that triggers text selection in the browser.
For analogy, if the divs were not contenteditable, clicking in the same line of text after the last character would trigger a text selection starting at the end of the line.
The workaround is to wrap the contenteditable divs into a container element and style the container with -webkit-user-select: none to make it unselectable.
As Alex Char points out in a comment, this will not prevent a mouse click outside the container to trigger a selection at the start of the text inside it, since there is no static text between the first contenteditable div and the (selectable) ancestor container around it.
There are likely more elegant solutions, but a way to overcome this problem is to insert an invisible, nonempty span of text of zero width just before the first contenteditable div to capture the unwanted text selection.
Why non empty?: Because empty elements are ignored upon text selection.
Why zero width?: Because we don't want to see it...
Why invisible?: Because we don't want the content to be copied to the clipboard with, say Ctrl+A, Ctrl+C.
div.outside {
margin: 30px;
}
div.text-input {
display:inline-block;
background-color: black;
color: white;
width: 300px;
}
div.text-input-container {
-webkit-user-select: none;
}
.invisible {
visibility: hidden;
}
<div class="outside">
<div class="text-input-container">
<span class="invisible">​</span><div class="text-input" contenteditable="true">
Input 1
</div>
<div class="text-input" contenteditable="true">
Input 2
</div>
</div>
<div class="unrelated">This is some unrelated content<br>
This is some more unrelated content
This is just some space to shows that clicking here doesn't mess with the contenteditable div
but clicking the side mess with it.
</div>
</div>
Even in normal circumstances it is generally a good idea to keep adjacent inline-block elements in a separate container rather than next to a block element (like the unrelated div) to prevent unexpected layout effects in case the order of the sibling elements changes.
If it's not needed to use display: inline-block, I would recommend using float. Here is the example.
Based on your example, the new CSS would be:
div.text-input {
display: block;
background-color: black;
color: white;
width: 300px;
float: left;
margin-right: 10px;
}
div.unrelated {
clear: both;
}
Disable text selection in container... should fix that.
For example:
* {
-ms-user-select: none; /* IE 10+ */
-moz-user-select: -moz-none;
-khtml-user-select: none;
-webkit-user-select: none;
user-select: none;
}
How about a little jQuery?
$(".outside").click(function(e){
$(e.target).siblings(".text-input").blur();
window.getSelection().removeAllRanges();
});
And if IRL you need to account for clicks on contenteditable=true siblings' children:
$(".outside").click(function(e){
if ($(e.target).siblings(".text-input").length != 0){
$(e.target).siblings(".text-input").blur();
window.getSelection().removeAllRanges();
}
else {
$(e.target).parentsUntil(".outside").last().siblings(".text-input").blur();
window.getSelection().removeAllRanges();
}
});
window.getSelection().removeAllRanges();"The trick is to remove all ranges after calling blur"

Mismatch in text width between div and textarea

I'm trying to create a textarea control in which it is possible to mention other users. The feature is pretty much similar to the one found in Facebook, and the implementation is similar too. When the user types an "#", a dropdown is presented from which a user can be selected which is then displayed with a highlight in the textarea. To be able to selectively render highlights in the textarea, I'm using an overlay div with the same text, but with span tags to create highlights.
The overlay has the same width, the same font and font-size, the same letter-spacing, same line-height, etc., to make sure all highlights will align properly with the text in the textarea. All the text in the overlay div, except for the highlights themselves, is transparent to avoid artifacts of rendering anti-aliased text over text.
This all works pretty well, except that when there is a mention highlight, the text in the highlight is somehow just slightly less wide than the text below it in the textarea, which causes a very slight mismatch. Worse, this small mismatch accumulates when there are multiple highlights, and it can sometimes cause a line to wrap in the textarea but not in the div, after which the whole illusion just falls apart.
I have verified that all text rendering options are exactly the same for the text in the textarea and in the overlay and in the highlights. All have equal font, font-size, letter-spacing, line-height, there's no margin, border or padding on the highlights, etc.. I have also looked in the WebKit Inspector to see if I might have missed any properties that could still affect text rendering, but couldn't find any. Simply put, I can't explain where this slight rendering difference comes from.
Please note that the rendering difference does not occur as long as the overlay doesn't contain any highlights.
I have also tried only rendering the overlay and not rendering the textarea at all (instead of having the overlay be transparent outside of the highlights), but this has the nasty side-effect that I won't see any cursor anymore.
Is there some CSS property that I still might have overlooked or is there some other reason why breaking the text into multiple spans would cause the total width of the text to slightly differ from an uninterrupted text node? Any suggestions would be greatly appreciated!
Update: For any others who might run into this problem, it's illustrated in the following jsfiddle: http://jsfiddle.net/brt8w85z/5/
<style type="text/css">
.parent {
text-rendering: optimizeLegibility;
position: relative;
}
textarea {
border: 0;
color: #000;
resize: none;
}
.overlay {
color: transparent;
pointer-events: none;
}
textarea,.overlay {
font-family: Helvetica,sans-serif;
font-size: 12px;
left: 10px;
letter-spacing: normal;
margin: 0;
padding: 0;
position: absolute;
top: 10px;
width: 200px;
}
.highlight {
background-color: #00f;
color: #fff;
}
</style>
<div class="parent">
<textarea>Tom Kleijn, Mark van der Velden and Arend van Beelen</textarea>
<div class="overlay"><span class="highlight">Tom Kleijn</span>, <span class="highlight">Mark van der Velden</span> and <span class="highlight">Arend van Beelen</span></div>
</div>
The problem can be fixed by adding "text-rendering: geometricPrecision" to the "textarea,.overlay" rule.
Seems I have found the solution myself: On the body there's a definition of "text-rendering: optimizeLegibility". Setting this back to "text-rendering: geometricPrecision" on the textarea fixed the problem. The reason this was not obvious before was because the WebKit Inspector did not show the inherited text-rendering on the textarea, even though it does so for (most?) other inherited properties.

Jquery: Target part of a text node on a second line

I don't know if this is possible. I have an h2 tag with some text:
<div>
<h2>Here Goes a Big Sample Slider</h2>
</div>
It is a big font in a short width div.
h2{
font-size: 69px
}
div{
width: 600px;
}
So it breaks up into 2 lines. Here is a JSfiddle: http://jsfiddle.net/6Qypv/
I would like to always target the second line in the sentence, no matter what the sentence is(It will be user generated), so I can wrap it in a span and give it some properties. I cannot change the HTML directly, though.
Thank you
You could try using the :first-line pseudo-element to do something like:
h2:first-line { /* Normal style */
font-size: 69px
}
h2 { /* Second line */
font-size:13px;
}
Sample fiddle

Placing a Line and Text over Regular Inline Text in HTML/CSS

I have a body of text in regular paragraph form that I'd like to annotate with footnotes, and I'd like it to be clear on what exact text from the paragraph the footnote is commenting, so I'd like to have a line (with endpoints/arrowheads/etc. if possible) over the text, with the footnote number in the center, like so:
<--- 1---> <------ 2 ------>
Hi, here's some text to annotate, isn't it so cool?
I appreciate any pointers on how to do this with HTML/CSS/JS, if it's even possible.
Here's one way you can do it:
.annotation {
border-top: dashed 2px black;
position: relative;
}
.annotation::after {
content: attr(data-footnote);
position: absolute;
left: 50%;
top: -1.15em;
}
<p><span data-footnote="1" class="annotation">Hi, here's</span> text to annotate, <span data-footnote="2" class="annotation">isn't it so cool?</span>
</p>
The <span> (or it could be an <a href> link to the footnote itself) wraps the text. By putting position: relative on it, that allows the child pseudo-element to be absolutely positioned relative to it.
Then the border and positioning takes care of the dashing and number. Arrowheads would be harder though.... maybe they can be done with a background image, but I haven't trued.
You'll want to make sure the line-height in the paragraph is big enough so the number above doesn't overlap with other lines of text.

How can I make only the actual text and not the background of a hyperlink in javascript be clickable?

I have an a href inside a div, and this link inside a table cell is multiline and text-align centered.
Now the whole cell becomes clickable, since the a href is filling the whole space except a little area closest to the border. I want the area around the link-text to not be clickable, and only the text.
this is the css:
tbody td.link a{
display: inline;
font-family: Arial;
font-size: 15px;
color: #545454;
position: absolute;
padding-top: 4px;
z-index: 10;
}
Since the z-index is 10, then the link is "closer to the user" than the background, and if I change this the whole link is disabled.
I also tried this, but without result:
$('td.link').find('a').click(function(ev){
ev.preventDefault();
ev.stopPropagation();
});
Summary: Is there a way to make only the actual text characters of a link clickable, and not the background?
THanks!
You cannot, because the actual text does not constitute an element. An element always contains some empty space around the characters (not to mention their inside). But you can limit the area occupied by the element. This may mean removing its padding, setting its line height to a smaller value (maybe 1), and changing a block element to an inline element. For more specific advice, you need to provide more specific information (HTML and CSS code).
I'm guessing you have a problem with the padding:4px
since padding is part of the element, it becomes clickable. I would suggest, using margin,
or padding on the parent element. (you could use box-sizing:border-box, to solve any sizing problems.)
You add this style
tbody td.rank a{
text-decoration:none;
}

Categories

Resources