Discrepancy between elements retrieved via e.target and those shown in DOM - javascript

I'm building a todo list with nested lists. List items are attached to their respective unordered lists with the use of Handlebars templating. The list items each contain a text input to represent them in the browser, empty <ul></ul> tags for any future child list items, and a button to add those list items. This works, and the text inputs appear properly in the DOM. However, when I attempt to retrieve newly added <li></li> elements via e.target beyond the master list, nothing is detected in the child lists, despite the fact that they all appear in the DOM.
In the code, I have an event handler bound to the button which adds a new list item for each list. In this case, e.target is the button. My aim is to use jQuery's .focus() on the new item's input so the cursor is already there when it appears. I have tested out using both jQuery and vanilla JS methods in console.log() to traverse the tree, going through the button's sibling-level <ul></ul> but this does not appear to be a problem with traversal methods, as the lists are always logged, though empty.
In this scenario, one item is present on the master list. Subsequently, one item is added to the first sublist via the event handler function for the "new list item" button (which itself is inside the first item's <li></li> tags). Inside that function, I traverse the tree to access the list item's child unordered list and log the result to the console, as with the following:
console.log(e.target.closest('li').querySelector('ul'));
The returned result shows that the list is empty:
<ul id="list-2" class="parent-list">
</ul>
However, the newly appended list item is clearly visible in the DOM:
<ul id="list-2" class="parent-list">
<li data-id="...">...</li>
</ul>
The relevant HTML including script for list templating with handlebars:
<body>
<label><input type="checkbox" id="toggle-all-checkbox">Toggle All</label>
<br>
<button class="add-parent-list-todo">+</button>
<br>
<ul id="list-1" class="parent-list">
</ul>
</body>
<script id="todo-list-template" type="text/x-handlebars-template">
{{#this}}
<li {{#if completed}} class="completed" {{/if}} data-id="{{id}}">
<input class="todo-input" type="text" value="{{text}}">
<div id='options'>
<button class="delete">x</button>
<input type="checkbox">
</div>
<br>
<button class="add-parent-list-todo">+</button>
<br>
<ul id="{{childList}}" class="parent-list">
</ul>
</li>
{{/this}}
</script>
The event handler function in question:
enterTodoText: function (e) {
var parentList = $(e.target).next().next().attr('id');
this.todos.push({
text: '',
id: util.uuid(),
completed: false,
parentList: parentList,
childList: "list-" + childListId
});
childListId++;
this.render();
$(e.target).next().next().children('li:last-child').find('.todo-input').focus();
console.log(e.target.closest('li').querySelector('ul'))
}
I'd very much appreciate any insight anyone might have.

I have come to realize that this in overwhelming likeliness has something to do with e.target being inside of a Handlebars template. Using e.target from a template, I am seemingly unable to traverse beyond the template itself. This is confirmed by testing with console.log($(e.target).parents()), which shows that for sublists, the topmost detectable parent is <li> (which is the parent element in the template), when I expected it to be <html>.
The solution I came to is to just traverse the tree using $('body') as the starting point, as opposed to $('e.target').

Related

Why does removeChild function behave differently with list items and i tag?

I have noticed that removeChild does NOT behave as it does with other elements such as list item. I am using the i tag for some icons from frontAwesome and want these items removed individually when a button is clicked.
Unfortunately, I can only remove each i tag element only if I use removeChild() function twice. (Weird!)
What's going on?
HTML:
<div id="myFonts">
<i>1</i>
<i>2</i>
<i>3</i>
<i>4</i>
<i>5</i>
</div>
Javascript:
function FunctionTwo() {
var font = document.getElementById("myFonts");
font.removeChild(font.childNodes[0]);
}
https://codepen.io/anon/pen/EeaYvL
EDIT
Note: It makes a difference if you use LineBreaks or not!
<ul id="myList">
<li>Coffee</li>
<li>Tea</li>
<li>Milk</li>
</ul>
Here, there are 6 child nodes. Apparently, the LineBreaks are also considered as child nodes!
<ul id="myList"><li>Coffee</li><li>Tea</li><li>Milk</li></ul>
Here, there are 3 child nodes. WEIRD - is this a bug?
From MDN,
childNodes includes all child nodes, including non-element nodes like
text and comment nodes. To get a collection of only elements, use
ParentNode.children instead.
Hence, in both the cases, the elements are being removed weirdly. You should update
from
font.removeChild(font.childNodes[0]);
to
font.removeChild(font.children[0]);
For tweaking, https://codepen.io/anon/pen/aazoLK
Also.. if you notice both of your code closely on your link...
Code for <ul id="myList">
<ul id="myList"><li>Coffee</li><li>Tea</li><li>Milk</li></ul>
We see no spaces between tags.
Whereas, Code for <div id="myFonts">
<div id="myFonts">
<i>1</i>
<i>2</i>
<i>3</i>
<i>4</i>
<i>5</i>
</div>
You see the empty spaces before the <i>? Those got added as a text node in the childNodes object of your div
<div id="myFonts">
<i>1</i>
</div>
You could have used exactly the same code that you have currently, if you had chosen to rather Not add spaces before the <i> tags.
For Eg. like this:
<div id="myFonts"><i>1</i><i>2</i><i>3</i><i>4</i><i>5</i></div>
Check modified HTML for div here:
CodePen

Angular 2+ switching betweens elements, based on click

So, I'm trying to use a angular way to send the buttons created to either the #start or #finish divs, based on click on the buttons selected, so in a way they would send themselves if you may if they follow a condition, which in this case is to be either inside of the #start or #finish divs. With Jquery, I just check what's the parent of certain element, if matches one, I send it to the other, and vice versa. Now with angular, I have been looking into the whole, rendering and stuff, but my head can't just understand the overall picture, and I even though I was able to click and send the element clicked to a different div, I couldn't do it with the other buttons created, and also with the button that was first clicked, in the other div.
<div class="ui raised very padded text container segment"
#finish>
</div>
<div class="ui raised very padded text container segment" #start>
<button
*ngFor='let word of ge_array'
(click)="goToNext()"
>{{word}}</button>
</div>
Does anybody know how to tackle this situation?
Renan, I would change all your plan. I have an array of element. this elements have a property "place". I will show in two div one for "plan1" and the other for "plan2".
//the .ts is like
items:any[]=[{word:'uno',place:1},{word:'dos',place:1},{word:'tres',place:2}]
get items1(){
return this.items.filter(it=>it.place==1);
}
get items2(){
return this.items.filter(it=>it.place==2);
}
//the .hmtl like
<h2>place 1</h2>
<button *ngFor="let item of items1" (click)="item.place=2">{{item.word}}</button>
<h2>place 2</h2>
<button *ngFor="let item of items2" (click)="item.place=1">{{item.word}}</button>
I would prefer using a getter and have "item1" and "item2". the click make that the property "place" of the item becomes 2 in place 1 and becomes 1 in place2
You can make two *ngFor over the same array also using a *ngIf like
<h1>place 1</h1>
<div *ngFor="let item of items">
<button *ngIf="item.place==1" (click)="item.place=2">{{item.word}}</button>
</div>
<h1>place 2</h1>
<div *ngFor="let item of items">
<button *ngIf="item.place==2" (click)="item.place=1">{{item.word}}</button>
</div>
and forget the getters

how to add dynamic ng-repeat in page onclick

I want to create dynamic ng-reapet and push the array element on click event:
my controler code is
$scope.AddlistItem = function (index) {
$scope.selecttaglist($scope.tag);
};
$scope.selecttaglist = function (tag) {
//var i=$scope.selectedTags.length;
angular.forEach($scope.selectedTags,function(tag,index){
console.log(tag.name);
$scope.selectedTagslist.push(tag);
})
And View Code:
<ul id="boxElement" ><li ng-repeat="tag in selectedTagslist" ng-controller="ItemController" ng-bind="tag.name" ></li></ul>
Html Code
<div class="AddButtn" id="aDD{{item.name}}" ng-controller="ItemController" ng-click="AddlistItem()" ></div>
problem is that when i clicking on link. array element are pushing on all ng-reapet element.i want array will be push only on clicked element container. iam not sure my approach is write or wrong for doing this. i am new in angularjs. anyone can help on this.
If the model for ng-repeat is the same then you need different approach as model drives the view so i.e. you will need more than one
<ul id="boxElement" ><li ng-repeat="tag in selectedTagslist" ng-controller="ItemController" ng-bind="tag.name" ></li></ul>
<ul id="boxElement" ><li ng-repeat="tag in selectedTagslist2" ng-controller="ItemController" ng-bind="tag.name" ></li></ul>
with a copy of the first element - angular.copy otherwise the object will be connected by reference and the effect will be same
hope that makes sense

Selecting an element created using document.createElement jquery/javascript

I am creating a number of div and ul elements using document.createElement. In a case, when I am creating these elements I need to take reference of one of those elements that I created above and assign it as the parent to element that's next in line to be created.
I have the id (e.g "idoftheelement") of the created element but when I do
$('#idoftheelement')
I don't get the element.
Is there a possibility to get the element. If yes how ?
Edit:
Below is html structure I am trying to generate based on a JSON input data. Every element in the JSON array could have a child array of elements. All the elements will have the same markup and the difference is only in where they are getting placed. I have to construct this in a recursive way i.e for every JSON element check if child elements(and the child elements could also contain child elements) are present, if yes then i should append these child elements to corresponding parent thread block. This is why I need to know if there is a direct way to get the reference of parent element that is in context to the current element so that It can be appended.
<div id="comment-12345">
<div id="threads-block-12345"
<ul id="thread-12345">
<li id="thread-item-12346">
<div id="comment-12346">
<div id="threads-block-12346"
<ul id="thread-12346">
<li id="thread-item-12347">
<!--keeps growing if there till all the children are processed-->
</li>
</ul>
</div>
</div>
</li>
<li>
<div id="comment-xyz">
<div id="threads-block-xyz"
<ul id="thread-xyz">
<li id="thread-item-xyz">
<!--keeps growing if there till all the children are processed-->
</li>
</ul>
</div>
</div>
</li>
</ul>
</div>
The code $('#idoftheelement') tells me you are using jQuery, so you shouldn't be using document.createElement. What you need to do is create the element using jQuery and reference it by that variable. For example:
var $node = $("<div id='" + id + "'>")
$node.append( ... new elements ... )

Href: Target ID in Current DIV

I have a list of <li> items being generated from a CMS/DB. Each <li> has a <div> in it which contains a link to a lightbox (a hidden <div>). The link targets the id of the hidden <div> (#inline-content-print) so the javascript plugin triggers and pulls up the lightbox.
The problem I'm running into is that all of the <li>s on the page generate with the same hidden div id (I can change this to classes). So no matter which <li> href is clicked, it always pulls up the lightbox for the first <li> on the page (the first instance of the id). I need a way for the href to say "open #inline-content-print" from THIS div (the one the link being clicked lives in)".
<li>
<div class="store-buttons-bg hide-print-buttons-{tag_Hide-Print-Buttons}">
PRINT
<div style="display: none;" id="inline-content-print">
CONTENT OF LIGHTBOX
</div>
<!-- end inline-content-print -->
</div>
</li>
Any advice would be greatly appreciated.
What server side language are you using? Is it producing these list items in a loop? Is there an index on that loop? If so, this would work for you.
[Server language version of a for loop with an index variable named "i"]
<li>
<div class="store-buttons-bg hide-print-buttons-{tag_Hide-Print-Buttons}">
PRINT
<div style="display: none;" id="inline-content-print_[server language output of "i"]">
CONTENT OF LIGHTBOX
</div>
<!-- end inline-content-print -->
</div>
</li>
[server language version of an end to the for loop]
Assuming you want to do this with jQuery/Javascript, you could use something like this:
$(document).ready(function()
{
$('li a.store-button').click(function(e)
{
var lightboxElement = $(e.currentTarget).find('div');
lightboxElement.show(); // or whatever function you need to display
return false;
});
});
Which is a little script that:
Waits for the page to load.
Finds your list elements (by li object type and the class on the links)
Intercepts click events.
Finds the div element nested in the link that was clicked.
Displays (or runs another function) on the target lightbox element.

Categories

Resources