1

The code

JavaScript:

var recurringF = (function(){
   this.$el = $("#target");
   this.arg = arguments[0];
   this.spl = (!_.isEmpty(this.arg)) ? this.arg.split(" ") : false;
   if(this.spl){
       for(var i=0;i<this.spl.length;i++){
          if(i===0){
              this.$el.append(document.createElement(this.spl[i]));
          }else{
              this.$el.children().last().append(document.createElement(this.spl[i]));
          }
       }
   }
   return {
       "$":this.$el
   }
});

var t = new recurringF("div h1 span");

HTML-Body:

<body>
    <div id="target"></div>
</body>

The Goal

I'd like to append elements sequentially to an parent element $("#target") so that the end result in the HTML is the following:

<body>
    <div id="target">
        <div>
            <h1>
                <span></span>
            </h1>
        </div>
    </div>
</body>

The loop does not append the created elements to the the last appended element, but to the in loop cycle 1 created element 'div' like the following:

    <div id="target">
        <div>
            <h1></h1>
            <span></span>
        </div>
    </div>

What am I missing?

Naftali
  • 144,921
  • 39
  • 244
  • 303
  • 1
    Problem is your code "this.$el.children().last().append(document.createElement(this.spl[i]));". You are always referencing to $el ("#target"), so you span gets placed as a child of your div, being the .last of your $el.children(). – Trickzter Apr 18 '16 at 14:41
  • Check out these answers for selecting deepest child of element, I think that's what is failing here http://stackoverflow.com/questions/3787924/select-deepest-child-in-jquery – Caleb O'Leary Apr 18 '16 at 14:42

2 Answers2

1

By using .children(), you'll only get the immediate div on every iteration after the first, thus resulting in

<div id="target">
  <div>
    <h1></h1>
    <span></span>
    <alltherest></alltherest>
  </div>
</div>

because .children only looks at children, not all descendants. What you want is .find(*) so that it will get the deepest nested descendant on each iteration.

this.$el.find('*').last().append(document.createElement(this.spl[i]));

https://jsfiddle.net/f3fb997h/

That said, it would be better if you just stored a reference to the last created element and append to it, rather than having to reselect it every iteration.

var $tempEl = this.$el, newEl;
if(this.spl){
    for(var i=0;i<this.spl.length;i++){
        newEl = document.createElement(this.spl[i]);
        $tempEl.append(newEl);
        $tempEl = $(newEl);  
    }
}

https://jsfiddle.net/f3fb997h/1/

Note that at this point you're not really benefiting from jQuery at all, so a small tweak and you're not depending on it.

var recurringF = (function(){
   this.el = document.getElementById('target');
   this.arg = arguments[0];
   this.spl = (!_.isEmpty(this.arg)) ? this.arg.split(" ") : false;
   console.log(this.spl);
   var tempEl = this.el, newEl;
   if(this.spl){
       for(var i=0;i<this.spl.length;i++){
          newEl = document.createElement(this.spl[i]);
          tempEl.appendChild(newEl);
          tempEl = newEl;  
       }
   }
   return {
       "el":this.el
   }
});
Kevin B
  • 94,570
  • 16
  • 163
  • 180
  • why should the OP use `new recurringF(...)`? – Naftali Apr 18 '16 at 15:25
  • I don't think that's related to the problem. I wouldn't have written it that way – Kevin B Apr 18 '16 at 15:27
  • +1 for actually explaining to the OP what he/she did wrong and providing alternate solutions. Too many answers you find suggest alternate approaches without explaining what was at fault in the original. – rlemon Apr 18 '16 at 15:32
0

You can try using regular javascript functionality, as it has child appending built in:

const recurseElement = (elementString, target) => {
    const elements = elementString.split(' ');
    elements.forEach(function(ele) {
        const domElement = document.createElement(ele); // create the element
        target.appendChild(domElement); // append to the target
        target = domElement; // this element is the new target
    });
}

So now you can use it like so:

recurseElement('div h1 span', document.getElementById('target'));

const recurseElement = (elementString, target) => {
  const elements = elementString.split(' ');
  elements.forEach(function(ele) {
    const domElement = document.createElement(ele); // create the element
    target.appendChild(domElement); // append to the target
    target = domElement; // this element is the new target
  });
};

recurseElement('div h1 span', document.getElementById('target'));
#target div {
  background: green;
  height: 16px; width: 128px; padding: 10px;
}

#target div h1 {
  background: red;
  height: 16px; width: 64px; padding: 10px;
}

#target div h1 span {
  background: purple; display: block;
  height: 16px; width: 32px; padding: 10px;
}
<div id="target"></div>

It should be noted that arrow functions are available for Chrome 45+, Firefox 22.0+, Edge, and Opera. They do not work in IE or Safari. Or they will work if you have a transpiler (like babel)

Naftali
  • 144,921
  • 39
  • 244
  • 303