Character Counter Super Slow - jQuery - javascript

I wrote this:
$('[name=item-title]').live('keyup',function(k){
char_limit=parseInt($('[data-charlimit]').attr('data-charlimit'));
total = char_limit-parseInt($(this).val().length);
$('.charcount span').text(char_limit-parseInt($(this).val().length));
});
But after the first few words it starts going super slow where I'll type and the words will show a millisecond after. If i get to like 250 words it gets nearly 3/4 of a second behind it seems. Any ideas why? To me what i wrote seems fairly minimal...

You're doing a DOM traversal per character and you're not sure why it's slow?
// calculate upper limit once -- it won't change, presumably
var char_limit=parseInt($('[data-charlimit]').attr('data-charlimit'));
$('[name=item-title]').live('keyup',function(k){
// length is already numeric, no need to parse it
var remaining = char_limit - $(this).val().length;
$('.charcount span').text( remaining ); // use the result, note I renamed it
});

If possible, precalculate the char_limit. It won't change during the typing.
Use the total variable in your fourth line, or leave out its calculation completely
Leave out the parseInts except for maybe the first one, they're useless.
This would give you
var char_limit=parseInt($('[data-charlimit]').attr('data-charlimit'));
$('[name=item-title]').live('keyup',function(k){
$('.charcount span').text(char_limit-$(this).val().length);
});
I'm not sure if there are multiple char_limits though, as this would ruin those. It seems that your current $('[data-charlimit]') approach doesn't allow for those anyway.

Related

Numbers from inside a string without regex?

I'm making a bot for a gaming chatroom with some friends, but I've hit an impasse. Is there a reliable way to get numbers from inside a string of text that won't completely break an inexperienced script kiddy's brain? Here's the best I've been able to come up with so far, variables simplified slightly for illustration's sake:
var k = [0];
function dieRoll(m,n) {
for(i = 0; i < m; i++) {
k[i] = Math.floor(Math.random()*n)+1;
}
}
var m = text[5];
var n = text[7];
if (text === 'roll '+m+'d'+n) {
dieRoll(m,n)
console.log(k);
}
The biggest problem as-is is that it's limited to single-digit input.
EDIT: Looping through the text looking for integers is exactly the kind of thing I'm looking for. I don't have much experience with programming, so I probably tend to end up with overly complicated and confusing messes of spaghetti code that would embarrass anyone remotely professional. As for the format of the input I'm looking for, "roll [number of dice]d[highest number on the dice]". For anyone who doesn't know, it's the notation most tabletop rpgs use. For example, "roll 2d6" for two normal six-sided dice.
EDIT: It's not that I'm necessarily against regex, I just want to be able to understand what's going on, so that if and when I need to edit or reuse the code it I can do so without going completely insane.
EDIT: Thank you all very much! split() seems to be exactly what I was looking for! It'll probably take some trial and error, but I think I'll be able to get her working how she's supposed to this weekend (Yes I call my bots 'she').
Basically, you need to look at the format of the input you're using, and identify certain facts about it. Here are the assumptions I've taken based on your question.
1) The "roll" command comes first followed by a space, and
2) After the command, you are provided with dice information in the form xdy.
Here's something that should work given those constraints:
function getRollParameters(inputCommand) {
var inputWords = inputCommand.split(' '); //Split our input around the space, into an array containing the text before and after the space as two separate elements.
var diceInfo = inputWords[1]; //Store the second element as "diceInfo"
var diceDetails = diceInfo.split('d'); //Split this diceInfo into two sections, that before and after the "d" - ie, the number of dice, and the sides.
//assign each part of the dicedetails to an appropriate variable
var dice = diceDetails[0];
var sides = diceDetails[1];
//return our two pieces of information as a convenient object.
return {
"dice": dice,
"sides": sides
};
}
//a couple of demonstrations
console.log(getRollParameters("roll 5d8"));
console.log(getRollParameters("roll 126d2"));
Effectively, we're first splitting the string into the "command", and the "arguments" - the information we want. Then, we split our arguments up using the "d" as a midpoint. That gives us two numbers - the one before and the one after the d. Then we assign those values to variables, and can use them however we like.
This obviously won't deal with more creative or flexible inputs, and isn't tested beyond the examples shown but it should be a decent starting point.

undertanding javascript pagination math problem

