0

I am trying to translate multiple fields on a webpage and put together a final output that will look like this:

NOT AN OFFICIAL TRANSLATION. FOR REFERENCE USE ONLY.
Field_Name: Translation
Field_Name2: Translation
Field_Name3: Translation

Coming from a python heavy background, with little javascript experience, I am finding this very difficult. The code below is 85%-90% complete. I have succesfully grabbed the field names and the text, the call is made to the translation api and a translation is returned. My problem is stringing it together as one alert rather than a separate alert for each translation. Due to the nature of ajax I understand that it is difficult work with returned data outside of the asynchronous request. I think I am on the right track by calling another function within the ajax request but I am wondering if there is a better way to do this.

//Gathers all the field names and text associated with them.
$(document).ready(function() {

var allSpans = document.getElementsByTagName('span');
var spanlist = [];
var x = 0;
var ajax_data = [];
var fieldlabels = [];

[].slice.call(allSpans).forEach(function(span) {
    if(span.className === 'readonlyfield') {
        if(span.textContent) {
            var spanobject = {};
            var textfrom = 'en';
            var textto = 'it';
            var text = span.innerText;
            var parentelem = document.getElementById(span.parentNode.id);
            var parenttext = parentelem.innerText;
            fieldlabels.push(parenttext + ': ');
            spanobject.parentfield = span.parentNode.id;
            spanobject.parenttext = parenttext;
            spanobject.textfrom = textfrom;
            spanobject.textto = textto;
            spanobject.text = text;
            spanlist.push(spanobject);
            x++;
        }
    }
});
var translatebutton = document.createElement('button');
translatebutton.setAttribute("id", x);
translatebutton.innerText = 'Translate!';
document.body.appendChild(translatebutton);
var finaloutput = "FOR REFERENCE USAGE ONLY. NOT AN OFFICIAL TRANSLATION." + '\n';
// On button click translate all text gathered and alert the final output.
$("button").click(function(){
    var outputdata = [];
    for(var i = 0, len = spanlist.length; i < len ; i++){
      var textfrom = spanlist[i].textfrom;
      var textto = spanlist[i].textto;
      var text = spanlist[i].text;
      var urlservice = 'MYTRANSLATIONURL==&text='+text+'&textfrom='+textfrom+'&textto='+textto;
      jQuery.support.cors = true;

           $.ajax({
               url: urlservice,
               type: 'GET',
               contentType: "text/html",
               success: function (data) {
                 getsomedata(data);
               },
               error: function (x, y, z) {
                   alert(x + '\n' + y + '\n' + z);
               }
           });
      function getsomedata(data) {
        alert(data);

// The code below does not work, I was experimenting with an idea.
        // ajax_data.push(data + '\n');
      }
      // var temp = fieldlabels.concat(ajax_data);
      // temp.toString();
};
});
});

Thanks to Andrew I was able to solve this problem, the following code is my final solution for anyone who is having similar difficulties:

$(document).ready(function() {

var allSpans = document.getElementsByTagName('span');
var spanlist = [];
var x = 0;

[].slice.call(allSpans).forEach(function(span) {
    if(span.className === 'readonlyfield') {
        if(span.textContent) {
            var spanobject = {};
            var textfrom = 'en';
            var textto = 'it';
            var text = span.innerText;
            var parentelem = span.parentNode.id;
            //REGEX to find field captions in my particular system.
            var captionID = parentelem.replace(/((TD))?/, "TDCAP");
            var captionelem = document.getElementById(captionID);
            var caption = captionelem.innerText;
            spanobject.spanparent = span.parentNode.id;
            spanobject.captionfield = captionelem;
            spanobject.caption = caption;
            spanobject.textfrom = textfrom;
            spanobject.textto = textto;
            spanobject.text = text;
            spanlist.push(spanobject);
            x++;
        }
     }
});

var translatebutton = document.createElement('button');
translatebutton.setAttribute("id", x);
translatebutton.innerText = 'Translate!';
document.body.appendChild(translatebutton);
var finaloutput = "FOR REFERENCE USAGE ONLY. NOT AN OFFICIAL TRANSLATION." + '\n';
$("button").click(function(){
    var outputdata = [];
    var completedRequests = 0;
    for(var i = 0, len = spanlist.length; i < len ; i++){
      var textfrom = spanlist[i].textfrom;
      var textto = spanlist[i].textto;
      var text = spanlist[i].text;
      let parentLet = spanlist[i].spanparent;
      let textLet = spanlist[i].text;
      let captionfield = spanlist[i].captionfield;
      let caption = spanlist[i].caption;
      var urlservice = 'MYTRANSLATIONURL==&text='+text+'&textfrom='+textfrom+'&textto='+textto;
      jQuery.support.cors = true;
           $.ajax({
               url: urlservice,
               type: 'GET',
               contentType: "text/html",
               success: function (data) {
                 datastring = caption + ": " + data;
                 outputdata.push(datastring);
               },
               error: function (x, y, z) {
                   alert(x + '\n' + y + '\n' + z);
               },
               complete: function() {
                 if (++completedRequests >= len) {
                   getsomedata(outputdata);
                 }
               }
           });
      function getsomedata(data) {
        var finaldata = data.join("\n\n");
        final = finaloutput + '\n' + finaldata;
        alert(final);
      }
};
});
});
Calvin Ellington
  • 713
  • 3
  • 11
  • 21

