3

I have the following JavaScript which build url paramters based on users input:-

$(document).ready(function(){

    $('#button').click(function(e) {  
        var count=1;
        var s="";

        var inputvalue = $("#journal").val();
        var inputvalue2 = $("#keywords").val();
        var inputvalue3 = $("#datepub").val();
        var inputvalue4 = $("#title").val();
        var inputvalue5 = $("#localcurrency").val();
        var inputvalue6 = $("#locations").val();
        var inputvalue7 = $("#dropdown1").val();
        var inputvalue8 = $("#dropdown2").val();

        if(inputvalue!=null && inputvalue!="")
        {
        s = s+ "FilterField"+count+"=Journal&FilterValue"+count+"="+inputvalue+"&";
        count++;
        }
        if(inputvalue2!=null && inputvalue2!="")
        {
        s = s+ "FilterField"+count+"=KeyWords&FilterValue"+count+"="+inputvalue2+"&";
        count++;
        }
        if(inputvalue3!=null && inputvalue3!="")
        {
        s = s+ "FilterField"+count+"=datepub&FilterValue"+count+"="+inputvalue3+"&";
        count++;
        }
        if(inputvalue4!=null && inputvalue4!="")
        {
        s = s+ "FilterField"+count+"=Title&FilterValue"+count+"="+inputvalue4+"&";
        count++;
        }
        if(inputvalue5!=null && inputvalue5!="")
        {
        s = s+ "FilterField"+count+"=localcurrency&FilterValue"+count+"="+inputvalue5+"&";
        count++;
        }
        if(inputvalue6!=null && inputvalue6!="")
        {
        s = s+ "FilterField"+count+"=locations&FilterValue"+count+"="+inputvalue6+"&";
        count++;
        }
        if(inputvalue7!=null && inputvalue7!="")
        {
        s = s+ "FilterField"+count+"=dropdown1&FilterValue"+count+"="+inputvalue7+"&";
        count++;
        }
        if(inputvalue8!=null && inputvalue8!="")
        {
        s = s+ "FilterField"+count+"=dropdown2&FilterValue"+count+"="+inputvalue8+"&";
        count++;
        }

        window.location.replace("/teamsites/Bib%20Test/Forms/search.aspx?"+s);

    });
});
</script> 

now the above script will generate URLs such as

http://***/teamsites/Bib%20Test/Forms/search.aspx?FilterField1=Journal&FilterValue1=123

http://***/teamsites/Bib%20Test/Forms/search.aspx?FilterField1=Journal&FilterValue1=123&FilterField2=localcurrency&FilterValue2=USD&

and thing were working well, till i tried passing a search parameter which contain &. for example i wanted to search for a record which have their journal = General&Procedure, so using my above code, the URL will be as follow:-

http://***/teamsites/Bib%20Test/Forms/search.aspx?FilterField1=Journal&FilterValue1=General&Procedure&

and i did not get any result,, as the application assume that the Procudure is a parameter and not part of the FilterValue1.. now to fix this specific problem, i define to build the URL parameters with encodeURIComponent() function as follow:-

var inputvalue = encodeURIComponent($("#journal").val());
var inputvalue2 = encodeURIComponent($("#keywords").val());
var inputvalue3 = encodeURIComponent($("#datepub").val());
var inputvalue4 = encodeURIComponent($("#title").val());
var inputvalue5 = encodeURIComponent($("#localcurrency").val());
var inputvalue6 = encodeURIComponent($("#locations").val());
var inputvalue7 = encodeURIComponent($("#dropdown1").val());
var inputvalue8 = encodeURIComponent($("#dropdown2").val());

now the generated URL will be as follow:-

http://***teamsites/Bib%20Test/Forms/search.aspx?FilterField1=Journal&FilterValue1=General%26Procedure

and i got the expected results.. but not sure if using encodeURIComponent() to only encode the parameter values is a valid fix,, as seems i will be encoding the & if it is part of the query string parameter,, but still the url contain non-encoded & which separate the url parameters .. now the result i got from the last url is correct.. but not sure if i am doing things correctly ? and is there a built-in function to do this work for me ?? Thanks