I am trying to understand how to approach math problems such as the following excerpt, which was demonstrated in a pagination section of a tutorial I was following.
const renderResults = (arrayOfItems, pageNum = 1, resultsPerPage = 10) => {
const start = (pageNum - 1) * resultsPerPage;
const end = pageNum * resultsPerPage;
arrayOfItems.splice(start, end).forEach(renderToScreenFunction);
};
In the tutorial this solution was just typed out and not explained, which got me thinking, had I not seen the solution, I would not have been able to think of it in such a way.
I understood the goal of the problem, and how splice works to break the array into parts. But it was not obvious to me how to obtain the start and end values for using the splice method on an array of of indefinite length. How should have I gone about thinking to solve this problem?
Please understand, I am learning programming in my spare time and what might seem simple to most, I have always been afraid and struggle with math and I am posting this question in hopes to get better.
I would really appreciate if anyone could explain how does one go about solving such problems in theory. And what area of mathematics/programming should I study to get better at such problems. Any pointers would be a huge help. Many thanks.
OK, so what you're starting with is
a list of things to display that's, well, it's as long as it is.
a page number, such that the first page is page 1
a page size (number of items per page)
So to know which elements in the list to show, you need to think about what the page number and page size say about how many elements you have to skip. If you're on page 1, you don't need to skip any elements. What if you're on page 5?
Well, the first page skips nothing. The second page will have to skip the number of elements per page. The third page will have to skip twice the number of elements per page, and so on. We can generalize that and see that for page p, you need to skip p - 1 times the number of elements per page. Thus for page 5 you need to skip 4 times the number of elements per page.
To show that page after skipping over the previous pages is easy: just show the next elements-per-page elements.
Note that there are two details that the code you posted does not appear to address. These details are:
What if the actual length of the list is not evenly divisible by the page size?
What if a page far beyond the actual length of the list is requested?
For the first detail, you just need to test for that situation after you've figured out how far to skip forward.
Your function has an error, in the Splice method
arrayOfItems.splice(start, end).forEach(renderToScreenFunction);
The second argument must be the length to extract, not the final
index. You don't need to calculate the end index, but use the
resultsPerPage instead.
I've rewrite the code without errors, removing the function wrapper for better understanding, and adding some comments...
// set the initial variables
const arrayOfItems =['a','b','c','d','e','f','g','h','i','j','k','l','m'];
const pageNum = 2;
const resultsPerPage = 5;
// calculate start index
const start = (pageNum - 1) * resultsPerPage; // (2-1)*5=5
// generate a new array with elements from arrayOfItems from index 5 to 10
const itemsToShow = arrayOfItems.splice(start, resultsPerPage) ;
// done! output the results iterating the resulting array
itemsToShow.forEach( x=> console.log(x) )
Code explanation :
Sets the initial parameters
Calculate the start index of the array, corresponding to the page you try to get. ( (pageNum - 1) * resultsPerPage )
Generates a new array, extracting resultsPerPage items from arrayOfItems , starting in the start index (empty array is returned if the page does not exist)
Iterates the generated array (itemsToShow) to output the results.
The best way to understand code, is sometimes try to run it and observe the behavior and results.

How to truncate complex html by lines

I am trying to truncate complex html by lines in order to be able to display a show more link after a certain number of lines has been reached. I found a great library trunk8.js which truncates by lines..but if falls short when it comes to truncating complex html. So for my particular case I overrode the truncate function so that I can handle complex the using another truncation function which gracefully leaves complex html tags intact. Truncation will work great with html but I am stuck on how to accurately calculate where to put show more more based on the number of lines
As seen in the image above I am trying to truncate to 7 lines but if the user input contains white spaces shown in yellow my calculations will be wrong because I am not accounting for the white spaces. My initial line of thought was that if I can calculate the length of the spaces in yellow for each line and convert it to characters, I can add this offset to the maximum number number of characters, then I can know where to put approximately the show more link. Is this the best approach to this problem and if not ,any suggestions to make my life easier.
This is a plunker of what I have tried so far and I am stuck in my truncateHTML() function in the trunk8.js where I am only now truncating based on the maximum length of the string.
Eureka!! After a couple of google searches and heavy debugging sprints, I stumbled upon a library truncate.js which I customized the truncatedNestednode() function for my needs.
element.appendChild(child);
/** Customized--here **/
var clonedNode = {};
if ($clipNode.length) {
if ($.inArray(element.tagName.toLowerCase(), BLOCK_TAGS) >= 0) {
// Certain elements like <li> should not be appended to.
$element.after($clipNode);
}
else
{ //Removed the commented line to put showmore next to the last line of text
$element.prev().append($clipNode);
//$element.append($clipNode);
}
}
In case someone faced this problem in the past, I have posted my revised plunker here

How do I decrease the governance cost of the following code

