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"
Related
I'm creating an HTML/CSS application, and I'm a bit stuck.
Let's say that I have 2 elements positioned next to eachother display: inline-block
Every element has again a couple of elements which are placed next to eachother.
See the following illustration that tries to explain it:
So, the image below describes 3 different levels of elements:
Level 1: Red - Outer element
Level 2: Yellow - Wrapper element
Level 3: Green - Content
In HTML, this could be constructed writting like the following:
<ul id="holder">
<li>
<div>
<div class="col">Col 1</div>
<div class="col">Col 2</div>
</div>
</li>
<li>
<div class="col">Col 1</div>
<div class="col">Col 2</div>
</li>
</ul>
The UL represents the red element, the LI represents the yellow elements and the DIV elements represents the green elements.
Now, let's say that our red element has a fixed width and I place the overflow on hidden. This means that when I resize the page, the elements on the right dissapear when they don't fit the page.
But here the problem arizes, when I do resize the window, and the window becomes too small to render everything, immediately, the latest LI element is not visible on the screen anymore.
Is there any CSS way to make sure that no the LI element are hidden but the DIV elements inside the LI? When both DIV elements are hidden, off course the LI element can be hidden aswell since it's empty?
If there's no CSS way to do this, anyone minds putting me in the right direction by using JavaScript or something else?
Here's a jsFiddle to explain it a bit more.
Kind regards
The li disappears from view because it's in display: inline-block.
As soon as the window isn't wide enough, it moves below the first li.
You can see this happen if you release the #holder's height (height:auto).
The solution is to add white-space : nowrap to force the li to stay in one line.
Updated fiddle :
https://jsfiddle.net/zLqfe4z8/4/
It's not a problem of your elements hiding because there's not enough width, it's because they're wrapping because there's not enough width (and then being hidden by the overflow: hidden).
You can see this happening if you remove the height constraint on your wrapper:
#holder { overflow: hidden; border: 1px solid red; }
The fix is simple, stop it from wrapping using white-space: nowrap:
#holder { white-space:nowrap; overflow: hidden; border: 1px solid red; height: 52px; }
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.
I have a problem when focusing on invisible elements. Markup:
<div id="outer">
<div id="inner" style="top: 0;">
<div class="group" style="background: red;">X</div>
<div class="group" style="background: blue;">Y</div>
<div class="group" style="background: green;">Z</div>
</div>
</div>
<button onclick="document.getElementById('inner').style.top = '-182px';">Down</button>
And css:
#outer {
width: 300px;
height: 182px;
overflow: hidden;
background: black;
}
#inner { position: relative; }
.group { width: 300px; height: 182px; background: red;}
When I press 'down' button inner's style become 'top: -182px', which functionally just shows another group of items. And component basically works as a vertical group slider. Everything works perfectly until I'm using 'Tab'. Just before slider show next group browser automatically shifts inner div without changing any attributes on it.
Is there any change to get offset made by browser from DOM or anywhere else? I know that scrolling on focus is default browser functionality so I am not going to fight with browsers and I am pretty sure I can't disable such scrolling.
I've figured out that browser scrolls before any js code executes. Is there any way to intercept focus event and scroll before browser?
Why not use a bit more JS?
I've forked your JSfiddle to use raw JS, though I would use jQuery to make it much cleaner.
http://jsfiddle.net/bldoron/wMXpt/4/
Basically I would traverse the elements and hide the irrelevant ones explicitly instead of implicitly with css rules.
You need to check if we passed the last node, but you get the picture.
Take a look at this: http://jsfiddle.net/byTLg/8/. It works! Fiddle will explain much more than me )
I have a simple blog page - a list of posts that each consist of a title and contents. When the page loads I want all posts' contents hidden until their titles are clicked. The following code accomplishes this but with an unwanted side effect - the on-page-load hide() function that hides each post's content also hides the background of the containing (id="content") div:
Relevant JavaScripts:
$(document).ready(function() {
$(".blog_post p").hide();
//BLOG CONTENT ANIMATION
$('.blog_post').click(function() {
$(this).find('p').slideToggle(130);
});
});
Summary of blog page:
<section class="grid_7">
<div id="content">
<div class="blog_post">
<div class="blog_head">
<h2>Title</h2>
</div>
<p>Contents</p>
</div>
</div>
</section>
Relevant CSS:
section {
border: 1px solid white;
}
#content {
margin: 20px;
background-image:url('../images/content_background.jpg');
}
When the page loads the list of titles displays without the #content parent div's background. However when I click on a post's title the #content div's background shows up behind all posts up to and including that one.
Any idea what's going on?
It sound like you have some CSS that applies to the blog_head elements, that makes them float, for example:
.blog_post { float: left; }
In that case, the reason that the background doesn't show up is that the height of the content div is zero. A floating element doesn't affect the size of its parent, and when the content div only contains the headers, the height becomes zero. The background is still there, but there is no area where it's visible.
Add an overflow to the content div, that will make it contain its children:
#content { overflow: hidden; }
Note that this will not hide anything as long as you don't specify a size for the content element, it will just change how it's rendered so that it will become a container for its children.
A bit of a stab in the dark: Your #content div will, of course, be a lot shorter as the blog posts aren't there, basically consisting just of the divs with the titles. Perhaps that's the problem.
Does the image have a blank (or subtle) bit at the top or something, so that it's only apparent that it's there when there's more content in the #content div (e.g., when it's taller)? Or is there some other reason you can see that when #content is really short, you wouldn't see the background on the part of it that's there? (You can use the debugging tools in most modern browsers to see what the dimensions of the #content div are when the paragraphs are hidden; or slap a border on it temporarily, but tools these days are pretty good.)
Basically, since the jQuery doesn't, of course, actually hide the background, it must be a side-effect of the paragraphs being hidden — because of the effect that has on the dimensions of the #content div.
This is working fine for me:
HTML:
<div class="blog_post">
<div class="blog_head">
<h2>Title</h2>
</div>
<p>Contents</p>
</div>
</div>
CSS:
section {
border: 1px solid white;
}
#content {
margin: 20px;
background-image:url('http://bluebackground.com/__oneclick_uploads/2008/04/blue_background_03.jpg');
}
JS
$(document).ready(function() {
$(".blog_post p").hide();
//BLOG CONTENT ANIMATION
$('.blog_post').click(function() {
$(this).find('p').slideToggle(130);
});
});
Check it live here: Jsfiddle example
I have three divs with display: inline-block. In every div i have div with display: none when im trying to show hiding div with $('#div-id').show(1000) nearest divs 'jump around'
What should i change? I do like to see div under div just draw and the left or right div doesn't change his place.
For example two divs with my problem there (hide div shows up onchange in the textbox)
http://jsfiddle.net/WZCJu/13/
I added this CSS:
#amount-div, #specific-div {
width: 300px;
vertical-align: top
}
Version without the width, you may like it better:
http://jsfiddle.net/WZCJu/15/
Try using css's visibility property instead since it retains the element's position in the flow.
Docs: http://www.w3schools.com/css/pr_class_visibility.asp
Example:
<div id="herp" style="width: 100px; height: 40px; visibility: hidden;">plarp</div>
<div id="derp" style="width: 100px; height: 40px; visibility: visible;">slarp</div>
If you change the divs to use float: left; with a specified width you can avoid the "jump around".
See my updated example at: http://jsfiddle.net/WZCJu/12/
I changed the following:
<div id="amount-div" style="display:inline-block;">
...
<div id="specific-div" style="display:inline-block;">
To use floats with a specified width.
<div id="amount-div" style="float:left;width:220px;">
...
<div id="specific-div" style="float:left;width:220px;">
I also changed the <br> tag which preceeds the submit button so that it will clear the floated divs like so (though, there are better ways of handling that in my opinion):
<br style="clear:both">
display none removes the element completely from the document. there wont be any space reserved for it. so when u bring it back(show) it ll rearrange the nearby divs. so try using visibility:hidden which will retain the space but keep the div hidden..
Changing an HTML element from display: none to display: block or some other value will always cause it to change the flow of other elements around it in the tree. To prevent the DIVs from jumping around, you have a few options. Here are a couple simple ones:
First, you could "pad" the DIV in another DIV with a fixed size. For example:
<div style="width: 100%; height: 2em;">
<div id="js-amount" style="display: none">
<p>You will achieve this goal by:</p>
<p id="achieved-date"> <p>
<p id="weekly-limit-amount">Your weekly limit will be decreased by $100</p>
</div>
</div>
Secondly, you could use absolute positioning to remove your DIV from the flow of the document:
<div id="js-amount" style="display: none; position: absolute; top: 200px; left: 50px;">
<p>You will achieve this goal by:</p>
<p id="achieved-date"> <p>
<p id="weekly-limit-amount">Your weekly limit will be decreased by $100</p>
</div>
You must set a fixed size for your divs, so when the new one appears, it's constrained with the given side. I updated your JSFiddle : http://jsfiddle.net/WZCJu/16/
Have a look at how I constrain the size for your divs in the CSS. To improve layout, I took the liberty to add some styling to the submit button, so the HTML is a little bit modified too.
If you have any trouble understanding my solution, ask some questions.
When using display: none, the element does not render at all so it doesn't use any space on the rendered web page. I think you might want to use visibility:hidden to hide your element but still make the space usage calculation.
EDIT: It appears jQuery method works only on the display style so my answer is not applicable and indeed a fixed offset is necessary to avoid side effects in the page flow.