6
  • Please use of Array Commented Oct 27, 2017 at 15:34
  • @GrégoryNEUT what do you mean? and how i can use it inside case? Commented Oct 27, 2017 at 15:35
  • 1
    jQuery.param() would make your life easier Commented Oct 27, 2017 at 16:12
  • @epascarello can you please advice more on how to use jQuery.param() in my case? second question, is the way i am doing things by encoding the parameter values using encodeURIComponent() as valid way ? Commented Oct 29, 2017 at 0:21
  • but not sure if using encodeURIComponent() to only encode the parameter values is a valid fix This is not only valid and correct, it is actually the only fix for supporting special characters in values which have special meaning for a URL. Commented Oct 31, 2017 at 9:39

5 Answers 5

3
+250

Here are sources for URL syntax:

You will notice that the exact content of the query component is not standardized. Its simple definition is:

The query component is indicated by the first question mark ("?") character and terminated by a number sign ("#") character or by the end of the URI.

However, the de-facto standard is to use ampersand (&) character as delimiter. With this convention, anytime this character also appears in your data and is not meant to be a delimiter, you have to "percent-encode" it, as per the standard as well:

A percent-encoding mechanism is used to represent a data octet in a component when that octet's corresponding character is outside the allowed set or is being used as a delimiter of, or within, the component.

You will easily understand that other special characters, like =, % and # must also be percent-encoded, should they appear in your data. There is no harm in encoding even more special characters as well.

Therefore if you follow this convention, your query component should be of the form:

?field1=value1&field2=value2

with each field and value being percent-encoded. In JavaScript, you can indeed conveniently use the encodeURIComponent function. Do not forget to encode the fields as well!

Furthermore, as your use case is very common, there are plenty libraries available that can handle such conversion for you, e.g. URI.js.

But since you mention using jQuery, you can conveniently use jQuery.param to do the conversion:

Create a serialized representation of an array, a plain object, or a jQuery object suitable for use in a URL query string or Ajax request. In case a jQuery object is passed, it should contain input elements with name/value properties.

$(document).ready(function() {
  $('#button').click(retrieveInputsValues);

  retrieveInputsValues();
});

