Javascript runs loop too much - javascript

Mind that I just started with JS.
I am writing a simple application in which a user needs to translate a word in English to a word in French. What I want to do is to show the English word in a div. The user then enters the French word in an input which is processed when the enter key is pressed.
I have two pages, Test2 contains an English and French word like below.
yes,oui
The other page contains all the JS and HTML. The problem I am encountering is that after the user has filled in one word (and this word is either correct or incorrect), the script should automatically move on to the next word (i.e. no,non). I tried to do this by making a function and calling this function inside this same function. For some reason this goes wrong after filling in the first word (the user is then asked multiple words at once). Does anyone have any idea to solve or work around this problem?
<script>
function getword(){
var arr;
$.ajax({ type: "POST", async: false, url: 'Test2', success: function(data) {
arr = data.split (",");
}});
return {or: arr[0], tr: arr[1]};
}
function process(or, tr){
if(tr == $("#translation").val()){
return true;
}else{
return false;
}
}
$(document).ready(function(){
go();
function go(){
var data = getword();
var or = data.or;
var tr = data.tr;
alert("Translate to French: " + or);
$("#translation").keypress(function(event) {
if (event.which == 13) {
if(process(or, tr)){
alert('good');
go();
}else{
alert('bad');
go();
}
}
});
}
});
</script>
<div id="result"></div>
<input type="text" placeholder="Antwoord" id="translation"/>

You should not reassign the key event handler on each run on of go. These are cumulative and so you will get a load of triggers of that key event handler after a while. So move that out of the go function, and declare your variables one level up, so they stay in scope for the event handler:
$(document).ready(function(){
var or, tr; // <!-- define here
go();
function go(){
var data = getword();
or = data.or;
tr = data.tr;
alert("Translate to French: " + or);
}
$("#translation").keypress(function(event) {
if (event.which == 13) {
if(process(or, tr)){
alert('good');
go();
}else{
alert('bad');
go();
}
}
});
});
NB: it is not considered good practice to interact with the user via alert. Consider putting a text on the page, for instance in a div element.

Related

Run function based on .pathname

So I've got this code in my page: a very simple but working script to translate it to several languages.
// preparing language file
var aLangKeys=new Array();
aLangKeys['en']=new Array();
aLangKeys['es']=new Array();
aLangKeys['fr']=new Array();
aLangKeys['cn']=new Array();
aLangKeys['en']['language']='english';
aLangKeys['es']['language']='español';
aLangKeys['fr']['language']='français';
aLangKeys['cn']['language']='中文';
aLangKeys['en']['buy']='buy';
aLangKeys['es']['buy']='comprar';
aLangKeys['fr']['buy']='acheter';
aLangKeys['cn']['buy']='买';
$(document).ready(function() {
// onclick behavior
$('.language').click(function() {
var lang = $(this).attr('id'); // obtain language id
if ($(this).attr('id') == 'es') {
$('.language').attr('id', 'fr');
}
else if ($(this).attr('id') == 'fr') {
$('.language').attr('id', 'cn');
}
else if ($(this).attr('id') == 'cn') {
$('.language').attr('id', 'en');
}
else if ($(this).attr('id') == 'en') {
$('.language').attr('id', 'es');
}
// translate all translatable elements
$('.translate').each(function(i){
$(this).html(aLangKeys[lang][ $(this).attr('key') ]);
});
} );
});
// HERE'S WHERE MY BRAIN STARTS MALFUNCTIONING
if ((window.location.pathname).split('/')[1] == 'es') {
// <-- EXECUTE FUNCTION ABOVE TO TRANSLATE TO SPANISH BASED ON PATHNAME
}
else if ((window.location.pathname).split('/')[1] == 'fr') {
// <-- EXECUTE FUNCTION ABOVE TO TRANSLATE TO FRENCH BASED ON PATHNAME
}
else {
}
So it basically translates (changes the value of certain elements on the page) when clicking a button. Every time you click on it, changes to the next language. That works fine.
THE PROBLEM is, I want it 'automatically' changed to a certain language if the user is visiting from a certain link:
Example:
www.mysite.com (nothing happens because nothing is on the pathname)
www.mysite.com/es/ ('automatically changes values to spanish')
www.mysite.com/fr/ ('automatically changes values to french')
I tried 'faking' the button click with javascript but didnt work.
Also tried 'naming' the translating function and 'call/run' it.
I know it's easier to do and I'm making it complicated but I'm such a noob.
Please, help. Or just a hint. Thanks in advance and sorry for my English.
Based on code above a couple thoughts:
1) lets change aLangKeys to an object with each key being another object.
i.e.
var aLangKeys={};
aLangKeys['en']={}; // Thats a named key/prop so we want an object here
...
aLangKeys['en']['language']='english'; // ditto the above comment
2) we probably want to move the logic that checks for locality inside the ready function.
I.e.
$(document).ready(function() {
// onclick behavior
$('.language').click(function() {
...
});
// we want access to the DOM *and* maybe certain functions that do stuff. So its gotta be in here...
if ((window.location.pathname).split('/')[1] == 'es') {
// <-- EXECUTE FUNCTION ABOVE TO TRANSLATE TO SPANISH BASED ON PATHNAME
}
else if ((window.location.pathname).split('/')[1] == 'fr') {
// <-- EXECUTE FUNCTION ABOVE TO TRANSLATE TO FRENCH BASED ON PATHNAME
}
}); // end of ready function
Couple reasons:
a) we want to change the page content based on info like pathname/locality. So we want to know the page is loaded first.
b) perhaps we will want to make a function that does language processing/changing and call that from multiple places. We want that function in scope of our locality checking logic. If we define that inside the ready function scope, we will need any logic that calls that function also inside the same scope

