How can I make this feature faster in Javascript? - javascript

I have created a highlight feature that will highlight anything contained in a <p> red using a user specified keyword. When the submit button is clicked Javascript/jQuery pull the keyword from the input field and compare it to any lines that conain it and then highlight those lines red. It works great... but its slow. Is there another way to do this that is faster when working with over 1000 lines of <p>?
HTML
Keyword: <input type="text" id="highlight_box" class="text_box" value="--Text--" />
<input type="button" id="highlight" value="Highlight" />
<!--Print area for the Access log-->
<div id="access_content" class="tabbed-content">
<ul id="access-keywords-row" class="keywords-row">
<!--When a user submits a keyword it is placed as a tag here so it can be deleted later-->
</ul><br /><br />
<div id="access_log_print" class="print-area">
<p>Some Content</p>
<p>Some more content</p>
<!--Most of the time this could contain 1000's of lines-->
</div>
</div>
JavaScript
//add highlight and tag
$("#highlight").click(
function(){
var id = $("#highlight_box").val();
if(id == "--Text--" || id == ""){
alert("Please enter text before highlighting.");
}else{
$("#access-keywords-row").append("<li><img src=\"images/delete.png\" class=\"delete_filter\" value=\"" + id + "\" /> " + id + " </li>");
$("#access_log_print p:containsi(" + id + ")").css("color","red"); }
});
//remove highlight and tag
$(".keywords-row").on("click", ".delete_filter",
function() {
var val = $(this).val();
//remove element from HTML
$(this).parent().remove();
$("#access_log_print p:containsi(" + val + ")").css("color","black");
});

Adding color, red means adding the style attribute to each p, I think this can be improved adding a class:
p.highlight {
color:red;
}
And replacing
$("#access_log_print p:contains(" + id + ")").css("color","red");
by
$("#access_log_print p:contains(" + id + ")").addClass('highlight');
This probably speeds a little bit the process

I've written a small solution using jQuery's contains() method. Obviously you can throw in some string validation.
http://jsfiddle.net/W2CZB/

Try defining a css class, e.g.:
.red{background-color:#f00;}
and then instead of adding to each "style=background-color:#f00;" you will just .addClass("red");
just less code to put, but still jQuery will have to go thru all lines and if it is a lot then I guess it depends on your machine speed ;)

The following solution will probably increase performance at the cost of space. It works by building a word mapping of the lines and accessing directly to add or remove the highlight class. This solution also keeps a count of the number of times a filter hit that line so it stays highlighted until the last filter is removed. I have tested with a few lines, I am not sure how will it perform with 1000s. You tell us :)
$(function(){
buildIndex();
$("#highlight").click(
function(){
var id = $("#highlight_box").val();
if(id == "--Text--" || id == ""){
alert("Please enter text before highlighting.");
}else{
var filter = $("<li><img src=\"images/delete.png\" class=\"delete_filter\" value=\"" + id + "\" /> " + id + " </li>");
filter.click(function(){
$(this).remove();
removeHighlight(id)
});
$("#access-keywords-row").append(filter);
$.each(index[id], function(i,line){
if (line.highlightCount)
line.highlightCount++;
else {
line.addClass('highlight')
line.highlightCount=1;
}
});
}
});
function removeHighlight(id) {
$.each(index[id], function(i,line){
line.highlightCount--;
if (line.highlightCount<1)
line.removeClass('highlight')
});
};
});
var index={};
function buildIndex(){
$("#access_log_print p").each(function(i) {
var line = $(this)
var words = line.text().split(/\W+/);
$.each(words, function(i,word){
if (!index[word]) { index[word]=[]; }
index[word].push(line);
});
});
}

Related

Javascript writing into a text area from button clicked

