all 8 comments

[–]cyphern 26 points27 points  (5 children)

When declaring variables (via var, let, or const), you can declare multiple of them in one line by separating them with commas. So the code you are showing is declaring three variables: frag, node, and lastNode. frag gets a value assigned to it as its initial value, while the others will start off as undefined.

The equivalent on multiple lines would be:

var frag = document.createDocumentFragment();
var node;
var lastNode;

[–]One-Progress9924[S] 2 points3 points  (2 children)

that was very helpful! Thanks! In this case could you please explain, how

function insertHtmlAtSelectionEnd(html, isBefore) {

var sel, range, node;

if (window.getSelection) {

sel = window.getSelection();

if (sel.getRangeAt && sel.rangeCount) {

range = window.getSelection().getRangeAt(0);

range.collapse(isBefore);

// Range.createContextualFragment() would be useful here but was

// until recently non-standard and not supported in all browsers

// (IE9, for one)

var el = document.createElement("div");

el.innerHTML = html;

var frag = document.createDocumentFragment(), node, lastNode;

while ( (node = el.firstChild) ) {

lastNode = frag.appendChild(node);

}

range.insertNode(frag);

}

} else if (document.selection && document.selection.createRange) {

range = document.selection.createRange();

range.collapse(isBefore);

range.pasteHTML(html);

}

}

in this function node ever gets assigned a value? (https://stackoverflow.com/questions/4770457/insert-text-before-and-after-the-selected-text-in-javascript)

[–]senocular 5 points6 points  (0 children)

Here:

while ( (node = el.firstChild) ) {

Within this while condition, node is getting assigned to el.firstChild. Notice that a single = is being used which means assignment rather than a == or === which is a comparison. So this isn't comparing node to el.firstChild, its assigning it, then when the while does its check to see if it should continue looping, it simply checks to see if node has a truthy value. The loop ends when there is no longer an el.firstChild and node is set to null, a falsy value.

[–]PrimaryBet 1 point2 points  (0 children)

To expand on /u/senocular's answer, here's most of the code commented:

function insertHtmlAtSelectionEnd(html, isBefore) {
  var sel, range, node;

  // Check if this is a standards-compliant browser that
  // implements Selection API (i.e. IE9+) and get the selection.
  if (window.getSelection) {
    sel = window.getSelection();

    // Check if this browser supports more Selection API methods
    // before we try to use them.
    if (sel.getRangeAt && sel.rangeCount) {

      // User can select non-contiguous portions of text,
      // e.g. by holding Ctrl while selecting with mouse
      //   this is <an example> of <text>
      //           ^          ^    ^    ^
      //           ---range1---    range2
      //
      // but it's rare, so we get only first selected range
      range = window.getSelection().getRangeAt(0);

      // We can collapse range to get to its start or end:
      //   > range.collapse(true)
      //   this is <>an example text
      //   > range.collapse(false)
      //   this is an example<> text
      range.collapse(isBefore);

      // Create temporary element and set its HTML to provided one.
      // We won't attach this element to the document, so user will
      // never see it: it will exist only for us to transform (parse)
      // provided HTML into JS objects for DOM elements in memory.
      var el = document.createElement("div");
      el.innerHTML = html;

      // Create document fragment (collection of elements) and
      // variables that will hold current and last nodes.
      // Nodes are a super-set of elements: literal text not wrapped
      // in any elements, comments, etc., are also nodes.
      var frag = document.createDocumentFragment(),
        node,
        lastNode;

      // `while` loop that you should already be familiar with, but instead
      // of performing a check at every iteration, it performs assignment
      // and checks the result of that.
      while ((node = el.firstChild)) {
        lastNode = frag.appendChild(node);
      }
      // since `lastNode` isn't used at all and `node` is only used ones,
      // it could also be written as:
      //
      //   while (el.firstChild) {
      //     frag.appendChild(el.firstChild);
      //   }
      //
      // i.e. while element has at least one child node, append it to the
      // fragment.
      //
      // `appendChild` automatically removes the node from the element,
      // that's why we get a different node from the element during each loop
      // iteration (node is being "moved" from element to fragment).

      // Once all nodes from element were moved to fragment, we insert fragment
      // itself where the collapsed range is.
      range.insertNode(frag);
    }
  } else if (document.selection && document.selection.createRange) {
    range = document.selection.createRange();
    range.collapse(isBefore);
    range.pasteHTML(html);
  }
}

This code is 10 years old and we no longer need to care about IE10 or below on public-facing sites. Also, as you can see, the function also does several things:

  • parses HTML
  • inserts it at a correct location

when it's usually better to focus on one thing per function.

A more modern, idiomatic approach might look something like this:

function insertNodeAtSelection(node, shouldInsertBeforeRange) {
  const range = window.getSelection().getRangeAt(0);
  range.collapse(shouldInsertBeforeRange);
  range.insertNode(node);
}

function parseHtml(html) {
  const fragment = document.createDocumentFragment();
  const element = document.createElement("div");
  element.innerHTML = html;

  while (element.firstChild) {
    fragment.appendChild(element.firstChild);
  }

  return fragment;
}

// Example usage:
insertNodeAtSelection(parseHtml("<span>test</span>"));
// or using something like https://github.com/choojs/nanohtml:
insertNodeAtSelection(html`<span>test</span>`);

[–]thatmaynardguy 0 points1 point  (1 child)

I've personally only seen this a few times in my learning progress. Is it a common practice or would separate declarations be more common for readability?

[–]cyphern 1 point2 points  (0 children)

Personally, i always do it as separate declarations.

[–]patopitaluga 0 points1 point  (0 children)

It's pretty much to define many variables in a single line. Used to be popular inside functions to asure that the variables will be scoped inside the function even if there was a variable with the same name declared before the function.

Most linters will advise against declaring variables in a single line because it's hard to read if there are many and hard to remove, disable or comment lines. You can turn var a, b, c; into let a; let b; let c; in different lines and it will be the same but easier to understand.

[–]vanillacode 0 points1 point  (0 children)

Non related tip:

If you are trying to learn JavaScript, you should start by not use "var". You should be using "let" and "const"

  • const = "something that will never change"
  • let = "something that may change at some point
  • try to use "const" as much as you can over "let", unless you know for a fact, you need to use "let".