Function is called too many times and retains old variable values

I currently have a table that is updated every time a user presses a button (handled using Backbone events).
Here is the event that is fired every time the button is pressed:
add: function(ev) {
if($(ev.target).data("card-id")) {
this.card_array.push($(ev.target).data("card-id"));
current_deck.push($(ev.target).data("card-id"));
updateCurrentDeckTable();
} else {
alert("Couldn't add the card. Please wait a few seconds before trying again.");
}
console.log(this.card_array);
console.log(current_deck);
}
updateCurrentDeckTable is defined as so:
function updateCurrentDeckTable() {
console.log(current_deck);
var content = "";
$.each(current_deck, function(i, cardId) {
console.log("Index: "+i+" || Card ID: "+cardId);
var M_Card = Parse.Object.extend("Card");
var M_CardQuery = new Parse.Query(M_Card);
M_CardQuery.get(cardId, {
success: function(m_card) {
console.log("Got "+m_card.get("cardTitle"));
content += "<tr>";
content += "<td>"+m_card.get("objectiveNumber")+"</td>";
content += "<td>"+m_card.get("cardTitle")+"</td>";
if (m_card.get("cardAffiliation") == null) {
content += "<td>-</td>";
} else {
content += "<td>"+m_card.get("cardAffiliation")+"</td>";
}
content += "<td><a>Details</a></td>";
content += "</tr>";
console.log("Content: "+content);
$('#deck-table tbody').append(content);
},
error: function() {
alert("Error.");
}
});
});
}
Now, the first time the user presses the button, everything works just fine. However, on the second press, more than one row is added to the table. Further, the content variable seems to keep it's old values. Shouldn't it reset to "" every time the function is called?
Does anyone have any idea what's happening? If you need more information, please ask.
Why oh why are you passing data using a global current_deck variable?
Each time you enter the updateCurrentDeckTable branch of your add method:
if($(ev.target).data("card-id")) {
this.card_array.push($(ev.target).data("card-id"));
current_deck.push($(ev.target).data("card-id"));
updateCurrentDeckTable();
}
You're adding another entry to the global current_deck and then updateCurrentDeckTable iterates over that array:
$.each(current_deck, function(i, cardId) { ...
I don't see anything that clears out current_deck so it will just keep growing. That would explain the odd behavior you're describing.
The solution would be to stop using a global for current_deck, just pass it around as an argument and clean it up as needed.

How to smooth a jQuery script that takes time to execute?

I have a jQuery script that searches in the DOM and shows the results in a list.
There is a simplified version of the script here: http://jsfiddle.net/FuJta/1/
There is usually a large number of results, so the script can take a while to execute. (In the example above, this is simulated with a function that delays the script). So if you type too fast in the searchbox, the script prevents you from typing, and it feels bad.
How could I change my script so that you can type freely, and the results show up when they are ready. I want something like the facebook search : if you type too fast, the results are just delayed, but you can still type.
Html
<p>Type in foo, bar or baz for searching. It works, but it is quite slow.</p><br/>
<input type="text" id="search"/>
<div id="container" style="display:none">
<div class="element">foo</div>
<div class="element">bar</div>
<div class="element">baz</div>
</div>
<div id="results">
</div>​
Javascript
$(function() {
function refreshResults() {
var search = $('#search').val();
var $filtered = $('#container .element').clone().filter(function() {
var info = $(this).text();
return info.toLowerCase().indexOf(search) >= 0;
});
$('#results').empty();
$filtered.each(function() {
$('#results').append($(this));
});
}
// simulating script delay
function pausecomp(millis) {
var date = new Date();
var curDate = null;
do {
curDate = new Date();
}
while (curDate - date < millis);
}
$('#search').keyup(function() {
pausecomp(700);
refreshResults();
});
});​
One solution could to refresh the results only when pressing enter. This way, the delay for searching the results feels ok. But I would prefer if I just delay the results and let the user freely type.
You should perform a search like this using asynchronous techniques. No doubt Facebook uses some sort of AJAX to request search results - which means getting the results from the server. This will help prevent the UI 'freeze' that you are currently experiencing.
Here is a very simple example of what you can try (it uses JQuery for the AJAX requests):
var searchInProgress = false;//used to work out if a search is in progress
var searchInQueue = false;//used to flag if the input data has changed
function getSearchResults(searchText){
if (searchInProgress ) {
searchInQueue = true;
return;
}
searchInProgress = true;
searchInQueue = false;
$.getJSON("URL",//URL to handle AJAX query
{ searchText: searchText},//URL parameters can go here
function (data) {
//handle your returned data here
searchInProgress = false;
if (searchInQueue){//text has changed, so search again
getSearchResults();
}
});
}
$('#search').keyup(function() {
getSearchResults($(this).val());
});
A few things to note: It is probably a good idea to handle failed AJAX requests to ensure you can reset the searchInProgress flag as needed. Also, you can add delays after the keyup as desired, but this all depends on how you want it too work.
From How to delay KeyPress function when user is typing, so it doesn't fire a request for each keystroke? :
var timeoutId = 0;
$('#search').keyup(function () {
clearTimeout(timeoutId); // doesn't matter if it's 0
timeoutId = setTimeout(refreshResults, 100);
});
It does what I want indeed.
Here's a solution that divides the search process into steps, returning flow to the browser during the process to allow the UI to respond.
$(function() {
function searchFunc($element,search) {
var info = $element.text();
return info.toLowerCase().indexOf(search) >= 0;
}
var searchProcessor = null;
function restartSearch() {
console.log('Restarting...');
// Clear previous
if (searchProcessor != null) {
clearInterval(searchProcessor);
}
$('#results').empty();
// Values for the processor
var search = $('#search').val();
var elements = $('#container .element').get();
console.log('l:',elements,elements.length);
// Start processing
searchProcessor = setInterval(function() {
if (elements.length == 0) {
// Finished searching all elements
clearInterval(searchProcessor);
searchProcessor = null;
console.log('Finished.');
} else {
console.log('Checking element...');
var $checkElement = $(elements.shift());
if (searchFunc($checkElement, search)) {
$('#results').append($checkElement.clone());
}
}
}, 10);
}
$('#search').keyup(function() {
restartSearch()
});
});
It only processes one element each time. That should probably be increased so it handles perhaps 10 or 100 each time around, but the important point is that the work is divided into chunks.
This solution should also be faster than the original because it doesn't clone() everything, only the elements that were matched.

load search results into a div that toggles with other divs

I am working with a page that has multiple divs that toggle. This function works. A search function was added and this works too.
The problem with the page as it exists currently: The search bar was placed on the "default" div and the results load below the bar into another div that is invisible when empty. The results div is inside this first default div. If you toggle to another div, you lose the default div and can't get back to it.
For this reason, I moved the search bar to the left navigation where the other toggle links are situated. I also moved the search results div out of the default div to "stand on its own."
What I am trying to do: Make the search button show the div with the results as well as find the results. Basically, to integrate the search function into the array/toggle function. The search function is in one .js file and the toggle function is in a different .js file.
I keep thinking there must be a way to get "onclick" to call from both .js files so that I don't have to do a bunch of extra work combining the two functions that already exist and work separately. I am a Javascript newbie learning by examples and haven't been able to figure this out. I have never seen a working example of this and my searches haven't produced one.
I would be very grateful for any help. Hope I explained the problem adequately.
Edit: Here is the code I already have for the toggle function.
var ids=new Array('a','b','c',[and so on--search results not added here yet]);
function switchid(id_array){
hideallids();
for( var i=0, limit=id_array.length; i < limit; ++i)
showdiv(id_array[i]);
}
function hideallids(){
for (var i=0;i<ids.length;i++){
hidediv(ids[i]);
}
}
function hidediv(id) {
//safe function to hide an element with a specified id
if (document.getElementById) { // DOM3 = IE5, NS6
document.getElementById(id).style.display = 'none';
}
else {
if (document.layers) { // Netscape 4
document.id.display = 'none';
}
else { // IE 4
document.all.id.style.display = 'none';
}
}
}
function showdiv(id) {//safe function to show an element with a specified id
if (document.getElementById) { // DOM3 = IE5, NS6
document.getElementById(id).style.display = 'block';
}
else {
if (document.layers) { // Netscape 4
document.id.display = 'block';
}
else { // IE 4
document.all.id.style.display = 'block';
}
}
}
function initialize(){
var t = gup("target");
if( t )
{
switchid([t]);
}
}
function gup( name )
{
name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
var regexS = "[\\?&]"+name+"=([^&#]*)";
var regex = new RegExp( regexS );
var results = regex.exec( window.location.href );
if( results == null ){
return "";
} else {
return results[1];
}
}
Thanks in advance!
When your toggle function code is loaded, the functions are declared in the Global scope. When you search functions are loaded, they are also in the Global scope. Since they are in the same scope, even though it's a different file, the toggle functions can be used by your search function, if you include the file with the search function after the file with the toggle function.
TL;DR
function search(...) {
// do your search stuff
// when you get a result ID, toggle it from here
hideallids();
showdiv(id);
}
I seriously recommend you use meaningful names, objects as namespaces to organize your code, and CamelCase or underscores to mark word boundaries in identifiers. For example:
window.ZESearch = {
'initialize' : function() { ... },
'search': function() {
// Find the node with the desired result
ZESearch.showResult(id);
},
'hideAllResults': function() { ... },
'hideResult' : function(id) { ... },
'showResult' : function(id) { ... },
...
};
ZESearch.initialize();
Since you're just learning, I've avoided the complexity of the this keyword, presented a simple way to create an object to keep your code organized but added your object to window so you can get it from anywhere in your code.

Loading dynamically generated content after page reload

I have a Google Instant style search script written in jQuery. When a user searches, a URL is created which is something like #search/QUERY/1/. However, when you either reload the page, click a result which goes to a different page or return back from a previous page the search results are no longer there. Why could this be?
My jQuery code is:
$(document).ready(function(){
$("#search").keyup(function(){
var search=$(this).val();
var query=encodeURIComponent(search);
var yt_url='search.php?q='+query+'&category=web';
window.location.hash='search/'+query+'/1/';
document.title=$(this).val()+" - My Search Script";
if(search==''){
window.location.hash='';
document.title='My Search Script';
}
$.ajax({
type:"GET",
url:yt_url,
dataType:"html",
success:function(response){
if(response !=""){
$("#result").html(response);
} else {
$("#result").html("No results were found.");
}
}
});
});
});
$(document).ready(function(){
/// Your original key up here...
// With browser's back you should get your hash back, so you should be
// able to fill in the query value back and simulate key release to trigger search
if(window.location.hash.indexOf('#search/') == 0) {
query = window.location.hash.replace('#search/', '').replace('/1/', '');
$('#search').val(decodeURIComponent(query)).keyup();
}
});
Because its return to the previous state of the page before the search.
Maybe this can be a workaround:
$(document).ready(function(){
/*
your code here
*/
if ($("#search").val() != "")
$("#search").keyup();
});
So it will mimic the behavior of when the user is searching when the page reloads, return back, etc..

Categories

Resources