I'm wanting to write into a textarea from buttons clicked by users using Javascript or jQuery
Like so:
<html>
<head></head>
<body>
<button>Q</button>
<button>W</button>
<button>E</button>
<button>R</button>
<button>T</button>
<button>Y</button>
<br/><br/>
<textarea></textarea>
</body>
</html>
So a user can click one of the "QWERTY" buttons here and it will be pasted into the box below. Is there a relatively simple way to do this? I've looked up some examples online, but they all go overboard for a novice like me.
It would also be great if we could write text to the textarea characters that aren't on the button
I can't seem to get this to work
<html>
<head>
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script>$('button').click(function(){
$('textarea').text($('textarea').text() + $(this).text());
//$('input:text').val($('textarea').text() );
$('input:text').val($('input:text').val() + ' ' + $(this).data('stuff'));
});
</script>
</head>
<body>
<button data-stuff='stuff_Q'>Q</button>
<button data-stuff='stuff_W'>W</button>
<button data-stuff='stuff_E'>E</button>
<button data-stuff='stuff_R'>R</button>
<button data-stuff='stuff_T'>T</button>
<button data-stuff='stuff_Y'>Y</button>
<br/><br/>
<input type='text'/>
<br/><br/>
<textarea></textarea>
</body>
</html>
If you want to just append to the end of the textarea then use
$('button').on('click', function(){
var letter = $(this).text();
$('textarea')[0].value += letter;
});
Full demo
$(function() {
$('button').on('click', function() {
var letter = $(this).text();
$('textarea')[0].value += letter;
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<button>Q</button>
<button>W</button>
<button>E</button>
<button>R</button>
<button>T</button>
<button>Y</button>
<br/>
<br/>
<textarea></textarea>
JS
$('button').click(function(){
$('textarea').text($('textarea').text() + $(this).text());
//$('input:text').val($('textarea').text() );
$('input:text').val($('input:text').val() + ' ' + $(this).data('stuff'));
});
HTML
<button data-stuff='stuff_Q'>Q</button>
<button data-stuff='stuff_W'>W</button>
<button data-stuff='stuff_E'>E</button>
<button data-stuff='stuff_R'>R</button>
<button data-stuff='stuff_T'>T</button>
<button data-stuff='stuff_Y'>Y</button>
<br/><br/>
<input type='text'/>
<br/><br/>
<textarea></textarea>
Updated per OP needs additional appending values after all the button clicked.
Updated again per OP asking "write into the textbox a letter that is different from the button tag says". I would like to store some data for each button using data() to get it.
FIDDLE
How about this:
$('button').click(function(){
$('textarea').text( $(this).text() );
});
jsFiddle Demo
If you want to take this example a bit futher, we can make it so the letters are appended to the textarea, rather than overwriting what was there previously:
var ta = $('textarea'); //cache selector for re-use (see note at bottom)
$('button').click(function(){
ta.text( ta.text() + $(this).text() );
});
Now, let's add a button to erase the textarea. Here we assign an ID to one specific button and check for it:
var ta = $('textarea');
$('button').click(function(){
if (this.id == "eraseit"){
ta.text('');
}else{
ta.text( ta.text() + $(this).text() );
}
});
jsFiddle Demo #2
Notes:
In the 2nd and 3rd examples, we cached the selector (saved the reference into a variable) for speed.
Each time the code $('textarea') is hit, the DOM is searched for that selector. Caching the selector eliminates all but the initial search. Not at all important in this simple example, but very useful on a large project.
IDs and Classes are extremely important. They are used similarly by css and by javascript/jQuery, for identifying/selecting elements.
The same class name can be applied to multiple elements (e.g. several buttons can have the class), but no two elements are allowed to have the same ID.
In response to your question:
var ta = $('textarea');
$('button').click(function(){
if (this.id == "eraseit"){
ta.text('');
}else{
if ( $(this).hasClass('bob') ) {
ta.text( ta.text() + ' bob' );
}else if ( $(this).hasClass('x') ) {
var ans = prompt('What is your name?');
ta.text( ta.text() + ans );
}else{
ta.text( ta.text() + $(this).text() );
}
}
});
jsFiddle Demo #3

Changing content of dynamically generated textboxs within Divs

I have a system that when a button is pressed a textbox is created on the left of screen and surrounded in a div. Each new text box is named textbox1,2 etc. What I want to do is when the user clicks on the div surrounding it then the textbox replaces the button on the right of the screen and the user is able to type into the newly generated textbox which shall populate the textbox on the left of the screen as well. Reason I want to do this is because the textbox on the left shall be uneditable unless the user clicks on the div and edits the box on the right. I cannot seem to figure out how to do it.
Please forgive any mistakes in missing tags etc as this is a severely edited version. Also before looking at code below you may want to look at the demo I set up. http://jsfiddle.net/JHmaV/335/.
Here is the HTML code
<html>
<body>
<table width ="100%" alight="right">
<td width ="51.5%"></td>
<td> <div id="addField" class="tabOptions">Add field</div></td>
</table>
<table align ="left"style="background-color: white" width="100%">
</tr>
<td width ="50%">
<ul id ="firstColumn">
<div id="identifier">
</div>
</ul>
</td>
<td>
<ul id ="secondColumn" width="5%">
<div id="placeholder">
<div id="mainPanel">
<li><input type="button" class="formCreationButton" id="textAdd" value="Single line text" /></li>
</div>
</div>
</ul>
</td>
<tr>
</table>
And here is the jQuery
var counter = 0;
var textBoxCounter = 1;
var tempStorage = $('div#placeholder').html();
function setValue(target) {
$("textBox1").replaceWith("<h2><input type='text' onkeydown='setValue(this)' value='"+ target.value +"' id='" + target.id +"' name='" + target.id +"')'></h2>");
target.value = target.value;
}
function changeHeader(target) {
if(target.id == "formNameChange"){
$('h2').replaceWith('<h2>' +target.value + '</h2>');
}
else{
$('h4').replaceWith('<h4>' +target.value + '</h4>');
}
}
$(document).ready(function() {
$("#addField").live('click','div',function(){
$('div#placeholder').html(tempStorage);
});
$('#textBox1').live('keyup',function() {
alert("TESTING");
});
$('#textBox1').live('keypress',function ()
{
$('textBox1').val('Changed Value');
alert('test');
});
$('#textBox1').live('onFocus',function ()
{
alert('test');
});
$(".container").live('click','div',function(){
var divID = this.id;
if(divID != ""){
var lastChar = divID.substr(divID.length - 1);
var content = document.getElementById(divID).outerHTML;
var text = document.getElementById(divID).innerHTML;
var textboxId = $('div.container')
.find('input[type="text"]')[lastChar]
.id;
$('div#placeholder').html(content);
alert(textboxId);
}
else{
}
});
$('#textAdd').live('click',function() {
var newdiv = document.createElement('div');
newdiv.innerHTML = "Textbox " + textBoxCounter + " <br><div id='container " + counter + "' class='container'><li><input type='text' onkeydown='setValue(this)' id='textBox " + textBoxCounter +"' name='textBox " + textBoxCounter +"')'></li></div></br>";
document.getElementById("identifier").appendChild(newdiv);
textBoxCounter++
counter++;
});
});​
Please note that I am using jQuery 1.4.4. I have had several attempts at this and cannot do it correctly. Any help will be greatly appreciated, thank you in advance.
Paul.
There are a lot of problems with what you posted. I don't understand what you're doing well enough to fix it all, but here's what I found that's probably causing at least some (if not all) of your problems:
You cannot have spaces in element IDs. That's not valid HTML.
You end up with two inputs that have the same id (the one on the left, and then one that shows up on the right when you click inside the left text box). This will cause any events you set through $('#myId').live to also be bound to this newly created text box.
If you elements with the same id (as I said above). This is not valid HTML.
EDIT:
Here is a jsFiddle that does what you're looking for (I think! hah). I modified your code a bit to get it to work but it should be pretty self explanatory. When you focus a text box, I create a new one, setup some data- attributes that link back to the original text box, and then set a live event listener for the text box. When a keyup happens, I figure out the text box that's related to the one you're typing in and update the value of it.
Make sense?
It should be noted that this isn't the CLEANEST solution out there as the code is kinda messy (it could be cleaned up). This should, however, get you started on a path that works. I'll leave the refactoring up to you :)
http://jsfiddle.net/JHmaV/338/

simpler way of grouping numerous variables by value of select?

thanks for looking.
im still learning the more complex javascript and jquery coding so could do with some help as i have no idea about the following or even if its possible!
i need a better/simpler/shorter way of doing the following (please note i have removed the irrelevant validation etc coding):
'
function Findbox5( myform, box1, box2, box3, box4, box5, Storeall, s1, s2, s3, s4, s5)
{
//store values
Myform = document.forms.myform;
box1 = Myform.box1.value;
box2 = Myform.box2.value;
box3 = Myform.box3.value;
box4 = Myform.box4.value;
box5 = Myform.box5.value;
s1 = Myform.s1.value;
s2 = Myform.s2.value;
s3 = Myform.s3.value;
s4 = Myform.s4.value;
s5 = Myform.s5.value;
//set as one string
Storeall = s1 + ":" + box1 + ";" + s2 + ":" + box2 + ";" + s3 + ":" + box3 + ";" + s4 + ":" + box4 + ";" + s4 + ":" + box5 + ";" ;
// next function...
} '
as you can see i have 5 input boxes and relevant selects for each box(each select has 4 options:1,2,3,4.). when a user enters data into a box they choose a relevant option. all boxes and options must be entered then they submit the form.
this data will be emailed to me as the variable stored under storeall. which would be something like 1:1234;2:1324;1:3232;4:5434;2:3211;
so what i hope to do is simplify this data into the following with either a seperate function or the same one: 1:1234-3232;2:1324-3211;4:5434;
is this possible? or have i done it the easiest way?
any comments or help welcomed, thanks again
First, you'll want to group these things into a single element that can be iterated against. So if your HTML looks like:
<form>
<input name="s1" />
<input name="box1" />
<input name="s2" />
<input name="box2" />
...
</form>
Then it's probably better to do something like:
<form>
<div class="set">
<input class="s" name="s1" />
<input class="box" name="box1" />
</div>
<div class="set">
<input class="s" name="s2" />
<input class="box" name="box2" />
</div>
...
</form>
Now you've established some commonality among these elements, instead of just different names/IDs. Each set of inputs is grouped by the .set class, and within each set, you know there's going to be two inputs: one with the .s class, and one with the .box class. Now iterating against them with JQuery is easy:
var str = "";
$("form div.set").each(
function(index, element)
{
currentValueS = $(element).find("input.s").val();
currentValueBox = $(element).find("input.box").val();
str += currentValueS + ":" + currentValueBox + ";";
}
);
This uses JQuery's .each() function. .each() allows you to provide a function to perform on each of the elements that JQuery finds from the indicated selector. Here, your selector is form div.set, which means "all div elements that have the class of .set, and are found anywhere under any form element". For each of these elements, you'll need to find the value of the <input> element with the .s class, and also the value of the <input> element with the .box class. Then you just add those to your growing str variable.
If you want everything in the form, you should use serializeArray :
$('#my_form').submit(function() {
var str = '';
$.each($(this).serializeArray(), function () {
str += this.name + ":" + this.value + ";";
});
sendByMail(str);
return false;
});

Get the values of input fields within child elements of a specific div

I have my form broken up into sections, and I'm trying to have a script check the number of fields empty in each section. Previously I had written an array with the id's of each section and had it cycle through that array, but i'd like to find a more universal solution that doesn't depend on predefined data.
at first I was trying .find() like so
function blankFields(section){
var totblank = 0;
var inputs = $('#' + section).find('input');
$.each(inputs, function(){
if(this.val() == '') { totblank++; );
}
when that didn't work i tried .serializeArray()
function blankFields(section){
var totblank = 0;
var inputs = $('#' + section + ' input').serializeArray();
$.each(inputs, function(i, field) {
if (field.value == '') { totblank++; }
});
and it gets followed up with
if(totblank > 0){
$("#"+section+"B").html(totblank+" Empty");
} else {
$("#"+section+"B").html("All full!");
}
}
section is the id of a div, the div has a table with form inputs.
This is my first time using these functions so I'm not really sure where I'm going wrong. Feels like I'm expecting the output to be something its not.
There are a few syntax errors in your code. Fixed:
function blankFields(section){
var totblank = 0;
var inputs = $('#' + section).find('input');
inputs.each(function(){
if($(this).val() == '') totblank++;
});
alert(totblank);
}
I think you can try to find all empty fields, and browse back the DOM to retrieve the parent section.
After, store results in associative array like "section" => count. If the section isn't in array, add an entry "newsection" => 1, else if section is in, increment section's count.
I think this part should give you an error
+= totblank
Or maybe I don't know JavaScript syntax well enough..
Anyway, try this instead
totblank++;
Overall, it's also good to make sure you're really dealing with Numbers. Common mistake is to get .val() and do some maths for example '1' + 1. Result is 11 and is something you probably didn't want
Wrote a little snippet that does the trick : http://jsfiddle.net/9jdmH/
$('#check').click(function() {
$('.section').each(function() {
checkSection($(this));
});
})
function checkSection(section) {
//assuming we're getting a jquery object. easily modifieable
section.find('input[type="text"]').each(function() {
if ($(this).val() == '') {
$('#log').prepend('<p>Found an empty input section ' + section.index() + '</p>');
}
})
}
This will work for a general markup type.
You'll need to make some changes depending on what kind of information you want. I haven't used IDs so I'm just using the .index() point out which ones are empty. As long as you get/make a jquery reference in the checkSection method you'll be fine.
My take on this:
http://jsfiddle.net/fqBrS/
function findNumberOfEmptyInputs(section) {
var inputs = $('#'+section+' input[type="text"]');
alert(inputs.filter(":empty").length);
}
findNumberOfEmptyInputs("section2");
The good thing is that you do not have to (explictly at least) use a loop.
Comments appreciated :)
code with sample. Should give pretty much the same output you have now.
<html>
<head>
<script type="text/javascript" src="Scripts/jquery-1.4.2.min.js"></script>
<script type="text/javascript">
function checkBlank(f){
$(f).find(".section").each(function () {
var c = $(this).find("input[value='']").size()
$("#" + $(this).attr("id") + "B").text(c ? c + " Empty" : "All full");
})
return false
}
</script>
</head>
<body>
<form onsubmit="return checkBlank(this)">
<div class="section" id="section1">
<input type="text">
<input type="text">
</div>
<div class="section" id="section2">
<input type="text">
<input type="text">
</div>
<input type="submit" value="Click">
</form>
<div id="section1B"></div>
<div id="section2B"></div>
</body>
</html>

add templated div to page multiple times with different ids

I am using ASP.Net MVC along with Jquery to create a page which contains a contact details section which will allow the user to enter different contact details:
<div id='ContactDetails'>
<div class='ContactDetailsEntry'>
<select id="venue_ContactLink_ContactDatas[0]_Type" name="venue.ContactLink.ContactDatas[0].Type">
<option>Email</option>
<option>Phone</option>
<option>Fax</option>
</select>
<input id="venue_ContactLink_ContactDatas[0]_Data" name="venue.ContactLink.ContactDatas[0].Data" type="text" value="" />
</div>
</div>
<p>
<input type="submit" name="SubmitButton" value="AddContact" id='addContact' />
</p>
Pressing the button is supposed to add a templated version of the ContactDetailsEntry classed div to the page. However I also need to ensure that the index of each id is incremented.
I have managed to do this with the following function which is triggered on the click of the button:
function addContactDetails() {
var len = $('#ContactDetails').length;
var content = "<div class='ContactDetailsEntry'>";
content += "<select id='venue_ContactLink_ContactDatas[" + len + "]_Type' name='venue.ContactLink.ContactDatas[" + len + "].Type'><option>Email</option>";
content += "<option>Phone</option>";
content += "<option>Fax</option>";
content += "</select>";
content += "<input id='venue_ContactLink_ContactDatas[" + len + "]_Data' name='venue.ContactLink.ContactDatas[" + len + "].Data' type='text' value='' />";
content += "</div>";
$('#ContactDetails').append(content);
}
This works fine, however if I change the html, I need to change it in two places.
I have considered using clone() to do this but have three problems:
EDIT: I have found answers to questions as shown below:
(is a general problem which I cannot find an answer to) how do I create a selector for the ids which include angled brackets, since jquery uses these for a attribute selector.
EDIT: Answer use \ to escape the brackets i.e. $('#id\\[0\\]')
how do I change the ids within the tree.
EDIT: I have created a function as follows:
function updateAttributes(clone, count) {
var f = clone.find('*').andSelf();
f.each(function (i) {
var s = $(this).attr("id");
if (s != null && s != "") {
s = s.replace(/([^\[]+)\[0\]/, "$1[" + count + "]");
$(this).attr("id", s);
}
});
This appears to work when called with the cloned set and the count of existing versions of that set. It is not ideal as I need to perform the same for name and for attributes. I shall continue to work on this and add an answer when I have one. I'd appreciate any further comments on how I might improve this to be generic for all tags and attributes which asp.net MVC might create.
how do I clone from a template i.e. not from an active fieldset which has data already entered, or return fields to their default values on the cloned set.
You could just name the input field the same for all entries, make the select an input combo and give that a consistent name, so revising your code:
<div id='ContactDetails'>
<div class='ContactDetailsEntry'>
<select id="venue_ContactLink_ContactDatas_Type" name="venue_ContactLink_ContactDatas_Type"><option>Email</option>
<option>Phone</option>
<option>Fax</option>
</select>
<input id="venue_ContactLink_ContactDatas_Data" name="venue_ContactLink_ContactDatas_Data" type="text" value="" />
</div>
</div>
<p>
<input type="submit" name="SubmitButton" value="AddContact" id='addContact'/>
</p>
I'd probably use the Javascript to create the first entry on page ready and then there's only 1 place to revise the HTML.
When you submit, you get two arrays name "venue_ContactLink_ContactDatas_Type" and "venue_ContactLink_ContactDatas_Data" with matching indicies for the contact pairs, i.e.
venue_ContactLink_ContactDatas_Type[0], venue_ContactLink_ContactDatas_Data[0]
venue_ContactLink_ContactDatas_Type[1], venue_ContactLink_ContactDatas_Data[1]
...
venue_ContactLink_ContactDatas_Type[*n*], venue_ContactLink_ContactDatas_Data[*n*]
Hope that's clear.
So, I have a solution which works in my case, but would need some adjustment if other element types are included, or if other attributes are set by with an index included.
I'll answer my questions in turn:
To select an element which includes square brackets in it's attributes escape the square brackets using double back slashes as follows: var clone = $("#contactFields\[0\]").clone();
& 3. Changing the ids in the tree I have implemented with the following function, where clone is the variable clone (in 1) and count is the count of cloned statements.
function updateAttributes(clone, count) {
var attribute = ['id', 'for', 'name'];
var f = clone.find('*').andSelf();
f.each(function(i){
var tag = $(this);
$.each(attribute, function(i, val){
var s = tag.attr(val);
if (s!=null&& s!="")
{
s = s.replace(/([^\[]+)\[0\]/, "$1["+count+"]");
tag.attr(val, s);
}
});
if ($(this)[0].nodeName == 'SELECT')
{ $(this).val(0);}
else
{
$(this).val("");
}
});
}
This may not be the most efficient way or the best, but it does work in my cases I have used it in. The attributes array could be extended if required, and further elements would need to be included in the defaulting action at the end, e.g. for checkboxes.

Categories