2
\$\begingroup\$

I'm using jQuery to create a (hidden) form that redirects people to another page using JavaScript.

// redirect the user to another url
function redirect(options) {
  // create the form
  $("<form>").attr({
    "hidden": true, "action": options.url, "target": (options.new_tab === true) ? "_blank" : "_self"
  // create the query inputs and append them to the form
  }).append($.map(options.queries || {}, function(name, value) {
    return $("<input>").attr({"name": name, "value": value, "hidden": true})[0];
  // append the form to the body and submit it
  })).appendTo("body").submit();
}

To simplify this question the form looks like this when broken down:

// redirect the user to another url
function redirect(options) {
  // create the form
  var form = $("<form>").attr({
    "hidden": true,
    "action": options.url,
    "target": (options.new_tab === true) ? "_blank" : "_self"
  });

  // create the query inputs and append them to the form
  form.append($.map(options.queries || {}, function(name, value) {
    return $("<input>").attr({
      "name": name,
      "value": value,
      "hidden": true
    })[0];
  }));

  // append the form to the body and submit it
  form.appendTo("body").submit();
}

How can I simplify the second part of this code?

// create the query inputs and append them to the form
form.append($.map(options.queries || {}, function(name, value) {
  return $("<input>").attr({
    "name": name,
    "value": value,
    "hidden": true
  })[0];
}));
\$\endgroup\$
2
\$\begingroup\$

You shouldn't need to post (or even have) two versions of the same source code. You should only have one readable version, and use a compressor to create a production version.

And IMO it would be fine to use method chaining in the second (readable) version.


Concerning your question: I think the whole code is fine as it is. I'll list some points that one may differently in the second block, but I don't believe they are really needed:

You could avoid the || {} by validating the options first, or before calling this method, or simply not allow the queries property to be undefined.

You could use an arrow function (if your target systems support it, or by using a ES6 compiler):

form.append($.map(options.queries, (name, value) => 
  $("<input>").attr({
    "name": name,
    "value": value,
    "hidden": true
  })[0]
));

And you could use the native JavaScript and more light weight Array.map() method:

form.append(Object.keys(options.queries).map((key, index) => 
  $("<input>").attr({
    "name": key,
    "value": options.queries[key],
    "hidden": true
  })[0]
));

Expanding on the last point: Now-a-days it's worth considering, if you still need jQuery at all. It was once a big help to bridge incompatibilities between browsers, but current browsers almost compatible to each other.

EDIT: Here's a version without jQuery:

function redirect(options) {
    let form = document.createElement("form");
    form.setAttribute("hidden", "");
    form.setAttribute("action", options.url);
    form.setAttribute("target", (options.new_tab === true) ? "_blank" : "_self");

    Object.entries(options.queries || {}).forEach(([name, value]) => {
        let input = document.createElement("input");
        input.setAttribute("hidden", "");
        input.setAttribute("name", name);
        input.setAttribute("value", value);
        form.appendChild(input);
    });

    document.body.appendChild(form);
    form.submit();
}
\$\endgroup\$
1
\$\begingroup\$

IE will die, don't die with it.

Supporting old browsers like IE11 and IE9 via jQuery puts you as a front-end developer at a serious disadvantage.

Javascript as a language is developing rapidly and by not keeping up you are losing important skills that keep you relevant in the workforce.

By the looks of your code you are unaware of ES6+ features such as

  1. Object.assign,
  2. object property syntax (property shorthand),
  3. arrow functions,
  4. default parameters,
  5. destructuring assignment,
  6. export,
  7. import,
  8. for...of,
  9. spread syntax,
  10. Object.entries.

all that lets you simplify your code and code management.

The following example use all the above listed ES6+ features

Modern JS version of your code

// This is a module in a file /modules/redirect.js
const createTag = (name, props = {}) => Object.assign(document.createElement(name), props);
export default function redirect(options) {
    options = {queries: {}, ...options};
    const hidden = true;
    const form = createTag("form", {
       hidden, 
       action: options.url,
       target: options.new_tab ? "_blank" : "_self"
    });
    for (const [name, value] of Object.entries(options.queries)) {
        form.appendChild(createTag("input", {hidden, name, value}));
    }
    document.body.appendChild(form);
    form.submit();
}

To use the code you import it in the scope you need it

import redirect from "/modules/redirect.js"
redirect(options);

For Legacy support use the following, rather than the above

// requires Babel to run in IE
const createTag = (name, props = {}) => Object.assign(document.createElement(name),props);
function redirect(options) {
    options = {queries: {}, ...options};
    const hidden = true;
    const form = createTag("form", {
       hidden, 
       action: options.url,
       target: options.new_tab ? "_blank" : "_self"
    });
    for (const [name, value] of Object.entries(options.queries)) {
        form.appendChild(createTag("input", {hidden, name, value}));
    }
    document.body.appendChild(form);
    form.submit();
}

What to do about legacy

Use a transpiler, such as babel.js, latest Babel 7 for legacy ES6+ support.

For DOM support IE9, IE11 are fairly up to date and you can use a sub set of the DOM API's to cover most of your needs. If you have specific API needs that are not supported in legacy browsers then consider targeted shims to cover the incompatibility.

Setting DOM Attribute

When setting attributes of DOM elements you only need to use setAttribute if an attribute is not defined as part of the Javascript DOM interface for a particular element.

It is also important to know that setAttribute converts the value to a string before assigning or adding it to the element.

There are also other caveats regarding setAttribute see the link for more info.

It is recommended that you set defined attributes directly. eg HTMLInputElement.value = newValue

You can get a list of defined properties with a simple function, or go to a reference like MDN.

The next snippet lists properties that can be set directly for the elements form and input It is designed to also show better legacy support via ES6+ and babel.js.

Only tested on Edge, FF, and Chrome. Should support IE8 and above. For support down to IE4 you will need a shim for document.createElement. Also note that I have used CSS3 and HTML5 so on older browsers the look will be different.

// define legacy support. With babel this should work all the way back to IE8
// You will need a shim for document.createElement for support below IE8
const text = line.textContent ? "textContent" : "innerText";
const addEvent = (el, name, cb) => {
    (el.attachEvent && el.attachEvent("on" + name, cb)) || el.addEventListener(name, cb);
}
const $ = (name, props = {}) => Object.assign(document.createElement(name), props);
const {d10b13a2-8965-459f-9dbc-ba613551ffab}($("div"), $("span", {[text]: named}), $("span", {[text]: type})));
        }
      }
    }
  }
}
body {font-family: monospace; background:#000; color:#6F6}
button {border: 2px solid #6F6; background: #000; color:#0F0; cursor: pointer}
button:hover {color: #000; background: #0F0}
   
Click tag:<span id="buttons">
  <button value="form">Form</button>
  <button value="input">Input</button>
</span>
<h3 id="elementName"></h3>
<div>Attributes that can be set directly have types String, Boolean, or Number<br>
List excludes functions, objects, and constants</div>
<div id="line">
===============================================</div>

<code id="info"></code>

Note that I come from a sector that has never needed to support legacy browsers, my views on subject are thus somewhat biased.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.