I have text that may break onto multiple lines but should be truncated with an ... added to the end if the maximum character count is exceeded.
Example - Original text = A link to something Truncated text = A link ...
Is there a way to store the original text so that it is accessiable to screen readers? I have been looking into aria attributes but am not sure if those can help in this situation.
Here is a fiddle to my current code, but I do think it is very accessible: http://jsfiddle.net/9s57b/
Here is my HTML:
A link to something
Here is my JavaScript:
var element = document.querySelector('.js-text'),
// innerText for IE, textContent for other browsers
text = element.innerText || element.textContent,
textArray = text.split(''),
textArrayLength = textArray.length,
maxCharacterCount = 7,
newText = '';
if (textArrayLength > maxCharacterCount) {
for (var i = 0; i < maxCharacterCount; i++) {
newText += textArray[i];
}
}
element.innerHTML = newText + ' ...';
Related
I have DOM document with text content like as:
<p>Content scripts aren't completely cut off from their parent extensions. A content script can exchange messages with its parent extension</p>
When I click over this HTML(text) I get nodeValue as text. But can I get only symbol from text over was click?
For example, I do click over symbol s in text:
<p>They support then</p>
So this is a fairly simple pattern when one wants to do operations on single characters, but you have data in blocks of paragraph or word. The first thing to do would be to iterate through all paragraphs, like so:
var paras = document.querySelectorAll('p');
for (var i = 0; i < paras.length; i++) {
var para = paras[i];
var text = para.textContent;
var split = para.split('');
var newText = '';
for (var j = 0; j < split.length; j++) {
newText += '<span class="char">' + split[j] + '</span>';
}
para.innerHTML = newText;
}
Then, you would want to set up a click listener on the body or on each paragraph, and the event (having been produced from clicking one of the single-character spans) would contain all the position information of that specific character.
document.body.addEventListener('click', function(e) {
if (e.target.classList.contains('char')) {
console.log(e.clientLeft, e.clientTop);
}
});
A possible advantage of this method over Selection.focusNode is that it also allows the single character to be modified as an element, not just ascertained.
Note that this will destroy events and object references -- if you need to persist those, use something like jQuery's replaceWith and iterate over the text nodes.
I'm looking for a way to highlight and format code snippets passed as string for a live style guide. I'm playing around with highlighjs and prettify. They are really helpful and easy for highlighting, but I can't seem to figure out a way to format or whether they can actually do that or not.
By formatting, I mean tabs and newlines to make code legible. I need to pass code as a string to automate the output of dust template I'm using for the style guide.
That is, I want to pass:
"<table><tr><td class="title">Name</td><td class="title">Category</td><td class="title">Results</td></tr></table>"
And get something like:
<table>
<tr>
<td class="title">Name</td>
<td class="title">Category</td>
<td class="title">Results</td>
</tr>
</table>
Any ideas on how to accomplish this?
Thanks!
You could parse this as HTML into a DOM and than traverse every element writing it out and indenting it with every iteration.
This code will do the job. Feel free to use it and surely to improve it. It's version 0.0.0.1.
var htmlString = '<table><tr><td class="title">Name</td><td class="title">Category</td><td class="title">Results</td></tr></table>';
//create a containing element to parse the DOM.
var documentDOM = document.createElement("div");
//append the html to the DOM element.
documentDOM.insertAdjacentHTML('afterbegin', htmlString);
//create a special HTML element, this shows html as normal text.
var documentDOMConsole = document.createElement("xmp");
documentDOMConsole.style.display = "block";
//append the code display block.
document.body.appendChild(documentDOMConsole);
function indentor(multiplier)
{
//indentor handles the indenting. The multiplier adds \t (tab) to the string per multiplication.
var indentor = "";
for (var i = 0; i < multiplier; ++i)
{
indentor += "\t";
}
return indentor;
}
function recursiveWalker(element, indent)
{
//recursiveWalker walks through the called DOM recursively.
var elementLength = element.children.length; //get the length of the children in the parent element.
//iterate over all children.
for (var i = 0; i < elementLength; ++i)
{
var indenting = indentor(indent); //set indenting for this iteration. Starts with 1.
var elements = element.children[i].outerHTML.match(/<[^>]*>/g); //retrieve the various tags in the outerHTML.
var elementTag = elements[0]; //this will be opening tag of this element including all attributes.
var elementEndTag = elements[elements.length-1]; //get the last tag.
//write the opening tag with proper indenting to the console. end with new line \n
documentDOMConsole.innerHTML += indenting + elementTag + "\n";
//get the innerText of the top element, not the childs using the function getElementText
var elementText = getElementText(element.children[i]);
//if the texts length is greater than 0 put the text on the page, else skip.
if (elementText.length > 0)
{
//indent the text one more tab, end with new line.
documentDOMConsole.innerHTML += (indenting + indentor(1) ) + elementText+ "\n";
}
if (element.children[i].children.length > 0)
{
//when the element has children call function recursiveWalker.
recursiveWalker(element.children[i], (indent+1));
}
//if the start tag matches the end tag, write the end tag to the console.
if ("<"+element.children[i].nodeName.toLowerCase()+">" == elementEndTag.replace(/\//, ""))
{
documentDOMConsole.innerHTML += indenting + elementEndTag + "\n";
}
}
}
function getElementText(el)
{
child = el.firstChild,
texts = [];
while (child) {
if (child.nodeType == 3) {
texts.push(child.data);
}
child = child.nextSibling;
}
return texts.join("");
}
recursiveWalker(documentDOM, 1);
http://jsfiddle.net/f2L82m8h/
I am trying to create a function that will take an element's text, cut off any characters beyond 80, and add an ellipses if necessary. Here's my code so far:
var maxLength = 80;
function shorten(element) {
var text = $('#' + element).text();
var ret = text;
if (text.length > maxLength) {
text = text.substr(0,maxLength-3) + "...";
}
$('#' + element).text(text);
}
shorten('slide1');
So, the function should take the element, remove extra text off the end, add an ellipses, and then replace the old text in the element with the new string I've just created.
When I run the function, I don't get any errors, but it doesn't actually cut off the text as it should. Why is this?
var text = "Some Text Goes Here. La La La La!";
var textLength = 10; // Number of characters to cut off after
function shorten(text, textLength){
if(text.length > textLength){
text = text.substring(0, textLength) + '…';
}
return text;
}
var shortText = shorten(text, textLength);
Also, using the HTML character for ellipsis is better than using three periods.
I've added a Codepen showing the code working. Additionally, I added a function spaceShorten that will split your text at the last occurrence of a space that is less than the length provided, so you don't split the text mid word.
http://codepen.io/supah_frank/pen/EaYzNz
In InDesign, I'm pulling text out of an xml file and placing it in a textFrame. Since the textFrame doesn't support some tags, I have to replace bold, italic and boldItalic tags in the textFrame with formatting applied programatically. I was originally doing this using Adobe's GREP search/replace, but if a tag was inside another tag it didn't work right (e.g., '<b>a<i>b</i>c</b>'). So I decided to use brute force and split the string by the tags and then loop through each item. The code I came up with works, but I'm curious if there's a better, more efficient way to do this?
Currently, it stores the font, and the text position to add that font to. The tags are also removed.
Note: There is a separate font that styles the text both bold and italic. My apologies if I caused any confusion with the <bi> tag. It was meant to be a tag for a character that needs to be both bold and italic. <b><i>text</i></b> may be a better solution. In any case, it is definitely needed as a separate style for appropriate characters.
var TAGSOBJ = {'<b>': 'Arial\tBold',
'<i>': 'Arial\tItalic',
'<bi>': 'Arial\tBold Italic',
'</b>': 'Arial\tBold',
'</i>': 'Arial\tItalic',
'</bi>': 'Arial\tBold Italic'};
var BGNTAGSOBJ = {'<b>': null, '<i>': null, '<bi>': null};
var ENDTAGSOBJ = {'</b>': null, '</i>': null, '</bi>': null};
var txt = 'This is some <b>really important <i>text</i></b> with <i>some <b>very <bi>very <br> very</bi> important</b> things</i> in it.';
var n = 0;
var prevTag = '';
var noTagTxt = '';
var dataArray = [['none']];
var txtArray = txt.split(/(<\/?(?:b|i|bi)>)/);
for (var i = 0; i < txtArray.length; i++) {
var iTxt = txtArray[i];
if (iTxt in BGNTAGSOBJ) {
dataArray.push([TAGSOBJ[iTxt]]);
prevTag = iTxt;
} else if (iTxt in ENDTAGSOBJ) {
if (prevTag in ENDTAGSOBJ) {
dataArray[dataArray.length - 1][0] = TAGSOBJ[iTxt];
}
dataArray.push(['none']);
prevTag = iTxt;
} else if (iTxt.length > 0) {
var iTxtLen = iTxt.length;
dataArray[dataArray.length - 1].push([n, n + (iTxtLen - 1)]);
noTagTxt += iTxt;
n += iTxtLen;
} else {
dataArray[dataArray.length - 1][0] = 'none';
}
}
for (var i = 0; i < dataArray.length; i++) {
$.writeln(dataArray[i]); // print to console in extendscript
}
$.writeln(noTagTxt);
/* Outputs to console in extendscript:
none,0,12
Arial Bold,13,29
Arial Italic,30,33
Arial Bold
none,34,39
Arial Italic,40,44
Arial Bold,45,49
Arial Bold Italic,50,63
Arial Bold,64,73
Arial Italic,74,80
none,81,87
This is some really important text with some very very <br> very important things in it.
Result: undefined
*/
I'm waiting for the explanation of the <bi> tag, but in the meantime here's some code that strips out the tags and collects the information you need. I'm also not 100% clear on the font stuff. Are the values in TAGSOBJ the ones needed to actually apply the formatting?
var
content = 'This is some <b>really important <i>text</i></b> with <i>some <b>very very <br>very important</b> things</i> in it.',
tagPattern = /<\/?(i|b)\b[^>]*>/ig,
stack = [],
tags = [],
offset = 0,
match,
tag;
while (match = tagPattern.exec(content)) {
if (match[0].substr(1, 1) !== '/') {
stack.push(match.index - offset);
} else {
tags.push({
tag: match[1],
from: stack.splice(-1, 1)[0],
to: match.index - offset
});
}
offset += match[0].length;
}
content = content.replace(tagPattern, '');
// now use tags array and perform needed actions.
Here's a fiddle demonstrating this.
Last, for what it's worth, I don't think it is standard practice to use upper-case identifiers in javascript.
I am using a 'contenteditable' <div/> and enabling PASTE.
It is amazing the amount of markup code that gets pasted in from a clipboard copy from Microsoft Word. I am battling this, and have gotten about 1/2 way there using Prototypes' stripTags() function (which unfortunately does not seem to enable me to keep some tags).
However, even after that, I wind up with a mind-blowing amount of unneeded markup code.
So my question is, is there some function (using JavaScript), or approach I can use that will clean up the majority of this unneeded markup?
Here is the function I wound up writing that does the job fairly well (as far as I can tell anyway).
I am certainly open for improvement suggestions if anyone has any. Thanks.
function cleanWordPaste( in_word_text ) {
var tmp = document.createElement("DIV");
tmp.innerHTML = in_word_text;
var newString = tmp.textContent||tmp.innerText;
// this next piece converts line breaks into break tags
// and removes the seemingly endless crap code
newString = newString.replace(/\n\n/g, "<br />").replace(/.*<!--.*-->/g,"");
// this next piece removes any break tags (up to 10) at beginning
for ( i=0; i<10; i++ ) {
if ( newString.substr(0,6)=="<br />" ) {
newString = newString.replace("<br />", "");
}
}
return newString;
}
Hope this is helpful to some of you.
You can either use the full CKEditor which cleans on paste, or look at the source.
I am using this:
$(body_doc).find('body').bind('paste',function(e){
var rte = $(this);
_activeRTEData = $(rte).html();
beginLen = $.trim($(rte).html()).length;
setTimeout(function(){
var text = $(rte).html();
var newLen = $.trim(text).length;
//identify the first char that changed to determine caret location
caret = 0;
for(i=0;i < newLen; i++){
if(_activeRTEData[i] != text[i]){
caret = i-1;
break;
}
}
var origText = text.slice(0,caret);
var newText = text.slice(caret, newLen - beginLen + caret + 4);
var tailText = text.slice(newLen - beginLen + caret + 4, newLen);
var newText = newText.replace(/(.*(?:endif-->))|([ ]?<[^>]*>[ ]?)|( )|([^}]*})/g,'');
newText = newText.replace(/[·]/g,'');
$(rte).html(origText + newText + tailText);
$(rte).contents().last().focus();
},100);
});
body_doc is the editable iframe, if you are using an editable div you could drop out the .find('body') part. Basically it detects a paste event, checks the location cleans the new text and then places the cleaned text back where it was pasted. (Sounds confusing... but it's not really as bad as it sounds.
The setTimeout is needed because you can't grab the text until it is actually pasted into the element, paste events fire as soon as the paste begins.
How about having a "paste as plain text" button which displays a <textarea>, allowing the user to paste the text in there? that way, all tags will be stripped for you. That's what I do with my CMS; I gave up trying to clean up Word's mess.
You can do it with regex
Remove head tag
Remove script tags
Remove styles tag
let clipboardData = event.clipboardData || window.clipboardData;
let pastedText = clipboardData.getData('text/html');
pastedText = pastedText.replace(/\<head[^>]*\>([^]*)\<\/head/g, '');
pastedText = pastedText.replace(/\<script[^>]*\>([^]*)\<\/script/g, '');
pastedText = pastedText.replace(/\<style[^>]*\>([^]*)\<\/style/g, '');
// pastedText = pastedText.replace(/<(?!(\/\s*)?(b|i|u)[>,\s])([^>])*>/g, '');
here the sample : https://stackblitz.com/edit/angular-u9vprc
I did something like that long ago, where i totally cleaned up the stuff in a rich text editor and converted font tags to styles, brs to p's, etc, to keep it consistant between browsers and prevent certain ugly things from getting in via paste. I took my recursive function and ripped out most of it except for the core logic, this might be a good starting point ("result" is an object that accumulates the result, which probably takes a second pass to convert to a string), if that is what you need:
var cleanDom = function(result, n) {
var nn = n.nodeName;
if(nn=="#text") {
var text = n.nodeValue;
}
else {
if(nn=="A" && n.href)
...;
else if(nn=="IMG" & n.src) {
....
}
else if(nn=="DIV") {
if(n.className=="indent")
...
}
else if(nn=="FONT") {
}
else if(nn=="BR") {
}
if(!UNSUPPORTED_ELEMENTS[nn]) {
if(n.childNodes.length > 0)
for(var i=0; i<n.childNodes.length; i++)
cleanDom(result, n.childNodes[i]);
}
}
}
This works great to remove any comments from HTML text, including those from Word:
function CleanWordPastedHTML(sTextHTML) {
var sStartComment = "<!--", sEndComment = "-->";
while (true) {
var iStart = sTextHTML.indexOf(sStartComment);
if (iStart == -1) break;
var iEnd = sTextHTML.indexOf(sEndComment, iStart);
if (iEnd == -1) break;
sTextHTML = sTextHTML.substring(0, iStart) + sTextHTML.substring(iEnd + sEndComment.length);
}
return sTextHTML;
}
Had a similar issue with line-breaks being counted as characters and I had to remove them.
$(document).ready(function(){
$(".section-overview textarea").bind({
paste : function(){
setTimeout(function(){
//textarea
var text = $(".section-overview textarea").val();
// look for any "\n" occurences and replace them
var newString = text.replace(/\n/g, '');
// print new string
$(".section-overview textarea").val(newString);
},100);
}
});
});
Could you paste to a hidden textarea, copy from same textarea, and paste to your target?
Hate to say it, but I eventually gave up making TinyMCE handle Word crap the way I want. Now I just have an email sent to me every time a user's input contains certain HTML (look for <span lang="en-US"> for example) and I correct it manually.