Highlight slices of text with Javascript - javascript

Problem
Suppose that in the backend of my Web application I have a generic string of letters:
seq = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
and an array of positions in such a string:
pos = [(0, 2), (4, 8)]
I need to render this sequence in the frontend by splitting it every n characters. Then when a user clicks a button I need to highlight the sequence between two parameters (taken from pos) for which the button refers to.
My solution
I solve this by implementing a Javascript function formatSequence which splits seq every n characters and iterates through the pos array in order to wrap each substring inside a span tag. The result is something like this:
<pre>
<span class="A">AA</span>AA<span class="B">A</span>
<span class="B">AAA</span>AA
AAAAA
</pre>
When the user clicks the button referring to the class A I simply change the CSS background rule for class A.
It works :) But the function formatSequence is way too complicated imho. It was a pain dealing with multiple lines. I prefer not posting the code since I am looking for other approaches not changing the code of such function.
A better solution?
I think that a (better?) solution would be to implement a function that given two parameters start and end it dynamically highlights the text between them. But it appears to be even more complicated than the previous one (remember that the sequence must be split every n characters and thus the highlight must be multilines).
Any suggestions? Better approach to solve this?

One simple solution would be just to print the full seq multiple times into the HTML and hide every row you don't need at the time. When a user clicks on a button, another row would be displayed (and the first one would be hidden).
HTML:
<div class="rows"></div>
<div class="buttons"></div>
JavaScript (depending on jQuery):
(function generateRowsAndButtons() {
var sequence = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
var position = [ [0,2], [4,8] ];
var $rows = $('.rows');
var $buttons = $('.buttons');
for(var i = 0; i < position.length; i++) {
if(position[i].length !== 2 || position[i][0] > position[i][1]) {
console.log("every position array needs exactly two values with the second larger than the first one");
continue;
}
// the index is used for mapping the button the highlight position
var row = '<div class="row" data-index="' + i + '" style="display: none;">';
// you should add some checks here, if position larger then the length of the string to avoid some misbehaviors. this is of course only necessary if you aren't validating the values on another place.
row += sequence.substring(0, position[i][0]);
row += '<span class="highlighted">';
row += sequence.substring(position[i][0], position[i][1]);
row += '</span>';
row += sequence.substring(position[i][1]);
row += '</div>';
var $row = $(row);
$rows.append($row);
// a button needs the index to find the link the highlighted value
var $button = $('<button data-index="' + i + '">' + position[i] + '</button>');
$buttons.append($button);
}
$buttons.find('button').click(function() {
var index = $(this).data('index');
// hide every row, except the one with the correct index
$rows.find('.row').hide().filter('[data-index="' + index + '"]').show();
});
})();
CSS:
.row .highlighted {
background: yellow;
}
Here is a jsFiddle: https://jsfiddle.net/y8uoou1L/2

Related

Adding clickable buttons or links for side bar

