I am using two text areas. Project is about online typing test. I used two text area. First textarea contains the matter to be typed in second textarea. For calculating the the net typing speed I need a javascript diff algorithm.
Javascript Diff Algorithm algo fits my all requirements..which uses this
jsdiff.js
javascript file for differencing of two strings. and
JS Diff Demo
is a demo which uses the same javascript file...You should have look of this demo. But I how can I know count correct words typed? Trouble is that the javascript file provided is not using any comments nor gives any documentation.
I'm not sure if you need much more explanation than the comment I placed above. I like the diff-highlighting your link shows, but if all you're after is counting the diffs, why does something like this not work? http://jsfiddle.net/vSySu/
var arr1 = $('#text1').val().split(' ');
var arr2 = $('#text2').val().split(' '); // split on whatever string or regex you want.
var diffs = 0;
for (var i = 0; i < arr1.length; i++) {
if (arr1[i] !== arr2[i]) {
diffs++;
}
}
alert(diffs);
You could use a combination of a lenvenshtein algorithm to find the accuracy, and some basic string manipulation to count the words that are different. This can be improved but you get the idea:
function wordAccuracy(str1, str2) {
var len = str1.length,
distance = levenshtein(str1, str2),
words1 = str1.split(' '),
words2 = str2.split(' ');
return {
accuracy: 100 - (0|(distance * 100) / len) +'%',
fail: words1.filter(function(word, idx){
return word != words2[idx];
}).length
}
}
// Example:
var str1 = 'Lorem ipsum dolor sit amet consectetur adipiscing elit';
var str2 = 'Lorme ipsmu dolor sit maet cnsectetur adipiscing elot';
console.log(wordAccuracy(str1, str2));
//^ {
// accuracy: '86%'
// fail: 5
// }
Related
I have a code element, and I know the text I'm looking for is inside it, for example:
<p>
Lorem ipsum <span class="bold">dolor</span> sit amet
</p>
Note the span that is used for styling specific words.
Now, assume I have a reference to the p element, and I want to programmatically mark the ipsum dolor sit part, how can achieve that?
You can use the Selection API with a Range argument to programmatically select text in an element.
The Range start and end positions accept a Child Node number, or Character inside a Text node. In our case, we need to reach the Text nodes to direct to the text position inside them (in our example, it will start on the first Text node of p, in position 11, and will end on the last Text in position 4).
To find the right node and the text position inside it, use the next function:
const findPositionInsideTree = (node, position) => {
if (node.nodeType === Node.TEXT_NODE) {
return { node, position };
}
for (let child of node.childNodes) {
if (position <= child.textContent.length) return findPositionInsideTree(child, position);
position -= child.textContent.length;
}
};
This recursive code loops over the child nodes and counts the expected position inside each node.
And now you only need to call this function for your text, create a Range and add it to the Selection:
const textStart = element.textContent.indexOf('ipsum dolor sit');
const textEnd = textStart + 'ipsum dolor sit'.length;
const start = findPositionInsideTree(element, textStart);
const end = findPositionInsideTree(element, textEnd);
const range = new Range();
range.setStart(start.node, start.position);
range.setEnd(end.node, end.position);
window.getSelection().removeAllRanges()
window.getSelection().addRange(range)
Maybe you can use this:
const text = pElement.textContent;
const words = text.split(" ");
const startIndex = words.indexOf("ipsum");
const spanElement = document.createElement("span");
spanElement.classList.add("bold");
spanElement.textContent = words.slice(startIndex, startIndex + 3).join(" ");
pElement.innerHTML = words.slice(0, startIndex).join(" ") + spanElement.outerHTML + words.slice(startIndex + 3).join(" ");
I wastry to add three dots after 130 characters of string which grabbed from a DIV through the JavaScript innerHTML method. Inside the innerHTML may have more tags and attributes which need to skip while counting. Also need to keep and re-assign the truncated HTML into the same DIV after the operation completed.
Here is some sample input string and expected outputs -
Input 1:
<p>There are many <i>variations</i> of passages of <b>Lorem Ipsum</b> available, but the majority have suffered alteration in some form, by injected humour, or randomised words that don't look even slightly believable.</p>
Output 1:
<p>There are many <i>variations</i> of passages of <b>Lorem Ipsum</b> available, but the majority have suffered alteration in some form, by injecte...</p>
input 2:
<p><span class="header3">There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words that don't look even slightly believable.</span></p>
output 2:
<p><span class="header3">There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injecte...</span></p>
Input 3:
<h4><span class="santral-pullquote-32"><span class="body-regular">There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words that don't look even slightly believable.</span></span></h4>
Output 3:
<h4><span class="santral-pullquote-32"><span class="body-regular">There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injecte...</span></span></h4>
Using the following function Input type 1 is only working but not others:
function cutString = (str) => {
let stringCount = 0;
let keepCounting = true;
let ans = '';
let openTags = [];
for (let i = 0; i < str.length; i++) {
if (str[i] == "<") {
keepCounting = false;
if (str[i + 1] != `/`) openTags.push(str[i + 1])
continue;
}
if (str[i] == ">") {
keepCounting = true;
if (str[i - 2] == `/`) openTags.splice(openTags.indexOf(str[i - 2]), 1)
continue;
}
if (keepCounting) stringCount++
if (stringCount == 131) {
ans = str.slice(0, i);
break;
}
}
openTags.forEach(tag => ans = ans + `…</${tag}>`);
return ans;
}
I'm using pure JavaScript (no jQuery).
Any help would be greatly appreciated!
Thanks in advance.
you can try this. Note that this code updates the html directly, if you wish to keep the original content, clone the node you want to play with, and use the cloned version to run the trim:
function map_node(f_objNode,f_currLimit){
for(var i=0;i<f_objNode.childNodes.length;i++){
var l_node = f_objNode.childNodes[i];
if(f_currLimit == 0){ //max length exceeded, remove node
l_node.remove();
i--; //move index backwards to account for the deleted node
continue;
}else if(l_node.nodeType == 3){ //textnode
var l_strText = l_node.nodeValue;
if((f_currLimit - l_strText.length) < 0){ //the text length
//exceeds the limit
//trim and 0 the limit
l_node.nodeValue = l_strText.substring(0,f_currLimit) + '...';
f_currLimit = 0;
}else{ //max length is below the text length, update the limit
f_currLimit -= l_strText.length
}
}
//process the children of the node,
//you can add check here to skip the call if no children
f_currLimit = map_node(l_node,f_currLimit);
}
return f_currLimit
}
//this is how you use it.
function test(){
var l_textLimit = 100; //your limit
var l_div = document.getElementById("test-div"); //your node
var l_data = map_node(l_div,l_textLimit); //parse the shit out of it
//not really used, but if you replace the
//the limit integer with {} you can add postprocessing
console.log(l_data)
}
as a side note, be aware of parsing as a way to tokenize html. It certainly has its uses, but it can get pretty complicated if you want to maintain the structure of the node. In such cases, it is easier and more efficient to roll with the DOM directly. Going that direction also has issues- larger DOM (sub)trees are not ideal target for recursive processing, so you need to pick your approach for the concrete context.
This is start of a srt file:
0
00:00:07,000 --> 00:01:00,000
lorem ipsum... // this line doesn't work
1
00:01:02,960 --> 00:01:05,800
lorem ipsum...
2
00:01:05,840 --> 00:01:08,960
lorem ipsum...
The first line doesn't work, I suppose because of0 as the ordinal.
I need a way, javascript way if possible, to correctly change all ordinal numbers at once, not one by one (over 1000 lines), starting with 1 and not with 0.
I was searching for various online solutions, without success.
You could create an html file with a textarea, load it in your browser and copy
the contents of the .srt file in. Use the following javascript to convert the textarea's text:
var numberRegex = /^\d\s*$/;
var originalLines = text.split('\n');
var lines = [];
for (var index = 0; index != originalLines.length; ++index)
{
var orig = originalLines[index];
var match = numberRegex.exec(orig);
lines.push(match ? parseInt(orig) + 1 : orig);
}
Then the converted text you want is provided by:
lines.join('\n')
Trying to use javascript to make a game/script that:
- each number from 0-20 equal a specific word. Ex: 0=saw, 1=tie, 2=noah.
- a number pops up on the screen and the user needs to type in the word that = that number.
- Then the program says if I was correct or not and adds to my points.
Demo: http://jsfiddle.net/elclanrs/gfQsP/
var points = 0;
function game() {
var words = 'lorem ipsum dolor sit amet consecteur adipisci elit'.split(' ');
var rand = -~(Math.random() * words.length);
var result = prompt('Word number '+ rand +'?');
var match = words[rand-1] === result.toLowerCase();
alert( match ? 'Good' : 'Bad');
match && points++;
}
well this can be easy:
just make a object containing key value pairs like this:
var obj = {
key1: value1,
key2: value2
};
Then ask the player the value of particular number. Loop through this object to scan the value of that particular number to match the value as entered by the player. if found increment the points counter.
Its pretty straight forward.
What I would like is to count the number of lines in a textarea, e.g:
line 1
line 2
line 3
line 4
should count up to 4 lines. Basically pressing enter once would transfer you to the next line
The following code isn't working:
var text = $("#myTextArea").val();
var lines = text.split("\r");
var count = lines.length;
console.log(count);
It always gives '1' no matter how many lines.
The problem with using "\n" or "\r" is it only counts the number of returns, if you have a line that is long it could wrap and then it wouldn't be counted as a new line. This is an alternative way to get the number of lines - so it may not be the best way.
Edit (thanks alex):
Script
$(document).ready(function(){
var lht = parseInt($('textarea').css('lineHeight'),10);
var lines = $('textarea').attr('scrollHeight') / lht;
console.log(lines);
})
Update: There is a much more thorough answer here: https://stackoverflow.com/a/1761203/145346
If you are just wanting to test hard line returns, this will work cross platform:
var text = $("#myTextArea").val();
var lines = text.split(/\r|\r\n|\n/);
var count = lines.length;
console.log(count); // Outputs 4
I have implemented the lines and lineCount methods as String prototypes:
String.prototype.lines = function() { return this.split(/\r*\n/); }
String.prototype.lineCount = function() { return this.lines().length; }
Apparently the split method will not count a carriage return and/or newline character at the end of the string (or the innerText property of a textarea) in IE9, but it will count it in Chrome 22, yielding different results.
So far I have accomodated for this by subtracting 1 from the line count when the browser is other than Internet Explorer:
String.prototype.lineCount = function() { return this.lines().length - navigator.userAgent.indexOf("MSIE") != -1); }
Hopefully someone has a better RegExp or another workaround.
user \n instead of \r
var text = $("#myTextArea").val();
var lines = text.split("\n");
var count = lines.length;
console.log(count);
However this is working if you need use it because it respond to your problem
let text = document.getElementById("myTextarea").value;
let lines = text.split(/\r|\r\n|\n/);
let count = lines.length;
console.log(count);
What about splitting on "\n" instead?
It will also be a problem where one line wrapped to 2 lines in the textarea.
To do it accurately like this, you could use a fixed height font and measure pixels. This could be problematic though.
This function counts the number of lines which have text in a textarea:
function countLine(element) {
var text = $(element).val();
var lines = text.split("\n");
var count = 0;
for (var i = 0; i < lines.length-1; i++) {
if (lines[i].trim()!="" && lines[i].trim()!=null) {
count += 1;
}
}
return count;
}
Counting the newlines is not a reliable way for finding the number of lines, since long text could simply break and still only count as a single line.
What you want to do, is find out the scrollHeight of the textarea and divide it by the height of a single line.
This is answered in detail here:
https://stackoverflow.com/a/1761203/9863305
I've used the original answer of Mottie but some functions were changed in the JQuery API. Here is the working function for the current API v3.1.0:
var lht = parseInt($('#textarea').css('lineHeight'),10);
var lines = $('#textarea').prop('scrollHeight') / lht;
console.log(lines);
All tumbs up for Mottie's answer!
This will aim to consider lines with both hard and soft returns:
//determine what the fontsize will be
let fontsize = 12;
//get number of characters that can fit in a row
let charsperrow = textarea.clientWidth / fontsize;
//get any hard returns
let hardreturns = textarea.textContent.split(/\r|\r\n|\n/);
let rows = hardreturns.length;
//loop through returns and calculate soft returns
for(let i = 0,len = rows; i < len; i++){
let line = hardreturns[i];
let softreturns = Math.round(line.length / charsperrow);
//if softreturns is greater than 0, minus by 1 (hard return already counted)
softreturns = Math.round(softreturns > 0 ? (softreturns - 1) : 0);
rows += softreturns;
}
console.log(Math.round(rows));
The normal newline character is "\n". The convention on some systems is to also have "\r" beforehand, so on these systems "\r\n" is often found to mean a new line. In a browser, unless the user intentionally enters a "\r" by copying it from somewhere else, the newline will probably be expressed as just "\n". In either case splitting by "\n" will count the number of lines.
<html>
<head>
<script>
function countLines(theArea){
var theLines = theArea.value.replace((new RegExp(".{"+theArea.cols+"}","g")),"\n").split("\n");
if(theLines[theLines.length-1]=="") theLines.length--;
theArea.form.lineCount.value = theLines.length;
}
</script>
</head>
<body>
<form>
<textarea name="myText" onKeyUp="countLines(this)" cols="10" rows="10">
</textarea>
<br>
Lines:
<input type=text name="lineCount" size="2" value="0">
</form>
</body>
</html>
Your ans can be done in very simple way.
var text = $("#myTextArea").val();
// will remove the blank lines from the text-area
text = text.replace(/^\s*[\r\n]/gm, "");
//It will split when new lines enter
var lines = text.split(/\r|\r\n|\n/);
var count = lines.length; //now you can count thses lines.
console.log(count);
This code for exact lines filled in the textarea.
and will work for sure.
Instead of textarea you could use a div with the attribute contenteditable="true". On a div with this attribute you can write anything, just like in a textarea, but any new line (except the first) is automatically wrapped inside a div. You can use jQuery or JS to count the div's and add +1, which is the first line.
It's a no brainer, i would use this instead of textarea with every occasion. It has several advantages. It auto resizes, you can easily count blank lines, you can customize every div or add spans with colors or font sizes or anything, you can use any line-height and any font-size, you can add rich text features and more, it's better for SEO and more. Here is a working example with jQuery:
$("#Editor").on("keyup mouseup", function(){
blankSpace = $(this).find("br").length; //count blank lines
urlCounter = $(this).find("div").length + 1 - blankSpace;
$(".lineCounter").text("Number of links: "+ urlCounter);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="Editor" contenteditable="true" style="color:aqua;width: 100%;height: 100%;background: blue;"></div>
<div class="lineCounter" style="position: absolute;bottom: 0;z-index: 999;left: 0;"></div>
Try calling this function every time you change its value.
textArea.addEventListener('input', function() {
setDynamicHeight();
});
function setDynamicHeight() {
textArea.style.height = 0; // set the height to 0 in case of it has to be shrinked
textArea.style.height = textArea.scrollHeight + 'px'; // set the dynamic height
}
Each line break is defined by '\n'. The goal is to count them. For this, we will have to iterate on this with a loop on each character. See the example below
let count = 0
const a = document.querySelector('textarea')
for (let i = 0; i < a.value.length; i++) {
if (a.value[i] == '\n') {
count++
}
}
console.log(count)
In this live demonstration we can see a concrete case with 3 sentences :
const textareaLineCount = () => {
let count = 0
const a = document.querySelector('textarea')
for (let i = 0; i < a.value.length; i++) {
if (a.value[i] == '\n') {
count++
}
}
return count
}
const displayTotalTextareaLine = (total) => {
document.querySelector('p').innerText = total
}
document.querySelector('button').addEventListener('click', () => {
const total = textareaLineCount()
displayTotalTextareaLine(total)
})
<textarea>
Lorem ipsum dolor sit amet.
Lorem ipsum dolor sit amet.
Lorem ipsum dolor sit amet.
</textarea>
<button>count</button>
<p></p>
⚠️ It's possible that the last phase is not a line break, so I advise you to add 1 to the total result