How to change the text direction of an element? - javascript

On Facebook, for example, if you have chosen the Arabic language for your keyboard, the textbox automatically gets RTL direction.
How can I implement this on my web application? I don't know the method or property used.

You can use the CSS direction property to achieve this:
input{
direction: rtl;
}
Update
To change the direction of the text dynamically based on the user input you can check the first character of input to see if it meets a given criteria, in this case a regular expression.
$('input').keyup(function(){
$this = $(this);
if($this.val().length == 1)
{
var x = new RegExp("[\x00-\x80]+"); // is ascii
//alert(x.test($this.val()));
var isAscii = x.test($this.val());
if(isAscii)
{
$this.css("direction", "ltr");
}
else
{
$this.css("direction", "rtl");
}
}
});
This is a basic example that uses ltr direction for ascii text and rtl for everything else.
Here's a working example.

In HTML5 you can simply do this:
<input type="text" dir="auto" />
this automatically change the direction of input base on first character.
Hope it helps.
NOTE:
Edited: People say that it does not work in IE11.

it's best to detect text direction based on first character of the text. if the first one belongs to RTL language, then direction has to change.
example, a Persian text with English word in it.
به معنای واقعی tired هستم
another example, an English paragraph might contain a Persian word but the whole text has to be in LTR.
this word is used in different situations. the meaning of شیر is...
this function will check the first character typed in. if it belongs to a RTL language, then direction will change. This code supports all RTL languages.
function isRTL(str) {
var letters = [];
allRTL = new RegExp(
"^[\u0590-\u05fe\u0600-۾܀-ݎݐ-ݾހ-\u07be߀-\u07fe\u0800-\u083e\u0840-\u085e\u08a0-\u08fe\u0900-ॾ]|\ud802[\udf60-\udf7e]+$"
);
var cursor = 1;
for (var i = 0; i <= cursor; i++) {
letters[i] = str.substring(i - 1, i);
if(/\s/.test(letters[i])) {
cursor++;
}
if (allRTL.test(letters[i])) {
return true;
}
}
return false;
}
var dir = $("input[type=text]");
dir.keyup(function(e) {
if (isRTL(dir.val())) {
$(this).css("direction", "rtl");
} else {
$(this).css("direction", "ltr");
}
});
for more info visit this codepen.

For input element you can use Unicode intrinsic direction
input {
text-align: start;
unicode-bidi: plaintext;
}
Of course, it works with other elements such as div with contenteditable as true as mentioned by comments.

DISCLAIMER as stated by #bool3max: Many languages utilize Unicode characters and are written
LTR. Your function sets direction: rtl for all non-ASCII characters,
which isn't desired.
I have found also a Pen that does exactly the same without Regex. I have tweaked the code a bit to be compatible with multiple input fields and textareas.
It serves the purpose of using it between; for example; EN and AR languages.
$('input[type="text"], textarea').each(function () {
$(this).on('input keyup keypress', function () {
if (isUnicode($(this).val())) {
$(this).css('direction', 'rtl');
} else {
$(this).css('direction', 'ltr');
}
});
});
function isUnicode(str) {
var letters = [];
for (var i = 0; i <= str.length; i++) {
letters[i] = str.substring((i - 1), i);
if (letters[i].charCodeAt() > 255) { return true; }
}
return false;
}

Related

text search using jquery filter delay during backspace