I'm having trouble adding links or buttons to my side bar dynamically. I'm trying to use JavaScript to add the appropriate amount of elements or buttons(I don't know what is better). In essence this is used to make a JS quiz website where each of the buttons in the sidebar will jump you to the question(to make it more clear: Question 4 takes you to 4th question and so on)
This is my side bar:
<div id="mySidenav" class="sidenav">
×
Question
</div>
My JS trying to make the elements but failing to do so:
//add question numbers to the side bar
function questionNav(){
for(var i = 0; i < numberOfQuestions; i++){
document.getElementById("mySidenav").innerHTML = "Question " + (i+1) + "<br/>";
//var newSideBarElm = "Question " + (i+1) + "<br/>";
//document.getElementById("mySidenav").insertAdjacentHTML = ('beforeend',newSideBarElm);
}
}
I've been trying numerous different methods but I can't get it to work and would greatly appreciate if someone was able to help me.
Here is the full code of the site in case you would like to see how I'm doing everything else: https://pastebin.com/hrSADLQy
So assuming that I understand your question correctly, what you are currently doing is targeting the div with the id 'mySideNav' and overwriting its content by assigning a new value to its innerHTML attribute. What you should be doing instead is
Create a new anchor element
Use the innerHTML attribute to insert your desired value (i.e "Question N")
Append your newly created element to your 'mySideNav' div element.
I wrote a small demo for you to see my answer in action, but will post my code below this answer for you to see as well.
// Grab and Store Element to append questions to
var mySideNav = document.getElementById('mySidenav');
// Designate number of questions
var numberOfQuestions = 10;
// Loop as many times as there are questions
for (let i = 0; i < numberOfQuestions; i++) {
// Step 1: Create a new anchor element
let newQuestion = document.createElement('a');
// Assign href to whatever you want
newQuestion.href = '#';
// Step 2: Use the innerHTML attribute to insert your desired value
newQuestion.innerHTML = 'Question ' + i + '<br>';
// Step 3: Append your newly created element to your 'mySideNav' div element.
mySideNav.appendChild(newQuestion);
}
I hope this helps!
According to your question and the code you provided i think you need to define a div with an onclick() property to make the corresponding question visible. That could be something like this (assuming you can access the number of questions inside this function):
function questionNav(){
for(var i = 0; i < numberOfQuestions; i++){
document.getElementById("mySidenav").innerHTML += "<div style='cursor:pointer' onclick=navigateToQuestion(" + (i+1) + ")/> Question " + (i+1) + "</div>";
}
}
And then define a the onclick function "navigateToQuestion" to show the question that is passed as parameter, maybe could be somethink like this:
function navigateToQuestion(question){
document.getElementById("question_" + currentQuestionInView).style.display = "none";
currentQuestionInView = question;
document.getElementById("question_" + question).style.display = "block";
}
Haven't tested the code but i think it should work.
Hope you get your problem solved :)

For every textarea i create, i want it to have its 'personal word count' on it.

