I need to calculate the typing speed of the user, the typing speed is calculated by inputting his email address.
The user will compete with other users who typed their email address. How I am doing it is I am getting the time taken to input their email address. e.g. 5 seconds
Then get the number of characters inputted e.g. 23 characters divide that by 5 (average word length)
Total Time / Total Words typed * 60 = Words per minute
The problem is that there is a discrepancy, if a user has a short email address e.g. me#me.com he will get 170 words per minute whilst if you have an average one like chrisemail#hotmail.com you will get 55 words per minute.
How can I find a way to standardize or add weight so that I can compare typing speed?
You standardize by having the users type the same input. This is especially important when you're having them enter very small amounts of data. Consider the following:
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla fermentum felis nec quam accumsan venenatis porta ligula vehicula. Praesent vitae sapien vitae velit tempor luctus eget a enim. Praesent eros metus, commodo id adipiscing vitae, congue eu tellus. Nullam feugiat, massa at adipiscing congue, tellus dui mollis nibh, id convallis metus libero sed libero. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nunc vitae congue eros. Sed non fringilla purus. Quisque lectus leo, lacinia vitae elementum at, laoreet eget leo. Integer sit amet orci tellus. Sed diam metus, elementum id varius at, iaculis sit amet eros.
This took me approximately 28 words per minute (which is much slower than my normal typing speed). It's a significant amount of text, but even more importantly is the fact that it's not normally typed text. It's not in my native language, so I had to slowly analyze each word. It's not composed of things I type very often. And so on.
I
This took me approximately 600 words per minute (though it was difficult to estimate, so there's a significant margin of error there). A personal best to say the least.
Why did these results vary so significantly? Because I was typing very different things. When you add another variable to this equation (multiple people), you get even more variation.
You need to standardize the test. When educational institutions test their students, they generally have those students perform identical, if not at least functionally equivalent, tasks. This helps eliminate variables so that the only variable is the one being tested... the person.
Try something along these lines:
<!doctype html>
<html>
<body>
<textarea id="email_add"></textarea>
<input type="button" value="Done" id="done"/>
<script>
var doneButton = document.getElementById('done');
var emailArea = document.getElementById('email_add');
var lengthOfEmail = 0;
var time_start = 0;
var time_end = 0;
emailArea.onkeyup = function() {
lengthOfEmail++;
if(lengthOfEmail == 1) time_start = new Date();
else time_end = new Date().getTime() - time_start;
}
doneButton.onclick = function() {
alert("Email Length: " + lengthOfEmail);
alert("Time: " + time_end + " milliseconds.");
}
</script>
</body>
</html>
Now, you're going to want to adjust the email address length to accommodate for the shift that's pressed when the '#' symbol is entered. But this should help you get the time, along with a way of verifying that they're not just copying and pasting.
Related
Lets say we have a div
<div id="my-div">
<p>Lorem ipsum dolor sit <strong>amet |consequat pharetra auctor </strong>condimentum lacus purus tortor habitasse molestie.</p>
<p>Auctor luctus lacus imperdiet <strong>pharetra| consequat</strong> egestas faucibus.</p>
</div>
And user selects a portion similar the one between "|".
function getSelectionPortion(){
const div = document.getElementById('my-div');
const selection = document.getSelection().getRangeAt(0).cloneContents(); // or extractContents();
return selection;
}
getSelectionPortion() will return the following:
<p><strong>consequat pharetra auctor </strong>condimentum lacus purus tortor habitasse molestie.</p>
<p>Auctor luctus lacus imperdiet <strong>pharetra</strong></p>
now we can get portions of the range by paragraphs, simply by e.g. fragment.querySelectorAll('p').
And if we extract the range contents and loop over each portion, we can take each strong inside each p and replace with its innerHTML
let blocks = getSelectionPortion().querySelectorAll('p');
for (let block of blocks){
let inlines = block.querySelectorAll('strong');
for (let inline of inlines){
inline.replaceWith(inline.innerHTML);
}
}
However, this outputs with additional paragraphs, because extracted fragment closes the paragraph tags. So we end up with
<div id="my-div">
<p>Lorem ipsum dolor sit <strong>amet</strong></p>
<p>consequat pharetra auctor condimentum lacus purus tortor habitasse molestie.</p>
<p>Auctor luctus lacus imperdiet pharetra</p>
<p><strong>consequat</strong> egestas faucibus.</p>
</div>
How do you remove the inline tag from the selection without creating additional block level elements with fragment? Is there a way to divide the range into peaces instead of its extract?
While creating a responsive web design it often happens that a one-letter word remains as the last word in a paragraph's line:
This is a
paragraph
I would like to move such a single-letter word as follows:
This is
a paragraph
Is there any built-in property in CSS which allows to achieve this effect?
If there is none, how can this be done in JavaScript?
If you want to ALWAYS keep "a" and "paragraph" together, add a "non-breaking-space" between them:
<div style="width:100px;border:1px solid #333;padding:5px">
<p>
This is a paragraph
</p>
</div>
In order to keep single-letter words (like 'a' or 'I') on the same line as the following word, you can replace the space character between them with the non-breaking space Unicode character \u00A0.
This could be automated with a little JavaScript. For example, this code replaces [space][letter][space] with [space][letter][non-breaking space]:
const modifySingleChars = str => str.replace(/ ([a-zA-Z]) /g,
' $1' + '\u00A0');
To change all instances on a page, first collect all the text nodes within the body (skipping anything inside a <script> tag.
Then simply iterate through the text nodes, making the appropriate substitutions.
A working example:
// form array of all text nodes in parentNode
function allTextNodes(parentNode) {
let arr = [];
if (!parentNode) {
return arr;
}
let nodes = parentNode.childNodes;
nodes.forEach(node => {
if (node.nodeName === 'SCRIPT') {
return;
}
if (node.nodeType === Node.TEXT_NODE) {
arr.push(node);
} else {
arr = arr.concat(allTextNodes(node));
}
});
return arr;
}
// convert [space][letter][space] to [space][letter][non-breaking space];
const modifySingleCharWords = str => str.replace(/ ([a-zA-Z]) /g,
' $1' + '\u00A0');
function fixAllSingleCharWordsInBody() {
let tNodes = allTextNodes(document.body);
tNodes.forEach(tNode => {
tNode.nodeValue = modifySingleCharWords(tNode.nodeValue);
});
}
<body>
<div id="wrapper" style="width:20rem">
<h4>Prevent single character words at end of line</h4>
<button type="button" onclick="fixAllSingleCharWordsInBody();">Fix All Words
</button>
<p>Lorem ipsum dolor i amet, consectetur a dipiscing elit, sed o eiusmod tempor incididunt u labore et dolore magna aliqua. <span>Nisl purus i mollis</span> nunc.
</p>
<p>In vitae turpis massa e elementum tempusus a sed. Eget mi proin e libero enim i faucibus. Quis lectus nulla a volutpat diam ut.
</p>
<p>Pharetra e ultrices neque ornare. Donec a tristique risus e feugiat in fermentum. Consectetur adipiscing e u aliquam purus sit amet.
</p>
<p>Vitae congue mauris rhoncus aenean e elit scelerisque mauris pellentesque. Mauris u eros i cursus turpis a tincidunt dui.
</p>
<p>At volutpat diam u venenatis tellus. Tellus integer feugiat scelerisque varius morbi i nunc faucibus at.</p>
<script>
const b = 'Do not modify anything inside a script tag';
</script>
</div>
</body>
from few years we have ES6 so...
[...document.body.querySelectorAll('*')]
.map(n => n.firstChild)
.filter(n => (n != null && n.nodeType === Node.TEXT_NODE && !['SCRIPT', 'STYLE'].includes(n.parentNode.nodeName)))
.forEach(el => {
el.nodeValue = el.nodeValue.replace(/ ([a-zA-Z]) /g, ` $1\u00A0`)
});
document.body can be replaced by any node, here is for simplicity
So I am trying to make a more/less content toggle feature, it worked just fine until I started adding more boxes to be toggled. I read up on using multiple event handlers and the suggestion was to use document.getElementsByClassName(); and iterate through them, but the second button when clicked does not show/hide the correct content, only the first one.
Any help is appreciated. The simpler way would be to use jQuery or assign different IDs to the buttons and call the function on each one but I'm sure it must be possible to just create/call the event listener once and have it work on every subsequently added element.
Here is my code:
HTML
<div class="card">
<div class="cardLessContent">
<h1>Here is a card</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent auctor mi sollicitudin, tristique tortor eget, tempus elit. Vestibulum ante leo, aliquam ac dapibus at, auctor vitae ligula.</p>
</div>
<div id="cardMoreContent" class="cardMoreContent">
<p> Nam finibus nec augue at semper. Quisque sit amet ex eu augue rutrum aliquet. Suspendisse dui enim, gravida quis turpis a, auctor pellentesque velit.</p>
</div>
<button id="toggleContent" class="toggleContent toggledOff">More</button>
</div>
<div class="card">
<div class="cardLessContent">
<h1>Here is a card</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent auctor mi sollicitudin, tristique tortor eget, tempus elit. Vestibulum ante leo, aliquam ac dapibus at, auctor vitae ligula.</p>
</div>
<div id="cardMoreContent" class="cardMoreContent">
<p> Nam finibus nec augue at semper. Quisque sit amet ex eu augue rutrum aliquet. Suspendisse dui enim, gravida quis turpis a, auctor pellentesque velit.</p>
</div>
<button id="toggleContent" class="toggleContent toggledOff">More</button>
</div>
JavaScript
const btnToggleContent = document.getElementsByClassName("toggleContent");
const cardMoreContent = document.getElementById("cardMoreContent");
var toggleContent = function() {
cardMoreContent.classList.toggle("showing");
this.classList.toggle("toggledOff");
if(this.classList.contains("toggledOff")) {
this.innerHTML = "More";
}
else {
this.innerHTML = "Less";
}
}
for (var i = 0; i < btnToggleContent.length; i++) {
btnToggleContent[i].addEventListener("click", toggleContent);
}
JSFiddle: https://jsfiddle.net/jw3qe9xf/6/
Id attribute should be unique in HTML.Otherwise it will only pick the first occurence of that Id.In your case you will always toggle the first cardMoreContent
Here is a simple solution for you by using event.target
Javascript
const btnToggleContent = document.getElementsByClassName("toggleContent");
const cardMoreContent = document.getElementById("cardMoreContent");
var toggleContent = function(e) {
e.target.previousElementSibling.classList.toggle("showing");
this.classList.toggle("toggledOff");
if(this.classList.contains("toggledOff")) {
this.innerHTML = "More";
}
else {
this.innerHTML = "Less";
}
}
for (var i = 0; i < btnToggleContent.length; i++) {
btnToggleContent[i].addEventListener("click", toggleContent);
}
JSFiddle : https://jsfiddle.net/c1gdjk25/
Yes , works only for first. This is reason :
you have multiply use ' id="toggleContent" ' same id value for more than one element it is wrong in basic.
Id attribute must be uniq !
Use example from this answer :
How to get child element by class name?
You will need only childNodes and loop trow it.
Count on that you can search child from child element also ...
after you remove all ids attributes change your javascript code to :
const btnToggleContent = document.getElementsByClassName("toggleContent");
var toggleContent = function(e) {
/*e.target will get the clicked element which is the button clicked and parentElement
gives you the parent element of this button which is the card you want to modify*/
var card = e.target.parentElement;
/*when you have the card you can use on its getElementsByClassName to retreive all
elements that have the class (cardMoreContent) in this case theres only one so you take
the first element from the array returned with [0]*/
var cardMoreContent = card.getElementsByClassName("cardMoreContent")[0];
//then rest of the code stays the same
cardMoreContent.classList.toggle("showing");
if(this.classList.contains("toggledOff")) {
this.innerHTML = "More";
}
else {
this.innerHTML = "Less";
}
}
for (var i = 0; i < btnToggleContent.length; i++) {
btnToggleContent[i].addEventListener("click", toggleContent);
}
In the MDN documentation for using custom elements, they detail an example for customizing built-in elements:
customElements.define('expanding-list', ExpandingList, { extends: "ul" });
Allowing this usage:
<ul is="expanding-list">
...
</ul>
I am wondering if it is possible to customize another custom element in the same way? For example, if I have created an element called custom-element, and I want to have variants of it, I might want to create a new special-custom-element class, and define it in the same way, so as to be able to use it like so:
<custom-element is="special-custom-element">
...
</custom-element>
However, I am prompted with an error stating:
Uncaught DOMException: Failed to execute 'define' on 'CustomElementRegistry': "custom-element" is a valid custom element name
When attempting to run the following:
customElements.define('special-custom-element', SpecialCustomElement, { extends: 'custom-element' });
Is this something I am doing wrong, or is this behaviour strictly limited to built-in elements? I'm finding it rather difficult to find any information about this behaviour other than what is on the page I linked to, so I was hoping for some advice from someone who knows the specs better.
You can not do it in the sense that you want since:
There are two types of custom elements you can create:
Autonomous custom element: Standalone elements; they don't inherit from built-in HTML elements.
Customized built-in element: These elements inherit from — and extend — built-in HTML elements.
customElements.define('word-count2', WordCount2, {extends: 'p'});
Is for extending the built-in elements.
You have to go the Autonomous custom element route as per the docs
Here is an idea:
// Create a class for the element
class MyElement extends HTMLElement {
constructor(text) {
// Always call super first in constructor
super();
// Create a shadow root
var shadow = this.attachShadow({
mode: 'open'
});
// Create the span
var wrapper = document.createElement('span');
wrapper.textContent = !!text ? text : 'foo';
shadow.appendChild(wrapper);
}
}
class MyElement2 extends MyElement {
constructor() {
// Always call super first in constructor
super('bar');
}
}
// Define the new element
customElements.define('my-element', MyElement);
customElements.define('my-element2', MyElement2);
<div>
<my-element text="">
</div>
<div>
<my-element2 text="">
</div>
Now you still could extend your class (and do customization of a build-in element) and access the output of the super so that might be useful to you and to some extend might allow you to get what you need:
// Create a class for the element
class WordCount extends HTMLParagraphElement {
constructor() {
// Always call super first in constructor
super();
// count words in element's parent element
const wcParent = this.parentNode;
function countWords(node) {
const text = node.innerText || node.textContent;
return text.split(/\s+/g).length;
}
const count = `Words: ${countWords(wcParent)}`;
// Create a shadow root
const shadow = this.attachShadow({
mode: 'open'
});
// Create text node and add word count to it
const text = document.createElement('span');
text.textContent = count;
// Append it to the shadow root
shadow.appendChild(text);
// Update count when element content changes
text.textContent = count;
}
}
class WordCount2 extends WordCount {
constructor() {
// Always call super first in constructor
super();
console.log(this.shadowRoot.textContent)
}
}
// Define the new element
customElements.define('word-count', WordCount, {
extends: 'p'
});
customElements.define('word-count2', WordCount2, {
extends: 'p'
});
<div>
<h2>Sample heading</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc pulvinar sed justo sed viverra. Aliquam ac scelerisque tellus. Vivamus porttitor nunc vel nibh rutrum hendrerit. Donec viverra vestibulum pretium. Mauris at eros vitae ante pellentesque bibendum.
Etiam et blandit purus, nec aliquam libero. Etiam leo felis, pulvinar et diam id, sagittis pulvinar diam. Nunc pellentesque rutrum sapien, sed faucibus urna sodales in. Sed tortor nisl, egestas nec egestas luctus, faucibus vitae purus. Ut elit nunc,
pretium eget fermentum id, accumsan et velit. Sed mattis velit diam, a elementum nunc facilisis sit amet.</p>
<p is="word-count"></p>
</div>
<div>
<h3>Sample heading</h3>
<p>Lorem ipsum dolor sit amet, consec Sed tortor nisl, egestas nec egestas luctus, faucibus vitae purus. Ut elit nunc, pretium eget fermentum id, accumsan et velit. Sed mattis velit diam, a elementum nunc facilisis sit amet.</p>
<p is="word-count2"></p>
</div>
You can also do this
class SpecialCustomElement extends CustomElement
{
...
}
customElements.define('special-custom-element', SpecialCustomElement, { extends: 'ul' });
and can use it as
...
First of all, I have searched quite some hours to find this.. I presume it has an easy fix but I'm really new to jQuery and Javascript, so I'm here for your help.
The problem
I'm working with multiple divs and jQuery ToggleSlide(). I want the script to find the right div to open when I click the corresponding div.
For example, when I click the div 'hackandfly', I want it to open the div 'hackandfly-open'.
The code
$(document).ready(function() {
$('.project-open').hide();
$('.hackandfly, .scanergy, .connecting-food').click(function() {
$slidah = $(this);
$slidah.slideToggle();
$('div.project-open').not($slidah).slideUp();
});
});
HTML
<div class="content projects">
<h3>Projects</h3>
<div class="project project-big hackandfly">
<h3>Hack and Fly</h3>
</div>
<div class="hackandfly-open project-open" style="display: none;">
<img src="images/schiphol-logo.png" class="img-project-open"> Proin nec elit ac sapien facilisis ultrices. Integer pellentesque ex a luctus fringilla. Aenean in quam quam. Integer gravida quam eget mauris laoreet hendrerit. Vestibulum feugiat ipsum id.
<br>
<br>
Metus aliquet iaculis. Proin massa justo, maximus in tortor et, Proin massa justo, maximus in tortor et. In aliquam laoreet magna et iaculis. Vestibulum vel orci lobortis, elementum nulla eget, porta eros. Interdum et malesuada fames ac ante ipsum primis in faucibus.
<br>
<br>
Proin massa justo, maximus in tortor et, tincidunt efficitur nibh. Mauris vulputate euismod lorem, vel rutrum ipsum iaculis eu.
</div>
So what I'm looking for, is that when I push 'hackandfly' div, 'scanergy' div or the 'connecting-food' div, I want it to slideToggle the corresponding div that has -open behind it (I have 3 divs with info called hackandfly-open, scanergy-open, connecting-food-open).
I tried some things like:
$slidah = $(this).after('-open');
And some other stuff but it didn't work. Who can help me?
Cheers!
Use attr() like
$(document).ready(function() {
$('.project-open').hide();
$('.hackandfly, .scanergy, .connecting-food').click(function() {
$slidah = $($(this).attr('class')+'-open');
$slidah.slideToggle();
$('div.project-open').not($slidah).slideUp();
});
});
However, the above will fail if you have multiple classes.
A workaround would be to add data-* to the clicked elements like
<div class="hackandfly other-class" data-class-target="hackandfly-open"></div>
and then
$(document).ready(function() {
$('.project-open').hide();
$('.hackandfly, .scanergy, .connecting-food').click(function() {
$slidah = $('.'+$(this).attr('data-class-target'));
$slidah.slideToggle();
$('div.project-open').not($slidah).slideUp();
});
});
I would generate a unique click handler for each class. That way, you can store all the applicable class names in an array:
// Creates a new unique click function for each class name
function generateClickHandler(className) {
return function(e) {
// select the open class here
$slidah = $('.'+className+'-open');
$slidah.slideToggle();
$('div.project-open').not($slidah).slideUp();
};
}
// Iterate over all the class names and add a new function for each
var clsList = ["hackandfly", "scanergy", "connecting-food"];
$.each(clsList, function(className) {
$("."+className).click(generateClickHandler(className));
});
Use:
$('.hackandfly, .scanergy, .connecting-food').click(function() {
$slidah = $("."+$(this).attr('class')+"-open");
$slidah.slideToggle();
});
If you added a wrapper around each section like so:
<div class="content projects">
<h3>Projects</h3>
<div class="project-wrapper">
<div class="project project-big hackandfly">
<h3>Hack and Fly</h3>
</div>
<div class="hackandfly-open project-open" style="display: none;">
{...}
</div>
</div>
</div>
you could use parent and find to get the corresponding element as follows:
$('.hackandfly, .scanergy, .connecting-food').click(function() {
$(this).parent().find('.project-open').eq(0).slideToggle();
}