I am using jquery filter to search for names in a big list of names. It works just fine with good speed when I type in the input field. But when I press backspace button to clear the search text there is a delay of more then 4s when there are about 2 or 3 characters left.
I have made a demo to explain my problem
<input type="text" class="search">
<div class="list"></div>
<script>
// Get the list div
let $list = $('.list');
// Form a list of numbers from 1 to 8000
let listHtml = ''
for (let i = 0; i < 8000; i++) {
listHtml += `<div class="list_item"><div class="list_item_value c${i}">${i}</div></div>`;
}
$list.html(listHtml);
// Get all the list items
$listItem = $list.find('.list_item');
$('.search').on('keyup', function(e) {
// Get the search text
let text = $(this).val();
$listItem.filter(function() {
$(this).toggle($(this).find(`.list_item_value`).text().includes(text));
});
});
</script>
I have simplified my problem with this demo by replacing the text search by number search.
One way to fix this problem would be by canceling the ongoing jquery filter process when a backspace is pressed. But I don't know how to do that. Please someone help me fix it.
Consider the following example.
$(function() {
function makeItems(n, t) {
for (var i = 1; i < n; i++) {
$(t).append("<div class='list_item'><div class='list_item_value " + i + "'>" + i + "</div>");
}
}
var $list = $('.list');
makeItems(8000, $list);
$('.search').keyup(function(e) {
var text = $(this).val().toLowerCase();
$(".filtered").removeClass("filtered");
var search = (text.length > 0);
if (search) {
$(".list_item", $list).filter(function(index) {
return $(this).text().indexOf(text) === -1;
}).addClass("filtered");
}
});
});
.filtered {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="text" class="search">
<div class="list"></div>
Not too much different from your code. Some reduction of code. When the Search is done, there is a condition to ensure it's not done on an empty value. Also we reset the view each time. Show, Hide, and Toggle have easing and callbacks, so they can be slower at times. Add/Remove class is very quick.
https://ryanpeden.com/javascript-indexof-vs-includes-performance/
So while includes will be a tiny, tiny amount slower because it has to check if you passed it a regex, in reality this will make no difference to how fast your code runs. You should use indexOf if you care about where the substring is in the original string. If you don’t care, just call includes because it makes the intent of your code more clear.
In the end, it's your call if you want to use includes or indexOf for your comparison.

Input Text format, Jquery, Javascript or Css

While Typing in a input First letter should change to Capital, But rest letters should be in lowercase, Even user Input capital letters it should changed it lowercase.
Example input word: mOuSe , ComPuTER
Important Point: While Typing it should change to: Mouse, Computer
I have tried many Stackflow soloution, But for above logic i found none.
Using Css: Click Here
Using Javacript: Click Here (This one worked but for last letter it doesnt works)
Any solution is accepted using Jquery, Javascript or Css
$(document).ready(function() {
//alert('ready');
$('input').on('keypress', function(event) {
var $this = $(this),
val = $this.val();
val = val.substr(0, 1).toUpperCase() + val.substr(1).toLowerCase();
$this.val(val);
});
});
Use the input event instead of the keypress event.
$('input').on('input', function(event) {
function initCap(t){
t.value=t.value.toLowerCase();
t.style='text-transform:capitalize;'
}
call the method onkeypress and onblur
Try This
$('input').on('keyup', function(event) {
var firstchar = $(this).val().substring(0,1);
var remainchar = $(this).val().substring(1,$(this).val().length).toLowerCase();
$(this).val(firstchar + remainchar);
});
$('input').on('keydown', function(event) {
var firstchar = $(this).val().substring(0,1);
var remainchar = $(this).val().substring(1,$(this).val().length).toLowerCase();
$(this).val(firstchar + remainchar);
});
DEMO
It might be interesting to point out that CSS text-transform: capitalize; only alters the display of the input, while Javascript modifies the actual HTML value. This matters if the input is, for example, a CAPTCHA response or form input field that needs to be validated.
With pure Javascript, you could split the input into separate words & transform each one of them, like so:
HTML
<div id="transform">mOuSE, cOmPUTEr</div>
<button type="button">Capitalize</button>
JS
var rgx = /,/; /* word splitter, can be anything */
var target = document.getElementById('transform');
document.getElementsByTagName('button')[0].onclick = function() {
var strArr = target.innerHTML.split(rgx); /* split the input */
for (var word = 0; word < strArr.length; word++) { /* iterate over each word */
strArr[word] = strArr[word].toLowerCase().trim();
var firstLetter = strArr[word].substr(0,1).toUpperCase();
var otherLetters = strArr[word].substr(1, strArr[word].length)
strArr[word] = firstLetter+otherLetters;
}
target.innerHTML = strArr.join();
}
Demo: http://jsbin.com/farib/2/edit
NB: If your input is in a <textarea> or <input>, replace .innerHTML with .value.
Try this css solution.
DEMO
input {
text-transform:capitalize;
color: blue;
}
$(document).ready(function(){
$("input").on("keypress",function(){
$(this).val($(this).val().toLowerCase());
});
})

Change text direction (ltr and rtl) of wysiwyg on fly

I use nicEdit as wysiwig editor on a website in Persian language.
The text of the contents are sometimes mix of Persian and English words,
Persian is an right to left language, so the default direction of the editor is right to left. Then when one types an English word in the middle of the text the rtl directionality applies on it and causes misreading
for example
C# will be written as #C
a solution for it is to surround the word (for example C#) with an span tag which its direction be ltr C#
but how can i achieve it?
I thought in the editor keypress event, check if the entered key is English replace it with the span mentioned above, but I dont know how to do it
in other words you can think of this problem as changing the background color of the typed text on fly
in the following I wrote what I am trying however it doesnt work yet
$('body').on('keypress','.nicEdit-main', function(e) {
var c= String.fromCharCode(e.which);
var isWordcharacter = c.match(/\w/);
if (isWordcharacter && !en)
{
en = true;
nicEditor.nicCommand('insertHTML', '<span dir="ltr" style="direction:ltr; background-color:#eee">');
}
else if (!isWordcharacter)
{
en = false;
// need to close or escape the current span or create a new one with the opposite direction but nor works
nicEditor.nicCommand('insertHTML', '<span dir="rtl" style="direction:rtl;>');
}
});
the problem is in the else I should escape the current span
I prepared small fiddle: http://jsfiddle.net/br8qt320/
This is pretty rough example that shows different background for letters and numbers. You need to fine-tune it a little bit but it should work quite well. Note that I do not know NicEditor and maybe there is a better approach for this library.
Code from fiddle:
$(document).ready(function() {
nicEditors.allTextAreas();
var ltrSpan = $('<span style="background-color:#00ff00">');
var rtlSpan = $('<span style="background-color:#ff0000">');
var currentSpan = null;
var isLtr = true;
$('body').on('keydown','.nicEdit-main', function(e) {
var editorArea = $(this);
var currentChar = String.fromCharCode(e.keyCode || e.which);
var isNumeric = currentChar.match(/\d/);
if (isNumeric) {
if (!isLtr) {
isLtr = true;
currentSpan = ltrSpan.clone();
currentSpan.appendTo(editorArea);
}
} else {
if (isLtr) {
isLtr = false;
currentSpan = rtlSpan.clone();
currentSpan.appendTo(editorArea);
}
}
currentSpan.append(currentChar);
return false;
});
});

disable textarea by height not characters

So many times we want to limit how much a user can write, but here I have a special sized box that it has to fit in, so I want to disable adding more characters if it would surpass a specific height. here is what I did:
var over;
$('textarea').keypress(function(e){
var key = e.charCode ? e.charCode : e.keyCode ? e.keyCode : 0;
var t = $(this).val();
jQuery('<div/>', {
style: "visibility:hidden",
text: t,
id: "test"
}).appendTo('body');
var h = $('#test').height();
if(h >= 100){
over = true;
}
else{
over = false;
}
if(over){
//code goes here
}
$('#test').remove();
});
I got the limiting code (what goes where I have the "code goes here" comment) from here and it ALMOST works.
There is only one problem:
if somebody copies and pastes, it can place multiple characters and therefore still go over the limit.
How can I fix this issue?
jsfiddle
Another somewhat hacky solution could be to check scroll on key up. If scroll exists, delete the last character while scroll exists:
function restrictScroll(e){
if(e.target.clientHeight<e.target.scrollHeight){
while(e.target.clientHeight<e.target.scrollHeight){
e.target.value = e.target.value.substring(0, e.target.value.length-1);
}
}
};
document.getElementById("textareaid").addEventListener('keyup', restrictScroll);
This would work as you type and if you paste blocks of text. Large text blocks may take a little longer to loop through though. In which case you may want to split on "\n" and remove lines first, then characters.
jsfiddle
If you want your function to fire whenever the text in your field changes, you can bind it to the input and propertychange events, as per this answer:
https://stackoverflow.com/a/5494697/20578
Like this:
$('#descrip').on('input propertychange', function(e){
This will make sure your code fires when e.g. the user pastes in content using the mouse.
As for stopping them from entering content that would go over the limit, I think you have to keep track of what content they've entered yourself, and then revert their last edit if it infringed your criteria.
Note that e.g. Twitter doesn't stop the user from entering more characters if they've gone over the limit - they just tell the user they're over the limit, and tell them when they're back under. That might be the most usable design.
You may try this:
$('#descrip').bind('paste',function(e) {
var el = $(this);
setTimeout(function() {
//Get text after pasting
var text = $(el).val();
//wath yu want to do
}, 100);
};
Jsfiddle
The solution is taken from here and here. It works by binding to the paste event. Since paste event is fired before the text is pasted, you need to use a setTimeout to catch the text after pasting. There is still some rough edges (i.e. if you select text and press backspace, it does not update).
Still, Spudley comment has some valid points, that you may want to consider.
Edit:
Note on the jsfiddle: It allow you to go over the limit when pasting, but once over the limits, you cannot paste (or type) more text until you go under the limit again.
Must be taken into account that, since you are limiting the text length by the size it ocuppies after rendering (wich have it own issues as pointed by Spudley), and not a defined lenth, you can know if a text fits or not, but not know how much of the text is inside the limits, and how much is out of them.
You may consider reseting textbox value to its previous value if pasted text makes imput go over the limit, like in this one.
However, for cutting down the text after pasting so as non-fitting text is left out, but the rest of the pasted text is allowed, you need an entirely different approach. You may try iterating over the text until you find how much of the new text is enough.
By the way, line feeds and seems to cause your original script to behave weirdly.
I've been able to get the program working:
var over = false;
var num = 0;
var valid_entry = "";
$('#descrip').keyup(function(e){
var key = e.charCode ? e.charCode : e.keyCode ? e.keyCode : 0;
var t = $(this).val();
getMaxRow(t,key,this);
});
function getMaxRow(t,key,obj) {
jQuery('<div/>', {
"class": "hide",
text: t,
id: "test"
}).appendTo('body');
var h = $('#test').height();
$('#test').remove();
if(h >= 100){
num += 1;
if(num == 1){
var div = '<div id="message">You have run out of room</div>'
$('body').append(div);
}
over = true;
}
else{
over = false;
valid_entry = t;
if(num >= 1){
$('#message').remove();
num = 0;
}
}
if( over ) {
//Do this for copy and paste event:
while ( over ) {
//Try using a substring here
t = t.substr(0,(t.length-1));
over = getMaxRow(t,key,obj);
}
}
$(obj).val(valid_entry);
return over;
}

javascript catch paste event in textarea

I currently have a textarea which I requires control over text that has been pasted in,
essentially I need to be able to take whatever the user wants to paste into a textarea and place it into a variable.
I will then work out the position in which they pasted the text and the size of the string to remove it from the textarea,
Then at the end deal with the text thats is in the variable in my own way.
My question: how would I go about getting a copy of the text in a variable that was just pasted in by the user?
Thanks.
I answered a similar question a few days ago: Detect pasted text with ctrl+v or right click -> paste. This time I've included quite a long function that accurately gets selection boundaries in textarea in IE; the rest is relatively simple.
You can use the paste event to detect the paste in most browsers (notably not Firefox 2 though). When you handle the paste event, record the current selection, and then set a brief timer that calls a function after the paste has completed. This function can then compare lengths to know where to look for the pasted content. Something like the following:
function getSelectionBoundary(el, start) {
var property = start ? "selectionStart" : "selectionEnd";
var originalValue, textInputRange, precedingRange, pos, bookmark, isAtEnd;
if (typeof el[property] == "number") {
return el[property];
} else if (document.selection && document.selection.createRange) {
el.focus();
var range = document.selection.createRange();
if (range) {
// Collapse the selected range if the selection is not a caret
if (document.selection.type == "Text") {
range.collapse(!!start);
}
originalValue = el.value;
textInputRange = el.createTextRange();
precedingRange = el.createTextRange();
pos = 0;
bookmark = range.getBookmark();
textInputRange.moveToBookmark(bookmark);
if (/[\r\n]/.test(originalValue)) {
// Trickier case where input value contains line breaks
// Test whether the selection range is at the end of the
// text input by moving it on by one character and
// checking if it's still within the text input.
try {
range.move("character", 1);
isAtEnd = (range.parentElement() != el);
} catch (ex) {
log.warn("Error moving range", ex);
isAtEnd = true;
}
range.moveToBookmark(bookmark);
if (isAtEnd) {
pos = originalValue.length;
} else {
// Insert a character in the text input range and use
// that as a marker
textInputRange.text = " ";
precedingRange.setEndPoint("EndToStart", textInputRange);
pos = precedingRange.text.length - 1;
// Delete the inserted character
textInputRange.moveStart("character", -1);
textInputRange.text = "";
}
} else {
// Easier case where input value contains no line breaks
precedingRange.setEndPoint("EndToStart", textInputRange);
pos = precedingRange.text.length;
}
return pos;
}
}
return 0;
}
function getTextAreaSelection(textarea) {
var start = getSelectionBoundary(textarea, true),
end = getSelectionBoundary(textarea, false);
return {
start: start,
end: end,
length: end - start,
text: textarea.value.slice(start, end)
};
}
function detectPaste(textarea, callback) {
textarea.onpaste = function() {
var sel = getTextAreaSelection(textarea);
var initialLength = textarea.value.length;
window.setTimeout(function() {
var val = textarea.value;
var pastedTextLength = val.length - (initialLength - sel.length);
var end = sel.start + pastedTextLength;
callback({
start: sel.start,
end: end,
length: pastedTextLength,
text: val.slice(sel.start, end),
replacedText: sel.text
});
}, 1);
};
}
window.onload = function() {
var textarea = document.getElementById("your_textarea");
detectPaste(textarea, function(pasteInfo) {
var val = textarea.value;
// Delete the pasted text and restore any previously selected text
textarea.value = val.slice(0, pasteInfo.start) +
pasteInfo.replacedText + val.slice(pasteInfo.end);
alert(pasteInfo.text);
});
};
You might now use FilteredPaste.js (http://willemmulder.github.com/FilteredPaste.js/) instead. It will let you control what content gets pasted into a textarea or contenteditable and you will be able to filter/change/extract content at will.
A quick search shows me that there are different methods for different browsers. I'm not sure if jQuery has a solution. Prototype.js does not appear to have one. Maybe YUI can do this for you?
You can also use TinyMCE, since it does have a gazillion of different event triggers. It is a full fledged word processor, but you can use it as plain text if you want. It might be a bit too much weight to add though. For example, upon initiation, it turns your <textarea> into an iFrame with several sub. But it will do what you ask.
--Dave

Categories

Resources