The code below is to appear additional 2 textbox and 1 textarea everytime i click a button.
var x=1;
var count=0;
$('body').on('click','#add',function()
{
if(count < 6)
{
$('#div').append("<div class='line'><input type='text' name = 'txta"+x+ "' id='txta"+ x +"'><span class =wordtab></span> <textarea rows='9' onkeyup='countChar2(this)' cols='50' name = 'txtc"+x+ "' id='txtc"+ x +"'></textarea> <span class =wordtab></span><input style = 'width:50px' type='text' name = 'txtb"+x+"' id='txtb"+ x +"'><span class =wordtab></span><button class='delete' value ='Delete Row'>Delete Row</button></div><div style='margin-left: 750px' id='charNum" + x + "'></div>");
count++;
x++;
}
else
alert("Maximum 6 Skills");
});
$('body').on('click','.delete',function()
{
$(this).closest('.line').remove();
count--;
});
The below function is the code that i currently have (which i know its wrong) to put in a counter for every textarea that i added in.
function countChar2(val)
{
var len = val.value.length;
if (len >= 200)
{
val.value = val.value.substring(0, 500);
}
else
{
var id = "charNum" + x;
$(id).text((200 - len)+" words left");
}
};
So my goal is that everytime i click on the add row and start typing on the textarea, it will show the word count for that particular texarea just right below the textarea box.
To get a unique counter added to each textarea, you could append another div to the textarea with a specific class e.g.
Set the HTML structure to something such as:
<textarea></textarea><div class='text-count-area'>Word Count: 0</div>
Add the following JS at the point where each textarea is added e.g. just before 'count++' in your original code (note: this is not the most efficient way of doing this, but this will work easily with your current code):
// Bind the text area to the keyup event
$("textarea").on('keyup', function(val) {
// Simple word count
var words = this.value.match(/\S+/g).length;
// Write the word count to the immediate text-count-area div afterwards
$(this).next(".text-count-area").text("Text count" + words);
});
The word count is kept simple here for readability, but the logic from other answers (highlighted in the comments) could be implemented at this stage.
A JS Fiddle demo of this working is here.
Let see your example:
You add each div by .append method, it's correct
You count input symbols by onkeyup event, it's correct too
All you need is update your countChar2 function because this function has wrong body in that lines:
var id = "charNum" + x;
$(id).text((200 - len)+" words left");
First of all: try to debug your code via developer tools in your favorite browser with breaks in that lines. This step can give your much more and quickly info than posting question in stackoverflow :)
For onkeyup event you should use link to this object instead of id inside your function:
$(val).parent().find('.words-left').val((200 - len));
This line uses val as a link to textarea object in just appended line. .parent() gives you access to wrapper and find() finds input for words left field. (I've added '.words-left' class to your input, see my fiddler bellow). And this code works in stage of your just added line.
Your code of $('body').click() should be executed, when document is fully loaded and DOM ready. But all your ids that you will create in future doesn't appends that DOM. That's why your delete function works properly - that function uses class selector instead of id.
Proposed by me code doesn't uses id selector because it is not needed. All that needs to me - link to current object in new line, val - what I need for that operation.
BTW: When you implement function that works with objects, such as onkeyup='countChar2(this)', better way to use object as passed variable - var countChar = function(obj) {. Because val is using for scalar values in common way.
You can check my code here https://jsfiddle.net/jo0cd3yr/1/

Directing animation to correct place

In my word game there is a grid with 3 letter words.
The aim of the game is to spell the words by clicking on the corresponding letters on the side.
When an area in the grid is highlighted it indicates to the user the word to spell. The user clicks the letters on the side of the grid and they should move to the highlighted area.
I have recently changed "drop-box" to a div in the following piece of code and now the animation takes the letter to the top corner of the grid before taking it to the correct position.
var row = '<tr>';
var spaceAvailInRow = numLetters;
while (spaceAvailInRow) {
var word = getWordToFitIn(spaceAvailInRow, unusedShuffledWords);
guesses[word] = [];
spaceAvailInRow -= word.length;
for (var k = 0; k < word.length; ++k) {
row += '<td data-letter="' + word[k] + '" data-word="' + word + '"><div class="drop-box"></div></td>';
}
}
row += '</tr>';
tbl.append(row);
}
$(".container").append(tbl);
Can someone tell me why the animation has broke now I have changed this?
Fiddle: http://jsfiddle.net/7Y7A5/27/
The problem is that position() gets an element's position relative to its (offset) parent - in your case, each drop-box div is positioned at 0,0 releative to its containing td. What you need is the containing td's position(), not the drop-box's.
I've changed things around a little, and the fiddle can be seen here: http://jsfiddle.net/mHDkV/1/
I've changed the targetPos variable to refer to the position of the parent td, and applied the occupied class to that td as well. Take a look though the code in the jsFiddle - it should hopefully make sense.

Is there an easy way to add multiple javascript images together via `imgCounter.src =`

In photoshop I created images 0.png thru 9.png and know I can change each digit to simulate a counter by changing each picture of each digit as it counts down.
IDEA 1:
One way I thought would be to have multiple lines of
HTML:
<div align="right"><img src="graphics/odometers/1.png" /id="digit1">
<img src="graphics/odometers/2.png" /id="digit2">
<img src="graphics/odometers/3.png" /id="digit3"></div>
JAVASCRIPT:
var imgCounter = document.getElementById('digit1');
imgCounter.src = "graphics/odometers/white and blue with black background/1.png"; // shows the digit 1
var imgCounter = document.getElementById('digit2');
imgCounter.src = "graphics/odometers/white and blue with black background/2.png"; // shows the digit 2
// etc for as many digits as I want to show
and change the digits in javascript by each ID. It's not hard but for flexibility can I have 1 image ID and string 3 .png's together in javascript? Like this...
IDEA 2: Not sure if this is possible.
HTML:
<div align="right"><img src="" /id="formtimer"></div>
JAVASCRIPT:
// somehow show 3 graphics in a row 123 without having 3 ID tags, only 1 ID tag
var imgCounter = document.getElementById('formtimer');
imgCounter.src = "graphics/odometers/white and blue with black background/1.png";
I know the example above only shows the digit 1 and doesn't do all 3 digits... (because I don't know if it's possible).
Is there a way to display 3 pictures together? lol. Easily? I know anything can technically be done the hard way...
If it's a big pain in the butt I'm ok with IDEA 1 but it's less "freedom" because I have to encode the HTML with multiple ID tags which is tedius...
Just curious what you all think and if you have a solution. :) Much appreciated.
ALSO:
Is it ok to leave the img src="" as a NULL or empty string if I'll later be sticking an image in via javascript?
You don't need to include the <img> elements in the source html at all, you can add them with JavaScript and not even give them ids.
At the point in your source where you want the counter just put an empty div with an appropriate id.
When the counterSet() function below is called with the id of the container div and a value it creates new child <img> elements in the div with src set appropriately, but reuses any existing child <img> elements that are there from any previous value that was set. If the new value has fewer digits than the last than the function removes the leftover <img> elements:
<div id="counter1"></div>
<script>
function counterSet(counterId, val) {
var c = document.getElementById(counterId),
i,
d;
for (i = 0; i < val.length; i++) {
// if the container already had enough child img elements
// for current digit set current img's src, otherwise add
// new img to end
if (i < c.childNodes.length)
c.childNodes[i].src = "graphics/odometers/" + val.charAt(i) + ".png";
else {
d = document.createElement("img");
d.src = "graphics/odometers/" + val.charAt(i) + ".png";
c.appendChild(d);
}
}
// if the container already had too many child img elements
// (from a previous value) remove the leftover digits
while (c.childNodes.length > val.length)
c.removeChild(c.childNodes[val.length]);
}
counterSet("counter1","218");
</script>
I would just use three separate ids.
You can use something like this to set the src attributes:
for (i = 1; i <= NUMBER_OF_IMAGES; i++) {
var img = document.getElementById('img_' + i);
imgr.src = 'images/image_' + i + '.png";
)