1 Answers1

1

One way to achieve what you want is to keep track of how many requests you are sending and keep a count of how many requests finished. When the same amount are returned as sent, you then show the data.

As requests finish successfully you can append the data to an object or array and that is what you will show at the end.

The main part to look at below is

if (++completedRequests >= len) {
  getsomedata(outputdata)
}

completedRequests is incremented each time the ajax request completes (success or failure). When that amount is the same as the length of the loop we know we are finished and can show our data.

caveat: Don't expect the data to be returned in any specific order, its asynchronous so the requests come back when they are finished, not the same order as you sent them

var spanlist = [0, 0, 0, 0]; // example placeholder

$("button").click(function() {
  var outputdata = [];
  var completedRequests = 0;
  for (var i = 0, len = spanlist.length; i < len; i++) {
    var noLet = i;
    let yesLet = i;
    $.ajax({
      url: "https://jsonplaceholder.typicode.com/posts/" + (i + 1),
      type: 'GET',
      success: function(data) {
        outputdata.push(data.id);
        // These console logs are to explain how let is used with this ajax inside a loop
        console.log("noLet: " + noLet);
        console.log("yesLet: " + yesLet);
      },
      error: function(xhr, status, errorThrown) {
        console.error(status + '\n' + JSON.stringify(xhr) + '\n' + errorThrown);
      },
      complete: function() {
        if (++completedRequests >= len) {
          getsomedata(outputdata)
        }
      }
    });
  }
});

function getsomedata(data) {
  alert(data);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>Click Me</button>

Edit: I added how using let can help you use variables that are defined outside the ajax success handler, yet used inside the success handler. If you see the noLet prints 3 always because var is function scoped and by the time the last request comes back the variable i already ran through the loop and is the last value.

since let is block scoped (the for loop in this example), it will hold the value of i that was used when the ajax function in that same loop iteration is fired. Here is a good thread on the difference between var and let

Andrew Lohr
  • 5,380
  • 1
  • 26
  • 38
  • Thanks for the response, However I need to be able to organize the returned data. I can't have the data come back arbitrarily because I have to be able to associate the field label with the translated text. I was thinking I could create an object like `{field_label : text, field_label : text}` I am not familiar with the limitations of javascript, would it be possible to replace `text` from within the ajax request if the object is defined outside? relating the text to the field labels is required, originally I had a translate button for each field but my requirements changed to the whole page. – Calvin Ellington May 09 '18 at 18:39
  • 1
    @NoOrangeJuice You can reuse variables such as `textfrom`, `textto`, and `text` in the success ajax handler (even though they are defined before the ajax request) IF you either use a closure or the `let` syntax (instead of `var`), but know that `let` is ES6 syntax – Andrew Lohr May 09 '18 at 18:53
  • Strange, I had attempted something like that earlier but kept getting undefined errors. I had tried to alter the span object so that `spanlist[i].text = data` I thought this way I would have the field labels related to the translated text but this did not work. I'll look up `let` as I'm unfamiliar with this, thanks for your help. – Calvin Ellington May 09 '18 at 18:58
  • 1
    @NoOrangeJuice I edited my answer with an example of `let` – Andrew Lohr May 09 '18 at 19:01
  • Cheers mate I think this is enough for me to go on on my own. I have to finish this by tomorrow afternoon, I'll accept this as the answer when I get to the end of this project. Thanks! – Calvin Ellington May 09 '18 at 19:59