0

I am constructing a query string in Javascript based on whether a checkbox is checked or not.

Some of the options in the checkboxes are

  • "Annual"
  • "Grass"
  • "Shrub (Evergreen)"
  • "Shrub (Deciduous)"

I found a function online that updates the url parameter:

function updateUrlParameter(uri, key, value) {
  value = value.replace(/\s/g, "%20");
  var i = uri.indexOf('#');
  var hash = i === -1 ? '' : uri.substr(i);
  uri = i === -1 ? uri : uri.substr(0, i);
  var re = new RegExp("([?&])" + key + "=.*?(&|$)", "i");
  var separator = uri.indexOf('?') !== -1 ? "&" : "?";

  if (!value) {
    // remove key-value pair if value is empty
    uri = uri.replace(new RegExp("([&]?)" + key + "=.*?(&|$)", "i"), '');
    if (uri.slice(-1) === '?') {
        uri = uri.slice(0, -1);
    }
  } else {
    console.log("value is " + value)
    uri = uri + separator + key + "=" + value;
  }
  return uri + hash;
}

Using the above function, if I check the checkboxes for the above four starting from top down, my query string becomes

?plantType=Annual&plantType=Grass&plantType=Shrub%20(Evergreen)&plantType=Shrub%20(Deciduous

Why is the function ignoring the last ')' in the string? Is there a work around this? I would like to keep the parenthesis in the query string because this will make querying the database easier.

I created a function to iterate through input checkboxes. If they are checked, then use the updateUrlParameter function to update the URI.

function getQueryString() {
  var inputsContainerChildren = $('#floatingDivForFilter').children();
  var input = document.createElement('input')
  var uri = '';

  for (var i = 0; i < inputsContainerChildren.length; i++) {
    var currChild = inputsContainerChildren[i].firstElementChild;
    if (currChild) {
        if (currChild.tagName === 'INPUT') {
            if (currChild.checked) {
                var id = currChild.id;
                    console.log(uri)
                    uri  = updateUrlParameter(uri, currChild.name, currChild.value);
            }
        }
    }
} 
console.log(uri);

}

The photo below shows a snapshot of the URL produced. I can't figure out why the last ')' is chopped off. url photo

hello
  • 65
  • 5
  • 4
    I would advise against querying the database with values passed directly in from your query string. That's a great way to open yourself up for SQL injection. – aridlehoover Mar 07 '19 at 17:58
  • 3
    This seems a little like reinventing the wheel, as submitting a form with method=GET will pretty much do all this for you. – James Mar 07 '19 at 18:04
  • You're over complicating things here, 1 you're using a function you dont understand to do something fairly simple doing other means – Joe Warner Mar 07 '19 at 18:06
  • 1
    The problem is not in this function. The function works as expected: https://jsfiddle.net/7d3ofu5w/ - The problem must be in the code that calls this function or in the HTML markup or in the code that uses the result. – NineBerry Mar 07 '19 at 18:19
  • If this function always removed the last `)`, it wouldn't have been able to add `(Evergreen)` correctly on the previous call. So there must be something specific to the `Deciduous` checkbox. – Barmar Mar 07 '19 at 18:22
  • 1
    Is you post a [mcve] we may be able to help you. – Barmar Mar 07 '19 at 18:23
  • The function does return the correct output, but when i log it to the console, it returns only "?plantType=Annual&plantType=Grass&plantType=Shrub%20(Evergreen)&plantType=Shrub%20(Deciduous" It is as if the url chops off the last ')' – hello Mar 07 '19 at 18:26
  • @hello We need an [mcve] – NineBerry Mar 07 '19 at 18:27
  • Hi, I added additional code I used and gave a snap shot of the output i got. – hello Mar 07 '19 at 18:37

2 Answers2

0

The issue you are seeing is just the Chrome developer tools trying to be too clever.