Generate page numbers using javascript/jquery?

How to generate page numbers like the below using javascript/jquery?
If the 5 th page is selected i have to show 3,4 and 6,7 and also 1,last page with prev,next...
Any suggestion....
EDIT:
How to work with json data that use these pagination div? (ie) My json data contains 50 records
I want to 10 in page 1 and so on... How to paginate json data with these numbers...
I want a jquery function to pass currentpageno,lastpagenumber and the function should generate me page numbers like the below for me
If it is the first page
If it is in the middle,
If it is the last page,
Second EDIT:
I have tried this function but doesn't seem to get the desired result
function generatePages(currentPage, LastPage) {
if (LastPage <= 5) {
var pages = '';
for(var i=1;i<=5;i++)
{
pages += "<a class='page-numbers' href='#'>" + i + "</a>"
}
$("#PagerDiv").append(pages);
}
if (LastPage > 5) {
var pages = '';
for (var i = 1; i <= 5; i++) {
pages += "<a class='page-numbers' href='#'>" + i + "</a>"
}
$("#PagerDiv").append(pages);
}
}
I have the lastPage and currentPage values please help me out getting this...
What you are looking for is called "pagination" and there's (as always) a jQuery plugin that does the job for you:
http://d-scribe.de/webtools/jquery-pagination/demo/demo_options.htm
(Download it here)
Edit: Since you don't seem to be able to get it working, here is one way (of several different) how you can use the plugin.
Step 1: Generate markup from your JSON-data like the following:
<div id="display">
<!-- This is the div where the visible page will be displayed -->
</div>
<div id="hiddenData">
<!-- This is the div where you output your records -->
<div class="record">
<!-- create one record-div for each record you have in your JSON data -->
</div>
<div class="record">
</div>
</div>
The idea is to copy the respective record to the display div when clicking on a page-link. Therefore, the plugin offers a pageSelect-callback function. Step 2 is to implement this function, for instance:
function pageselectCallback(pageIndex, jq){
// Clone the record that should be displayed
var newContent = $('#hiddenData div.record:eq('+pageIndex+')').clone();
// Update the display container
$('#display').empty().append(newContent);
return false;
}
This would mean that you have one page per record. If you want to display multiple records per page, you have to modify the above function accordingly.
The third and final step is to initialize the whole thing correctly.
function initPagination() {
// Hide the records... they shouldn't be displayed
$("#hiddenData").css("display", "none");
// Get the number of records
var numEntries = $('#hiddenData div.result').length;
// Create pagination element
$("#pagination").pagination(numEntries, {
num_edge_entries: 2,
num_display_entries: 8, // number of page links displayed
callback: pageselectCallback,
items_per_page: 1 // Adjust this value if you change the callback!
});
}
So, you just have to generate the HTML markup from your JSON data and call the init-function afterwards.
It's not that difficult, is it?
yeah #SLaks is right. nothing too crazy here. You will have 2 variables currentPageNumber and lastPageNumber.
$("div.paginator").append("<a...>prev</a>");
$("div.paginator").append("<a...>1</a>");
for (x = (currentPageNumber - 2; x <= (currentPageNumber + 2); x++) {
$("div.paginator").append("<a...>"+ x +"</a>");
}
$("div.paginator").append("<a...>"+ lastPageNumber +"</a>");
$("div.paginator").append("<a...>next</a>");
// you could apply styles to and a tag in the div.paginator
// you could apply a special class to the a tag that matches the currentPageNumber
// you can also bind some click events to each a tag and use the $(this).text() to grab the number of the page to go to
Use THIS or THAT plugin. They're both easy html pagination plugins. Put everything in the html at once and paginate with one of those.
Add two new hidden inputs
<input type='hidden' id='current_page' />
<input type='hidden' id='show_per_page' />
Next add an empty div to create pagination controls
<!-- An empty div which will be populated using jQuery -->
<div id='page_navigation'></div>
$(document).ready(function(){
//how much items per page to show
var show_per_page = 5;
//getting the amount of elements inside content div
var number_of_items = $('#content').children().size();
//calculate the number of pages we are going to have
var number_of_pages = Math.ceil(number_of_items/show_per_page);
//set the value of our hidden input fields
$('#current_page').val(0);
$('#show_per_page').val(show_per_page);
//now when we got all we need for the navigation let's make it '
/*
what are we going to have in the navigation?
- link to previous page
- links to specific pages
- link to next page
*/
var navigation_html = '<a class="previous_link" href="javascript:previous();">Prev</a>';
var current_link = 0;
while(number_of_pages > current_link){
navigation_html += '<a class="page_link" href="javascript:go_to_page(' + current_link +')" longdesc="' + current_link +'">'+ (current_link + 1) +'</a>';
current_link++;
}
navigation_html += '<a class="next_link" href="javascript:next();">Next</a>';
$('#page_navigation').html(navigation_html);
//add active_page class to the first page link
$('#page_navigation .page_link:first').addClass('active_page');
//hide all the elements inside content div
$('#content').children().css('display', 'none');
//and show the first n (show_per_page) elements
$('#content').children().slice(0, show_per_page).css('display', 'block');
});
function previous(){
new_page = parseInt($('#current_page').val()) - 1;
//if there is an item before the current active link run the function
if($('.active_page').prev('.page_link').length==true){
go_to_page(new_page);
}
}
function next(){
new_page = parseInt($('#current_page').val()) + 1;
//if there is an item after the current active link run the function
if($('.active_page').next('.page_link').length==true){
go_to_page(new_page);
}
}
function go_to_page(page_num){
//get the number of items shown per page
var show_per_page = parseInt($('#show_per_page').val());
//get the element number where to start the slice from
start_from = page_num * show_per_page;
//get the element number where to end the slice
end_on = start_from + show_per_page;
//hide all children elements of content div, get specific items and show them
$('#content').children().css('display', 'none').slice(start_from, end_on).css('display', 'block');
/*get the page link that has longdesc attribute of the current page and add active_page class to it
and remove that class from previously active page link*/
$('.page_link[longdesc=' + page_num +']').addClass('active_page').siblings('.active_page').removeClass('active_page');
//update the current page input field
$('#current_page').val(page_num);
}

Categories

Resources