Naor
Naor

Reputation: 24093

textarea immitation is not working well. any replacements?

I have this html and css: http://jsfiddle.net/b7rDL/6/

HTML:

<div class="text-block" contenteditable="true" spellcheck="false">Some Text</div>

css:

.text-block {
    resize: none;
    font-size:40px;
    border: none;
    line-height: 1;
    -moz-appearance: textfield-multiline;
    -webkit-appearance: textarea;
    min-width: 30px;
    overflow: visible;
    white-space: nowrap;
    display: inline-block;
}

This code allows me to write text with no width limit or height limit. It displays no scroll bar and it grows with the text. Those are basically the features I need.

  1. How can I convert this to regular textarea that will act the same? I want this to work on browser that doesn't implemented "contenteditable". Therefore I want to replace the div with textarea or other basiv element. How can I do it? (I don't mind using JavaScript).
  2. How can I disable the spellchecker? spellcheck=false doesn't work. In the example, when I focus on the text box, I get that buggy red line. I am using Firefox.
  3. How can I get rid of the border when I am focused? - SOLVED

I don't mind using JavaScript to solve those issues.

Any answer for those questions will help me.

Thanks

UPDATES:
@Oylex helped me with 3

Upvotes: 12

Views: 749

Answers (7)

dmi3y
dmi3y

Reputation: 3522

Best efficient way which was worked for me while I did something very close in the past was creating hidden out of flow div with the same exactly styles as the textarea has. And than setting out the timeout to update its html source based on information from textarea. This sounds bit scary but yet, after some testing and playing around nothing was better, that was already suggested, so just my variant.

http://jsfiddle.net/f2gAD/16/

and jQuery based script:

var textarea = $('textarea'),
    textBlock = $('div.text-block'),
    interval, value, freq = 10,
    doTextAreaAdjust = function(){
       textarea.css({
          height: textBlock.outerHeight(),
          width: textBlock.outerWidth()
       });
    };
doTextAreaAdjust();
textarea
.focus(function(){
    interval = window.setInterval(
        function() {
          value = textarea.val().replace(/(\r\n|\n|\r)/gm, '[rnnr]');
          value = value.replace(/</gm, '||'); // break html tags
          value = value.replace(/\[rnnr\]/gm, '<br>');
          value = value + '|'; // or <span>|</span> for better pixel perfect
          textBlock.html(value);
          doTextAreaAdjust();
        }, freq
    );console.log(interval);
})
.blur(function(){
    window.clearInterval(interval);
});
​

For performance wise did it as self starting/stopping timeout on focus/blur, though here is yet some workaround is required. While testing in Chrome noted that interval not properly stopped if you made blur by clicking on another tab. So probably replacement for self calling function into the setTimeout will be better.

It works more or less fine in IE 7-8 which suppose the main targets but still some text jumps time to time occur, while for others it is okay, but guess you will use editable feature for modern browsers. Would recommend use modernizer for its detection.

Upvotes: 0

tiffon
tiffon

Reputation: 5040

I was having trouble figuring out the bounds of the textarea's content, so with this approach I'm copying the content of the textarea into a similarly styled p element, which is set to float: left; and then resizing the textarea based on the size of the p. This handles both width and height.

I've tested on Mac 10.8.1 FF 18.0, Safari 6.0, Chrome 25.0.1362.0 canary, iOS Safari 6.0.1 and iOS Simulator 5.1 (272.21). I don't have a PC or IE handy.

http://jsfiddle.net/b7rDL/34/

HTML

<textarea id="tx" class="text-block" spellcheck="false"></textarea>
<p id="dupe" class="text-block"></p>

CSS

.text-block {
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
    resize: none;
    font-size:14px;
    border: none;
    line-height: 1;
    min-width: 30px;
    overflow: hidden;
    white-space: pre;
    display: block;
    outline: none;
    width: 30px;
    height: auto;
    border: 1px solid #ddd;
    background-color: #f7f7f7;
}

#dupe {
    float: left;
    display: none;
    width: auto;
    height: auto;
}

I added a background and border so I could see what's going on.

JavaScript

// no `var` so they are global and easier to work
// with in the inspector when using jsFiddle

$tx = $('#tx');
$dupe = $('#dupe');
lineH = Number($tx.css('line-height').replace('px',''));

update();

$tx.on('keydown', function() {
    setTimeout(update, 0);
});
$tx.on('propertychange input keyup change', update);

function update() {
    $dupe.text($tx.val());
    $tx.css({
        width: $dupe.width() + 7,
        height: $dupe.height() + lineH
    });
}

// not sure if this is needed, leaving it because
// I don't have many browsers to test on
 $tx.on('scroll', function() {
     tx.scrollLeft = 0;
     tx.scrollTop = 0;
 });

I'm adding extra space on the right and at the bottom because it seems to perform more consistently that way. Also, in the HTML, the wrap="off" is necessary for the version of Firefox, I'm using.

I got some good tips from this blog post.

Upvotes: 2

freethejazz
freethejazz

Reputation: 2285

#1

Working fiddle is here: http://jsfiddle.net/d9H9w/11/ (tested in IE8, Chrome and Firefox)

What you need is to set the width and height attributes as a user is typing within a text box.

Height

This is pretty straightforward:

  1. Get the content of the textarea
  2. Match for newline characters
  3. Set the height to total number of newline characters(plus one for the first line and 1.5 for wiggle room) in em's.

Setting the height in em's makes this font-size agnostic, so it'll work with multiple font-sizes.