When logging the url to the console, Chrome will not recognize the full url as a link but exclude the closing ")". They probably do that because it will be very common that people write an url in braces and it is not expected that the closing brace is part of the url.

Since this is only an issue of the developer tools, you can ignore the issue. It will not affect the runtime behaviour of your code.

The issue will be solved when you correctly escape special characters in the parameters (as you should do anyway):

function updateUrlParameter(uri, key, value) {
  // removed because escape will do that 
  // value = value.replace(/\s/g, "%20");
  var i = uri.indexOf('#');
  var hash = i === -1 ? '' : uri.substr(i);
  uri = i === -1 ? uri : uri.substr(0, i);
  var separator = uri.indexOf('?') !== -1 ? "&" : "?";

  if (!value) {
    // remove key-value pair if value is empty
    uri = uri.replace(new RegExp("([&]?)" + key + "=.*?(&|$)", "i"), '');
    if (uri.slice(-1) === '?') {
        uri = uri.slice(0, -1);
    }
  } else {
    console.log("value is " + value)
    // Use escape on key and value
    uri = uri + separator + escape(key) + "=" + escape(value);
  }
  return uri + hash;
}

let s = "http://chrome.is.too.clever/";
s = updateUrlParameter(s, "plantType", "Annual");
s = updateUrlParameter(s, "plantType", "Grass");
s = updateUrlParameter(s, "plantType", "Shrub (Evergreen)");
s = updateUrlParameter(s, "plantType", "Shrub (Deciduous)");
console.log(s);

Fiddle

NineBerry
  • 26,306
  • 3
  • 62
  • 93
  • Thank you for your explanation. I was wondering for the longest time why it excluded the last ')'. – hello Mar 07 '19 at 19:10
0

Instead of using a regular expression, just convert the params to an object, modify said object, and convert it back into params.

var url = 'https://x.y?plantType=Annual&plantType=Grass&plantType=Shrub%20(Evergreen)&plantType=Shrub%20(Deciduous)';

function updateUrlParameter(uri, key, value) {
  let url = new URL(uri), object = deserializeQuery(url.search); // params to obj
  object[key] = value; // modify obj
  return url.origin + '?' + serializeQuery(object); // obj to url + params
}

console.log(updateUrlParameter(url, 'plantType', [ 'Pine', 'Palm', 'Rose (Red)' ]));

/** ======= Serialization / Deserialization functions below ======== */

// https://stackoverflow.com/a/47517503/1762224
function deserializeQuery(queryString, queryKey) {
  let query = {}, pairs = (queryString[0] === '?' ? queryString.substr(1) : queryString).split('&');
  for (var i = 0; i < pairs.length; i++) {
    var pair = pairs[i].split('='), key = decodeURIComponent(pair[0]), value = decodeURIComponent(pair[1] || '');
    value = (value.indexOf(',') === -1 ? value : value.split(','));
    query[key] = query[key] ? (query[key].constructor === Array ? query[key].concat(value) : [query[key], value]) : value;
  }
  return typeof queryKey === 'undefined' ? query : query[queryKey];
}

// https://stackoverflow.com/a/53528203/1762224
function serializeQuery(params, keys = [], isArray = false) {
  const p = Object.keys(params).map(key => {
    let val = params[key];
    if ("[object Object]" === Object.prototype.toString.call(val) || Array.isArray(val)) {
      keys.push(Array.isArray(params) ? "" : key);
      return serializeQuery(val, keys, Array.isArray(val));
    } else {
      let tKey = keys.length > 0 ? ((isArray ? keys : [...keys, key]).reduce((str, k) => "" === str ? k : `${str}[${k}]`, "")) : key;
      if (isArray) {
        return encodeURIComponent(tKey) + '=' + encodeURIComponent(val);
      }
    }
  }).join('&');
  keys.pop();
  return p;
}
.as-console-wrapper {
  top: 0;
  max-height: 100% !important;
}

.as-console-row {
  white-space: pre-wrap;
  word-break: break-all;
}
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132