function retrieveInputsValues() {
  var inputIds = [
    'Journal',
    'KeyWords',
    'datepub',
    'Title',
    'localcurrency',
    'locations',
    'dropdown1',
    'dropdown2'
  ];
  var obj = {};
  var count = 1;
  var value;

  for (var i = 0; i < inputIds.length; i += 1) {
    value = $('#' + inputIds[i].toLowerCase()).val();
    if (value !== null && value !== '') {
      obj['FilterField' + count] = inputIds[i];
      obj['FilterValue' + count] = value;
      count += 1;
    }
  }

  console.log($.param(obj));
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
Journal
<input type="text" id="journal" value="test & ampersand, comma, % percent, = equal and space" />
<br />keywords <input type="text" id="keywords" />

<br />datepub
<select id="datepub">
  <option value=""></option>
  <option value="1950">1950</option>
  <option value="2010">2010</option>
  <option value="2017" selected>2017</option>
  <option value="audi">Audi</option>
</select>
<br />title
<select id="title">
  <option value=""></option>
  <option value="TestDoc">test doc</option>
  <option value="t">t</option>
</select>
<br />localcurrency
<select id="localcurrency">
  <option value=""></option>
  <option value="USD">USD</option>
</select>
<br />locations
<select id="locations">
  <option value=""></option>
  <option value="US">US</option>
  <option value="UK">UK</option>
</select>
<br />dropdown1
<select id="dropdown1">
  <option value=""></option>
  <option value="a">a</option>
  <option value="b">b</option>
</select>
<br />dropdown2
<select id="dropdown2">
  <option value=""></option>
  <option value="aa">aa</option>
  <option value="bb">bb</option>
  <option value="cc">cc</option>
  <option value="dd">dd</option>
</select>
<br />
<button type="button" id="button">search</button>
<!-- re-used from https://stackoverflow.com/a/47008115/5108796 -->

BTW, usually there is no need to pass the field names as values, just "field=value" is used.

But you may have specific use case for your back-end processing?

Sign up to request clarification or add additional context in comments.

5 Comments

thanks for the great reply.. so now i need to encodeURIComponent the parameters value and name ONLY,, while i should leave the & ,= un-encoded when they are used to build the url... second point, now instead of manually using encodeURIComponent as in my code. i can build the parameters using array and use the $.param(obj) to build the encoded url??? but my original code is valid,,, although it can be improved for better understandability and maintenalitiy ... is this correct?
Yep, sounds like. Read the manual for jQuery.param, you should be able to use it easily.
@Gyhbs but the problem i am facing is that in-case inside the advance search i leave a free text field or a drop-down field empty, then they will be part of the url query with empty value,, which will cause problems.. while inside my original code i will not include the field in the query unless it contain data... so can i got this fixed inside ur code? for example i define to ONLY search the datepub=1950 but i got this query
search.aspx?FilterField1=Journal&FilterValue1=&FilterField2=KeyWords&FilterValue2=&FilterField3=datepub&FilterValue3=1950&FilterField4=Title&FilterValue4=&FilterField5=localcurrency&FilterValue5=&FilterField6=locations&FilterValue6=&FilterField7=dropdown1&FilterValue7=&FilterField8=dropdown2&FilterValue8=
Not sure I got exactly what you mean, but if you want not to include fields which have falsy value, that is trivial to do. I will edit the above answer.
2

Extending my comment as an answer.

Using encodeURIComponent is not only valid and correct, it is actually the only fix for supporting special characters in values in URL which have special meaning for a URL.

Encoding the values in URL component is important for prevent XSS attacks as well. Have a look here

URL-escaping is susceptible to double-escaping, meaning you must URL-escape its parts exactly once. It is best to perform the URL-escaping at the time the URL is being assembled.

However, you can improve your code in the following manner

var inputs = [ "#journal", "#keywords", "#datepub", "#title", "#localcurrency", "#locations", "#dropdown1", "#dropdown2" ];
$(document).ready(function(){
    $('#button').click(function(e) {  
        var count =  1;
        var searchParams = inputs.filter( function( id ){
            return $( "#" + id ).val().trim().length > 0;
        }).map( function( id ){
            var value = encodeURIComponent( $( "#" + id ).val().trim() );
            return "FilterField" + (count) + "=" + id + "&FilterValue" + (count++) + "=" + value;
        }).join( "&" );
        window.location.replace("/teamsites/Bib%20Test/Forms/search.aspx?"+ searchParams );
    });
});

Alternatively, you can also use URL (though not supported in IE)

var inputs = [ "#journal", "#keywords", "#datepub", "#title", "#localcurrency", "#locations", "#dropdown1", "#dropdown2" ];
$(document).ready(function(){
    $('#button').click(function(e) {  
        var count =  1;
        var url = new URL( "/teamsites/Bib%20Test/Forms/search.aspx?", window.location.origin );        
        inputs.forEach( function( id ){
            var value = encodeURIComponent( $( "#" + id ).val().trim() );
            if ( value.length >  0 )
            {
                url.searchParams.append( "FilterField" + count, id );
                url.searchParams.append( "FilterValue" + (count++), value );
            }
        });
        window.location.replace( url.href );
    });
});

As you can see that in this approach as well, you will have to use encodeURIcomponent since as per spec

The append(name, value) method, when invoked, must run these steps:

Append a new name-value pair whose name is name and value is value, to list.

Run the update steps.

there is no guarantee that encoding will be done. So, the explicit encoding necessary!!.

2 Comments

now instead of manually using encodeURIcomponent i can build an array of all the parameters then i can encode them using jQuery.param is this correct ? or you prefer to do the encodeURIcomponent manually ??
@johnG yes off course, jquery param uses encodeuricomponent internally. If you are already using jquery then go for it.
1

/!\ THIS IS NOT AN ANSWER

In relation with comments

const [
   $("#journal").val(),
   $("#keywords").val(),
   $("#datepub").val(),
   $("#title").val(),
   // ...
].forEach((x) => {
   if (x !== null && x !== '') {
     s += ...;

     count += 1;
   }
});

2 Comments

this to improve the script ?
This is in order to improve the code indeed. Make it easier to read and easier to maintain. When it's writen efficiently it's less likely buggy :)
0