function getNewlines(){
    // get the value(text) of the textarea
    var content = textEl.value;

    //use regex to find all the newline characters
    var newLines = content.match(/\n/g);

    // use the count of newlines(+1 for the first line + 1 for a buffer)
    // to set the height of the textarea.
    textEl.style.height = ((newLines && newLines.length || 0)+2.5)+'em';
};

Width

This is fairly easy, too, with one gotcha.

  1. Get the content of the textarea
  2. Split on newline characters to get an array consisting of lines of the textarea
  3. Sort to get the longest line
  4. Set the width to the length of the longest string in em's, multiplied by about .6(emRatio in my code), plus 2 ems for buffer space.

That last part is the kicker. The 'em' measurement is supposed to be a square representing the width and height that a single character takes up. This doesn't take kerning into account, so the height of a char is usually accurate, but the width is dependent on the chars around it. So, by guess and check, I figured that .6 em is about the average width of a character after kerning. .6 is pretty close, so I add 2 ems to the width for a bit of buffer space.

var emRatio = .6;
function longestLine(){
    // get the value(text) of the textarea
    var content = textEl.value;

    // split on newline's. this creates an array, where each item in
    // the array is the text of one line
    var a = content.split('\n');

    // use a sorting function to order the items in the array:
    // longest string to shortest string
    a.sort(function(a,b){return b.length - a.length});

    // use the longest string * the emRatio to set the width
    // Due to kerning, the letters aren't ever really 1em x 1em
    // So I'm multiplying by an approximate ratio here (guess and check)
    textEl.style.width = (a[0].length * emRatio + 2)+ 'em';
};

Existing problems with this implementation

  • To support resizing during long-held key presses, an onkeydown handler has to be included as well(this is not optimal for all cases that don't include long key presses)

All things considered, I think this fits what you need.

EDITS

Instead of having emRatio be .7, I changed it to .6 and added a buffer of 2 ems to the width. This addresses both issues @Naor mentioned in his comments.

I've updated the fiddle link and the Width section to reflect the changes.

Upvotes: 9

JSuar
JSuar

Reputation: 21091

EDIT 0

Request #1 Update

Working Solution: http://jsfiddle.net/7aeU2/

JQuery

$(function() {
    //  changes mouse cursor when highlighting loawer right of box
    $("textarea").mousemove(function(e) {
        var myPos = $(this).offset();
        myPos.bottom = $(this).offset().top + $(this).outerHeight();
        myPos.right = $(this).offset().left + $(this).outerWidth();

        if (myPos.bottom > e.pageY && e.pageY > myPos.bottom - 16 && myPos.right > e.pageX && e.pageX > myPos.right - 16) {
            $(this).css({ cursor: "nw-resize" });
        }
        else {
            $(this).css({ cursor: "" });
        }
    })
    //  the following simple make the textbox "Auto-Expand" as it is typed in
    .keyup(function(e) {
        //  the following will help the text expand as typing takes place
        while($(this).outerHeight() < this.scrollHeight + parseFloat($(this).css("borderTopWidth")) + parseFloat($(this).css("borderBottomWidth"))) {
            $(this).height($(this).height()+1);
        };
    });
});​

Request #2 Update

Also, here's a good explanation of why you can't outright disable spell check.

This does not belong to the realm of CSS (which is optional presentational suggestions). It is not about stylistic features of rendering data but about processing data interactively.

On browsers that support “spell checking” (which may involve grammar and style checks), the HTML attribute spellcheck or the corresponding IDL (DOM) attribute, settable in JavaScript, is effective.

In practice, those browsers tend to have “spelling checking” enabled by default for textareas only, and as textareas normally contain human language texts, turning it off does not sound useful. It is in any case user-controllable (the user can switch it off or select language).

via https://stackoverflow.com/a/9209791/1085891


Request #1

  1. Simple Solution is pretty straight forward.

    Working example: http://jsfiddle.net/b7rDL/12/

    JQuery

    $("#Solution0").keyup(function(e) {   
        while($(this).outerHeight() < this.scrollHeight) {
            $(this).width($(this).width()+50);
        };
    });
    

    HTML

    <textarea id="Solution0" rows="1" style="height: 1.2em;"></textarea>
    
  2. Fancier solution that will require some updating if you want the width, rather than the height, to expand. Still, it's pretty nice.

  3. Other solutions - I know these all expand height. Let me know if you need width implementation of one of the below solutions.

Request #2

spellcheck="true" should work as described in the Mozilla docs: Controlling spell checking in HTML forms. It works for me in my first simple example running in Firefox 13.0.1. What version are you running?

Upvotes: 4

SidPen
SidPen

Reputation: 180

Request #2

Working Demo

<body spellcheck="false">
<div class="text-block" contenteditable="true">    
Some Text SpellCheck</div>

Hi Naor, The only problem with this thing is it will disable the spellcheck for all the elements in the <body> tag. If it doesn't matter you then you can go with it.

Your question is really interesting and challenging really liked it. I hope this may help you..!!

Upvotes: 2

Mujtaba Hassan
Mujtaba Hassan

Reputation: 2493

Working here http://jsfiddle.net/H2GSx/

Code here: HTML:

<div style="overflow: scroll; width: 200px; height: 100px;">
    <div class="text-block" contenteditable="true" spellcheck="false">Some Text</div>
</div>​

CSS:

.text-block {
    resize: none;
    font-size:40px;
    border: none;
    line-height: 1;
    -moz-appearance: textfield-multiline;
    -webkit-appearance: textarea;
    min-width: 30px;
    overflow: visible;
    white-space: nowrap;
    display: inline-block;
}
​

Upvotes: -1

Oylex
Oylex

Reputation: 310

for #3, the css option you are looking for is: outline: none;

Upvotes: 3

Related Questions