My demo in JS Fiddle https://jsfiddle.net/dineshkanivu/5fp2sjgb/2/
I want to add content Dynamically to the id="myNote" in its 4th line.
click the button lines , you can see total number of lines. i want to add some html content after 4th line. How can i do this using jQuery
Snippet :
$(function() {
$("#getLines").click(function() {
var myheight = $("#myNote").height();
parseFloat($("#myNote").css("line-height"));
//$('#myNote').after('<button>button</button>');
alert(myheight);
});
});
#myNote {
width: 300px;
line-height: 1;
height: auto;
text-align: justify;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="myNote">
Finally, designing the last sentence in this way has the added benefit of seamlessly moving the reader to the first paragraph of the body of the paper. In this way we can see that the basic introduction does not need to be much more than three or four
sentences in length. If yours is much longer you might want to consider editing it down a bit! Here, by way of example, is an introductory paragraph to an essay in response to the following question:
</div>
<button id="getLines">lines</button>
According to this post I wrote a little function to do this.
Surely there a more efficent way. But it works fine.
I wrap every word in an own span. After that I check the positions of all spans, get the line number and add a class with this line number to the span.
function insertTextAtLine(target,lineNumber,textInsert){
var words = target.text().split(' ');
var text = '';
$.each(words, function(i, w){
if($.trim(w)) text = text + '<span>' + w + '</span> ';
});
target.html(text);
var line = 0;
var prevTop = - parseFloat($("#myNote").css("line-height"));
$('span', target).each(function(){
var word = $(this);
var top = word.offset().top;
if(top != prevTop){
prevTop = top;
line++;
}
word.attr('class', 'line' + line);
});
var insert=$('<span />').text(textInsert);
target.find('.line'+lineNumber).first().prepend(insert);
};
Fiddle:https://jsfiddle.net/tye3czva/4/
Related
Is there a way in Vanilla JS to get each line of a text element when the page loads or when the window is resized?
Lets say for the element <h2>This is a beautiful example text</h2>.
On mobile (with less space it breaks into several lines) would be displayed as:
This is a
beautiful
example text
and i would like to get an array like:
["This is a", "beautiful", "example text"]
if we resize the window, maybe on desktop, than the text would have a little bit more space so it needs to break in to less lines, maybe:
This is a beautiful
example text
and then i would like to get:
["This is a beautiful", "example text"]
i have tried something like:
let text = document.querySelector('.text').textContent;
let lines = text.split(/\r|\r\n|\n/);
but i always end up getting the whole string together like ["This is a beautiful text"].
I am trying to do this, because i need to style each line independently (yeap, crazy client wishes!), i cannot change the html and it needs to work when i resize the window. So i think i need to do it with JS, and somehow see how CSS is affecting the text and breaking it into several lines...
so, to clarify, what i am trying to get with JS with the text of each line, when the text breaks because of the container getting smaller. So the Element and the text are the same, but due to less space it breaks into different amount of lines.
Any ideas?
** === UPDATE === **
I can get the number of lines using .getClientRects(). But what i am trying to do is get the text of each of the lines of the element
I've put together a mock up JS solution.
Overview:
Get an array of words
Append each word one at a time to a hidden element with the same font-sizing styles as the title
Check if the element is larger than the title, if so, add the current line (before we added the last word to it) to an array
function calcLines() {
// Build an array of each word used in the original title
var allWords = document.getElementById("title").innerText.match(/\S+/g) || [];
// The array we will fill with each line
var lines = [];
// The current line we are working on building
var currentLine = "";
// Work through the words until we're filling the correct amount of space
for (var i = 0; i < allWords.length; i++) {
// Build a new line and check if it is now too large for the container
var newLine = currentLine + allWords[i] + " ";
document.getElementById("fontSizeTester").innerText = newLine;
if (
document.getElementById("fontSizeTester").clientWidth >
document.getElementById("title").clientWidth
) {
// If the line is now larger, use the previous line (without the last added word) and reset the current line to just the last word
lines.push(currentLine.trim());
currentLine = allWords[i] + " ";
} else {
// If it's not long enough yet, just keep adding words
currentLine = newLine;
}
}
// Push any unfinshed final line to the array
lines.push(currentLine.trim());
console.log(lines);
}
// Run on load and on resize
calcLines();
window.addEventListener("resize", calcLines);
h2 {
font-size: 30px;
font-weight: normal;
font-family: arial;
}
#fontSizeTester {
position: absolute;
visibility: hidden;
height: auto;
width: auto;
white-space: nowrap;
}
<h2 id="title">This is a beautiful example text This is a beautiful example text This is a beautiful example text This is a beautiful example text This is a beautiful example text This is a beautiful example text This is a beautiful example text This is a beautiful example text This is a beautiful example text</h2>
<h2 id="fontSizeTester"></h2>
You can create a flex container in your body , and attach your array there. As result all items in your array will be filling the width of the screen.
https://codepen.io/Vlasenko/pen/vYLwMvE - check it here
const cont = document.querySelector(".container");
const arr = ["This is a", "beautiful", "example text"];
arr.forEach((item) => {
const div = document.createElement("div");
div.className = "item";
div.innerText = item;
cont.appendChild(div);
});
<style>
.container {
display: flex;
flex-wrap: wrap;
}
.item {
padding-right: 5px;
}
</style>
<body>
<div class="container"></div>
</body>
The new predictive type feature Smart Compose of Gmail is quite interesting.
Let's say we want to implement such a functionality ourselves:
User enters beginning of text, e.g. How and in gray behind it appears are you?.
User hits TAB and the word tomorrow is set.
Example:
Can a textarea with Javascript be used to achieve this?
And if not, how could this be implemented otherwise?
My previous answer got deleted, so here's a better attempt at explaining how I've somewhat replicated Smart Compose. My answer only focuses on the pertinent aspects. See https://github.com/jkhaui/predictable for the code.
We are using vanilla js and contenteditable in our solution (just like Gmail does). I bootstrap my example with create-react-app and Medium-Editor, but neither React nor Medium-Editor are necessary.
We have a database of "suggestions" which can be an array of words or phrases. For our purposes, in my example, I use a static array containing 50,000+ common English phrases. But you can easily see how this could be substituted for a dynamic data-source - such as how Gmail uses its neural network API to offer suggestions based on the current context of users' emails: https://ai.googleblog.com/2018/05/smart-compose-using-neural-networks-to.html
Smart Compose uses JavaScript to insert a <span></span> element immediately after the word you are writing when it detects a phrase to suggest. The span element contains only the characters of the suggestion that have not been typed.
E.g. Say you've written "Hi, how a" and a suggestion appears. Let's say the entire suggestion is "how are you going today". In this case, the suggestion is rendered as "re you going today" within the span. If you continue typing the characters in the placeholder - such as "Hi, how are you goi" - then the text content of the span changes dynamically - such that "ng today" is now the text within the span.
My solution works slightly differently but achieves the same visual effect. The difference is I can't figure out how to insert an inline span adjacent to the user's current text and dynamically mutate the span's content in response to the user's input.
So, Instead, I've opted for an overlay element containing the suggestion. The trick is now to position the overlay container exactly over the last word being typed (where the suggestion will be rendered). This provides the same visual effect of an inline typeahead suggestion.
We achieve correct positioning of the overlay by calculating the top + left coordinates for the last word being typed. Then, using JavaScript, we couple the top + left CSS attributes of the overlay container so that they always match the coordinates of the last word. The tricky part is getting these coordinates in the first place. The general steps are:
Call window.getSelection().anchorNode.data.length which retrieves the current text node the user is writing in and returns its length, which is necessary to calculate the offset of the last word within its parent element (explained in the following steps).
For simplicity's sake, only continue if the caret is at the end of the text.
Get the parent node of the current text node we're in. Then get the length of the parent node's text content.
The parent node's text length - the current text node's (i.e the last word's) text length = the offset position of the last text node within its contenteditable parent.
Now we have the offset of the last word, we can use the various range methods to insert a span element immediately preceding the last word: https://developer.mozilla.org/en-US/docs/Web/API/Range
Let's call this span element a shadowNode. Mentally, you can now picture the DOM as follows: we have the user's text content, and we have a shadowNode placed at the position of the last word.
Finally, we call getBoundingClientRect on the shadowNode which returns specific metadata, including the top + left coordinates we're after.
Apply the top + left coordinates to the suggestions overlay container and add the appropriate event handlers/listeners to render the suggestion when Tab is pressed.
Visit this link for documentation https://linkkaro.com/autocomplete.html .
May be you need to make few adjustment in CSS ( padding and width ).
I hope it will help.[![
$(document).ready(function(){
//dummy random output. You can use api
var example = {
1:"dummy text 1",
2:"dummy text 2"
};
function randomobj(obj) {
var objkeys = Object.keys(obj)
return objkeys[Math.floor(Math.random() * objkeys.length)]
}
var autocomplete = document.querySelectorAll("#autocomplete");
var mainInput = document.querySelectorAll("#mainInput");
var foundName = '';
var predicted = '';
var apibusy= false;
var mlresponsebusy = false;
$('#mainInput').keyup(function(e) {
//check if null value send
if (mainInput[0].value == '') {
autocomplete[0].textContent = '';
return;
}
//check if space key press
if (e.keyCode == 32) {
CallMLDataSetAPI(e);
scrolltobototm();
return;
}
//check if Backspace key press
if (e.key == 'Backspace'){
autocomplete[0].textContent = '';
predicted = '';
apibusy = true;
return;
}
//check if ArrowRight or Tab key press
if(e.key != 'ArrowRight'){
if (autocomplete[0].textContent != '' && predicted){
var first_character = predicted.charAt(0);
if(e.key == first_character){
var s1 = predicted;
var s2 = s1.substr(1);
predicted = s2;
apibusy = true;
}else{
autocomplete[0].textContent = '';
apibusy= false;
}
}else{
autocomplete[0].textContent = '';
apibusy= false;
}
return;
}else{
if(predicted){
if (apibusy == true){
apibusy= false;
}
if (apibusy== false){
mainInput[0].value = foundName;
autocomplete[0].textContent = '';
}
}else{
return;
}
}
function CallMLDataSetAPI(event) {
//call api and get response
var response = {
"predicted": example[randomobj(example)]
};
if(response.predicted != ''){
predicted = response.predicted;
var new_text = event.target.value + response.predicted;
autocomplete[0].textContent = new_text;
foundName = new_text
}else{
predicted = '';
var new_text1 = event.target.value + predicted;
autocomplete[0].textContent = new_text1;
foundName = new_text1
}
};
});
$('#mainInput').keypress(function(e) {
var sc = 0;
$('#mainInput').each(function () {
this.setAttribute('style', 'height:' + (0) + 'px;overflow-y:hidden;');
this.setAttribute('style', 'height:' + (this.scrollHeight+3) + 'px;overflow-y:hidden;');
sc = this.scrollHeight;
});
$('#autocomplete').each(function () {
if (sc <=400){
this.setAttribute('style', 'height:' + (0) + 'px;overflow-y:hidden;');
this.setAttribute('style', 'height:' + (sc+2) + 'px;overflow-y:hidden;');
}
}).on('input', function () {
this.style.height = 0;
this.style.height = (sc+2) + 'px';
});
});
function scrolltobototm() {
var target = document.getElementById('autocomplete');
var target1 = document.getElementById('mainInput');
setInterval(function(){
target.scrollTop = target1.scrollHeight;
}, 1000);
};
$( "#mainInput" ).keydown(function(e) {
if (e.keyCode === 9) {
e.preventDefault();
presstabkey();
}
});
function presstabkey() {
if(predicted){
if (apibusy == true){
apibusy= false;
}
if (apibusy== false){
mainInput[0].value = foundName;
autocomplete[0].textContent = '';
}
}else{
return;
}
};
});
#autocomplete { opacity: 0.6; background: transparent; position: absolute; box-sizing: border-box; cursor: text; pointer-events: none; color: black; width: 421px;border:none;} .vc_textarea{ padding: 10px; min-height: 100px; resize: none; } #mainInput{ background: transparent; color: black; opacity: 1; width: 400px; } #autocomplete{ opacity: 0.6; background: transparent;padding: 11px 11px 11px 11px; }
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<textarea id="autocomplete" type="text" class="vc_textarea"></textarea>
<textarea id="mainInput" type="text" name="comments" placeholder="Write some text" class="vc_textarea"></textarea>
]1]1
I need to put a span around the last two characters within a div.
The current structure looks like this:
<div id="price">3199</div>
I need to output something like this:
<div id="price">31<span id="smaller">99</span></div>
It's for a price in a dynamic HTML5 banner ad. I need the ability to format the final two characters smaller than the first two, but the integer from the dynamic feed is simply 3199 or 2099 etc. etc.
I've found some jQuery solutions but I need this to be plain JavaScript.
This fiddle solves it well with jQuery, and I'm sure it doesn't need much tweaking to work without jQuery but my javascript skills can't quite crack it.
You can use replace() with regex .{2}$
var ele=document.getElementById('price');
ele.innerHTML=ele.innerHTML.replace(/.{2}$/,'<span id="smaller">$&</span>')
#smaller{
font-size:10px;
}
<div id="price">3199</div>
Exaple using substring, not as nice as regex but works.
var str = document.getElementById('price').innerHTML;
cut = str.substring(str.length, 2);
document.getElementById('price').innerHTML = str.slice(0, -2) + '<span id="smaller">' + cut + '</span>';
#smaller{
font-size:10px;
}
<div id="price">3199</div>
Fiddle
If you already have event handlers on that HTML, you can't just reset innerHTML, you have to use a more foolproof of breaking up text nodes, using DOM manipulation.
var priceNode = document.getElementById('price');
var textNode = priceNode.firstChild;
var newTextNode = textNode.splitText(textNode.data.length - 2); // Index of where to break the text node
var span = document.createElement('span');
span.className = 'decimal';
span.appendChild(newTextNode);
priceNode.appendChild(span);
.decimal {
font-size: 50%;
}
<div id="price">3199</div>
Here's a case where innerHTML manipulation breaks an existing handler
$('.wholeamount').hover(
function(){ $(this).addClass('highlight')},
function(){ $(this).removeClass('highlight')}
);
$('button').click(function() {
var ele=document.getElementById('price');
ele.innerHTML=ele.innerHTML.replace(/.{2}$/,'<span class="decimal">$&</span>')
});
.highlight {
background-color: yellow;
}
.decimal {
font-size: 50%
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<pre>Hover over the 24 to see it highlight</pre>
<div id="price"><span class="wholeamount">24</span>.99</div> <hr />
<button>Make decimal</button>
<pre>The highlight will be gone when you manipulate innerHTML</pre>
Use slice function to edit the innerHTML
This is the cleanest way to do it.
var ele=document.getElementById('price');
ele.innerHTML = ele.innerHTML.slice(0, 2) + "<span id='smaller'>" + ele.innerHTML.slice(2) + "</span>";
Say I have something like this:
<p>Here are several words in a sentence.</p>
I'm trying to figure out how to display each word, one by one, via keypress or mouseclick, till it reaches the end.
So for example:
Here (click)
Here are (click)
Here are several , etc.
This may be basic, but I'm not very good and I'd love some help!
Thanks!
I just want to make some interventions on #Dean.DePue answer and make the code so you paste it in your project and does the trick:
Your html should look like this:
<div id="adiv"></div>
And you should add this javascript code too:
var index, newsentence, sentence, words;
sentence = "Here are several words in a sentence";
words = sentence.split(" ");
index = 0;
newsentence = "";
$(document).click(function(e) {
if (e.button === 0 && index < words.length) {
newsentence += words[index];
newsentence += " ";
$("#adiv").html(newsentence);
index = index + 1;
}
});
If you've got any doubt of the code just ask!
This has been turned into a jQuery Plugin: word-reveal.
While the other two answers will work (sort of), they aren't very reusable. What happens when you have more than one container you'd like to reveal with more than one sentence? I created a jQuery plugin that can easily be reused throughout the page.
The Setup
Include jQuery on the page
Download the plugin from GitHub, and include it on the page
You're set and ready to go!
The HTML
Set an id for each div or p tag. An example of multiple uses:
<p id="firstRevealer"></p>
<p id="secondRevealer"></p>
<p id="thirdRevealer"></p>
The jQuery
$(document).ready(function() {
$("#firstRevealer").wordReveal({text:"This reveals one word at a time."});
$("#secondRevealer").wordReveal({text:"Adding more text is easy!"});
$("#thirdRevealer").wordReveal({text:"It <b>also</b> works on <em>some</em> tags, since it splits on <b>spaces</b>!"});
});
The CSS
I added CSS on the example, to make clear where you're clicking to reveal the next word. A different answer registered clicks on the document, but any clicks (for a expandable menu, for example) would add a word.
p {
padding: 10px;
margin:10px;
min-height:25px;
background-color:#BADA55
}
The fiddle.
Note
This can easily be extended to act on other events (keypress).
<script type="text/javascript">
var sentence = "Here are several words in a sentence";
var words = sentence.split(" ");
var index = 0;
var newsentence = "";
function clickit() {
newsentence += sentence[index];
index = index + 1;
}
I have this js that is adjusting the size of the first letter of each word in my headers. The only prob is that it appends all the results to each header. So each header on the page, becomes a combo of every header on the page. This jsfiddle link makes what Im saying much more clear:
http://jsfiddle.net/GKYuG/
Can someone please let me know how I can get the correct result?
Try this:
$(document).ready(function() {
var titles = new Array();
titles[0] = 'latest_news';
titles[1] = 'contact_me';
$('.title').each(function(){
var words = $(this).text().split(' ');
var html = '';
$.each(words, function() {
html += '<span style="font-size:200%">'+this.substring(0,1)+'</span>'+this.substring(1) + ' ';
});
$(this).html(html);
});
});
You have to iterate through the selected divs, otherwise you are applying your changes to all the divs selected by the selector.