I use encodeUriComponent for this.

url += "&filter=" + encodeURIComponent(filter);

You want '&' inside the parameter value to be encoded, so you use 'encodeURIComponent' on the value of the parameter, but you don't want to encode the stuff between parameters.

6 Comments

so it is the same thing i am doing ??? and do we need to encode the & which separate the parameters ?? can you answer my question instead of providing general answer... thanks
To separate parameters, you need the & unencoded. You seem to understand correctly. You want '&' inside the parameter value to be encoded, so you use 'encodeURIComponent' on the value of the parameter, but you don't want to encode the stuff between parameters.
so the way i fix the problem,, is the correct way ?
Yes, you encode only the parameter values with encodeURIComponent
so can i say that we usually encode the preserved values when they are not used as intended.. so if i want to use & as part of a parameter value i should encode it ... but if i want to use it to separate the parameters i keep it as is !! is there any documentation which describe this?
|
0

Use this if you are not concerned about Internet Explorer or Edge.

I would recommend to use browser's URL API instead. It is stable and is available in most of the modern browsers to deal with URL specific work natively.

Your code can be changed as follows to use this API. It automatically encodes all the required parameters as per the specs. You don't need to deal with the query parameters manually.

$(document).ready(function() {

  $('#button').click(function(e) {
    var count = 1;
    var s = "";
    var url = new URL("http://yourhost.com/teamsites/Bib%20Test/Forms/search.aspx");

    var inputvalue = $("#journal").val();
    var inputvalue2 = $("#keywords").val();
    var inputvalue3 = $("#datepub").val();
    var inputvalue4 = $("#title").val();
    var inputvalue5 = $("#localcurrency").val();
    var inputvalue6 = $("#locations").val();
    var inputvalue7 = $("#dropdown1").val();
    var inputvalue8 = $("#dropdown2").val();

    if (inputvalue != null && inputvalue != "") {
      url.searchParams.set("FilterField" + count, "Journal");
      url.searchParams.set("FilterValue" + count, inputvalue);
      count++;
    }

    if (inputvalue2 != null && inputvalue2 != "") {
      url.searchParams.set("FilterField" + count, "KeyWords");
      url.searchParams.set("FilterValue" + count, inputvalue2);
      count++;
    }

    if (inputvalue3 != null && inputvalue3 != "") {
      url.searchParams.set("FilterField" + count, "datepub");
      url.searchParams.set("FilterValue" + count, inputvalue3);
      count++;
    }

    if (inputvalue4 != null && inputvalue4 != "") {
      url.searchParams.set("FilterField" + count, "Title");
      url.searchParams.set("FilterValue" + count, inputvalue4);
      count++;
    }

    if (inputvalue5 != null && inputvalue5 != "") {
      url.searchParams.set("FilterField" + count, "localcurrency");
      url.searchParams.set("FilterValue" + count, inputvalue5);
      count++;
    }

    if (inputvalue6 != null && inputvalue6 != "") {
      url.searchParams.set("FilterField" + count, "locations");
      url.searchParams.set("FilterValue" + count, inputvalue6);
      count++;
    }

    if (inputvalue7 != null && inputvalue7 != "") {
      url.searchParams.set("FilterField" + count, "dropdown1");
      url.searchParams.set("FilterValue" + count, inputvalue7);
      count++;
    }

    if (inputvalue8 != null && inputvalue8 != "") {
      url.searchParams.set("FilterField" + count, "dropdown2");
      url.searchParams.set("FilterValue" + count, inputvalue8);
      count++;
    }

    window.location.replace(url.href);

  });
});

In addition to it, I recommend to incorporate the suggestions from @GrégoryNEUT, as it makes the code concise and easy to read.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.