It netsuite there is a limit on how frequently you can use certain APIs (as well as certain scripts). For what I am doing I believe the following is the applicable cost:
nlapiLoadSearch: 5
nlobjSearchResultSet.getSearch(): 10
It takes about an hour, but every time my script(which follows) errors out, probably due to this. How do I change it to make it have less governance cost?
function walkCat2(catId, pad){
var loadCategory = nlapiLoadRecord("sitecategory", "14958149");
var dupRecords = nlapiLoadSearch('Item', '1951'); //load saved search
var resultSet = dupRecords.runSearch(); //run saved search
resultSet.forEachResult(function(searchResult)
{
var InterID=(searchResult.getValue('InternalID')); // process- search
var LINEINX=loadCategory.getLineItemCount('presentationitem');
loadCategory.insertLineItem("presentationitem",LINEINX);
loadCategory.setLineItemValue("presentationitem", "presentationitem", LINEINX, InterID+'INVTITEM'); //--- Sets the line value.-jf
nlapiSubmitRecord(loadCategory , true);
return true; // return true to keep iterating
});
}
nlapiLoadRecord uses 5 units, nlapiLoadSearch uses 5, then actually it is resultSet.forEachResult that uses another 10. On top of that, you are running nlapiSubmitRecord for each search result, which will use 10 more units for each result.
It looks to me like all you are doing with your search results is adding line items to the Category record. You do not need to submit the record until you are completely done adding all the lines. Right now, you are submitting the record after every line you add.
Move the nlapiSubmitRecord after your forEachResult call. This will reduce your governance (and especially your execution time) from 10 units per search result to just 10 units.
Different APIs have different costs associated with them[see suiteanswers ID 10365]. Also, different types of scripts (user, scheduled, etc) have different max limits on what the total usage limit can be. [see suiteanswers ID 10481]
Your script should consume less than that limit else NetSuite will throw an error.
You can use the following line of code to measure your remaining usage at different points in your code.
nlapiLogExecution('AUDIT', 'Script Usage', 'RemainingUsage:'+nlapiGetContext().getRemainingUsage());
One strategy to avoid the max usage exceeded exception is to change the type of script to "scheduled script" since that has the maximum limit. Given that your loop is working off a search, the resultset could be huge and that may cause even a scheduled script to exceed its limits. In such case, you would want to introduce checkpoints in your code and make it reentrant. That way if you see the nlapiGetContext().getRemainingUsage() is less than your threshold, you offload the remaining work to a subsequent scheduled script.

Improving Efficiency in jQuery function

The while statement in this function runs too slow (prevents page load for 4-5 seconds) in IE/firefox, but fast in safari...
It's measuring pixel width of text on a page and truncating until text reaches ideal width:
function constrain(text, ideal_width){
$('.temp_item').html(text);
var item_width = $('span.temp_item').width();
var ideal = parseInt(ideal_width);
var smaller_text = text;
var original = text.length;
while (item_width > ideal) {
smaller_text = smaller_text.substr(0, (smaller_text.length-1));
$('.temp_item').html(smaller_text);
item_width = $('span.temp_item').width();
}
var final_length = smaller_text.length;
if (final_length != original) {
return (smaller_text + '…');
} else {
return text;
}
}
Any way to improve performance? How would I convert this to a bubble-sort function?
Thanks!
move the calls to $() outside of the loop, and store its result in a temporary variable. Running that function is going to be the slowest thing in your code, aside from the call to .html().
They work very very hard on making the selector engines in libraries fast, but it's still dog slow compared to normal javascript operations (like looking up a variable in the local scope) because it has to interact with the dom. Especially if you're using a class selector like that, jquery has to loop through basically every element in the document looking at each class attribute and running a regex on it. Every go round the loop! Get as much of that stuff out of your tight loops as you can. Webkit runs it fast because it has .getElementsByClassName while the other browsers don't. (yet).
Instead of removing one character at time until you find the ideal width, you could use a binary search.
I see that the problem is that you are constantly modifying the DOM in the loop, by setting the html of the temp_item, and then re reading the width.
I don't know the context of your problem, but trying to adjust the layout by measuring the rendered elements is not a good practice from my point of view.
Maybe you could approach the problem from a different angle. Truncating to a fixed width is common.
Other possibility (hack?) if dont have choices, could be to use the overflow css property of the container element and put the … in other element next to the text. Though i recommend you to rethink the need of solving the problem the way you are intending.
Hugo
Other than the suggestion by Breton, another possibility to speed up your algorithm would be to use a binary search on the text length. Currently you are decrementing the length by one character at a time - this is O(N) in the length of the string. Instead, use a search which will be O(log(N)).
Roughly speaking, something like this:
function constrain(text, ideal_width){
...
var temp_item = $('.temp_item');
var span_temp_item = $('span.temp_item');
var text_len_lower = 0;
var text_len_higher = smaller_text.length;
while (true) {
if (item_width > ideal)
{
// make smaller to the mean of "lower" and this
text_len_higher = smaller_text.length;
smaller_text = text.substr(0,
((smaller_text.length + text_len_lower)/2));
}
else
{
if (smaller_text.length>=text_len_higher) break;
// make larger to the mean of "higher" and this
text_len_lower = smaller_text.length;
smaller_text = text.substr(0,
((smaller_text.length + text_len_higher)/2));
}
temp_item.html(smaller_text);
item_width = span_temp_item.width();
}
...
}
One thing to note is that each time you add something to the DOM, or change the html in a node, the page has to redraw itself, which is an expensive operation. Moving any HTML updates outside of a loop might help speed things up quite a bit.
As other have mentioned, you could move the calls to $() to outside the loop. You can create a reference to the element, then just call the methods on it within the loop as 1800 INFORMATION mentioned.
If you use Firefox with the Firebug plugin, there's a great way of profiling the code to see what's taking the longest time. Just click profile under the first tab, do your action, then click profile again. It'll show a table with the time it took for each part of your code. Chances are you'll see a lot of things in the list that are in your js framework library; but you can isolate that as well with a little trial and error.

Categories