Container.id vs getElementById - weird error - javascript

I have a function for my rpg game that simulates grabbing an element in a create js container object by removing it from the container (thereby removing it from the stage) when the player gets near it.
function grabIt(NPC_id, index) {
console.log(ContainerOfAnimals.children[index].id);
var childToRemove = document.getElementById(ContainerOfAnimals.children[index]);
console.log(childToRemove);
ContainerOfAnimals.removeChild(childToRemove);
}
The first console.log gives correct id of child: 21
But when I want to grab the child container object using getElementById, the child is null.
Why is this?

EaselJS elements have an id property but there aren't DOM elements. They're plain JavaScript objects (instances of a subclass of DisplayObject). And they're not added to the DOM tree.
Therefore, you can't get them by using document.getElementById.
To remove your element, simply do
ContainerOfAnimals.removeChild(ContainerOfAnimals.children[index]);
or (faster)
ContainerOfAnimals.removeChildAt(index);

Related

JavaScript: Add a class specific tag in classList

I have a class named X which has multiple <span> inside and I also have a css selector X span.
In JavaScript how can I use X span instead of X in the following case:
document.querySelector('.ABC').classList.add(X)
I tried document.querySelector('.ABC').classList.add(X span) which definitely isn't working.
In Javascript, working with the HTML works like this:
You ask the browser to give you an object or list of objects that correspond to the HTML elements
You use the objects to modify the page
If I understand correctly, what you want to do is:
Find element by className ".ABC"
Find "span"-s inside that element
Give those spans a class
To do those, follow these steps:
// get the first element that matches .ABC
let parent = document.querySelector(".ABC");
// now parent is either an Element, or undefined/null
// if it's not null, we can call querySelectorAll
// get all elements inside the parent that are spans
let spans = parent.querySelectorAll("span");
// spans is now either an array of Elements, or undefined/null
// if it's not null, we can iterate over it
for (let span of spans) {
span.classList.add('X');
}

Converting an Array of HTML Elements to Nodelist

I'm writing a function for swapping the position of child elements in a parent element.
<div class="parent">
<div class="first-child"></div>
<div class="second-child"></div>
</div>
So I'm getting the children of .parent turning the Nodelist into an array, reordering the array to swap the order / position of the elements i.e first-child, second-child becomes second-child, first-child - This all works perfectly. However, ideally the function will return the parent element with the reordered structure, but because I effectively spliced the nodelist into an array the elements in the array are no longer considered 'nodes' meaning I get an error when attempting to append it as a child to the parent.
So, how can I convert an array of elements back into a Nodelist as I understand that a Nodelist is not native to javascript?
Here's a Codepen of what I have so far. http://codepen.io/anon/pen/QNPKqB?editors=0011
Thanks!
The error in your code isn't that you need a NodeList, it's that you've named both a function and an element swap.
var parent = document.querySelector('.swap');
swap(parent, first, second);
Is what you need
I don't have a codepen account so instead, see the working code here:
https://jsfiddle.net/owr15hnf/
This is how you can convert HTML to node list,
const targetElement = document.getElementById('targetElement');
const htmlElementsArray = Array.from(targetElement.children).map(el => el.outerHTML)
// htmlElementsArray contains an array of HTML Elements.
console.log(htmlElementsArray,'ArrayList.')
const nodeList = new DOMParser().parseFromString([htmlElementsArray].join(''), "text/html").body.childNodes;
// nodeList is the converted Html list to node list
console.log(nodeList,'nodeList')
You can find the example here: https://codepen.io/furki911/pen/qByzdXm?editors=1111

Protractor check if element has children

I have a click event where child elements get appended to a parent element and then get removed on another click event. I want to test if those elements got removed from the parent. So is there something like
var container = element(by.css('.container'));
expect(container.length).toEqual(0);
that checks if there are any children elements?
There are special methods for checking if an element is present:
elm.isPresent();
parentElm.isElementPresent(childElm);
browser.isElementPresent(elm);
And here are the differences between them:
In protractor, browser.isElementPresent vs element.isPresent vs element.isElementPresent
Note that you can still find all elements inside a container and check the count:
var container = element(by.css('.container'));
expect(container.all(by.xpath("./*")).count()).toEqual(0);
Another alternative could be to check the inner HTML:
expect(container.getInnerHTML()).toEqual("");

Javascript: How to change a nodes name?

For example I have this HTML:
<body>
<div>Text</div>
</body>
And I would like to change the div to something else like p.
This is what I have tried but doesn't works:
var div = document.getElementsByTagName("div")[0]; // Get Element
div.nodeName = "p"; // Change It's Node Name to P
Please no libraries, and I don't really want to replace the actual div with a new p :)
You cannot just change an element. You have to create a new one. E.g.:
var div = document.getElementsByTagName("div")[0];
var p = document.createElement('p');
p.innerHTML = div.innerHTML;
div.parentNode.replaceChild(p, div);
But this could lead to invalid markup, if the original element contains nodes that cannot be descendants of the new node.
Reference: document.createElement, Node.replaceChild
Note: A better version (because it doesn't depend on serializing DOM to text and back and preserves attributes), can be found at https://stackoverflow.com/a/8584158/218196 .
The reason you can't just change the tagName property is because different HTML tags are actually different classes of objects. A div tag is an HTMLDivElement instance, a p tag is an HTMLParagraphElement instance, and so on. These classes can have vastly different properties and interfaces, so turning one into another is not as trivial as you'd think.
You can't.
As the MDC docs say:
nodeName is a read-only attribute.
You'll have to create a new element and give it the right content and attributes.
You cannot. The propery you're after is tagName, but it is read only. You would instead have to create a new node of the desired type, then transfer the innerHTML (and any other properties like className or style) to the new node. Then, insert the new node into the old node's parent, then remove the old node (or use replaceChild).
In other words, the long road is the only road.
I solved this in an XML scenario (eg. where there is no innerHTML) like so:
function renameNode (node, newNodeName) {
const newNode = node.ownerDocument.createElement(newNodeName);
Array.from(node.attributes).forEach(attr => newNode.setAttribute(attr.localName, attr.value));
Array.from(node.childNodes).forEach(childNode => newNode.appendChild(childNode));
node.parentElement.insertBefore(newNode, node);
node.parentElement.removeChild(node);
}
Does not return anything, but will update your DOM.

jQuery $.data(): Possible misuse?

Perhaps I'm using $.data incorrectly.
Assigning the data:
var course_li = sprintf('<li class="draggable course">%s</li>', course["fields"]["name"]);
$(course_li).data('pk', course['pk']);
alert(course['pk']); // shows a correct value
alert($(course_li).data('pk')); // shows null. curious...
course_li is later appended to the DOM.
Moving the li to a different ul:
function moveToTerm(item, term) {
item.fadeOut(function() {
item.appendTo(term).fadeIn();
});
}
Trying to access the data later:
$.each($(term).children(".course"), function(index, course) {
var pk = $(course).data('pk');
// pk is undefined
courses.push(pk);
});
What am I doing wrong? I have confirmed that the course li on which I am setting the data is the same as the one on which I am looking for it. (Unless I'm messing that up by calling appendTo() on it?)
When you store the data:
$(course_li).data('pk', course['pk']);
you're creating an element but not saving it, so it's lost. Your alert test test the wrong value; it should be:
$(course_li).data('pk', course['pk']);
alert($(course_li).data('pk'));
which is null. Consider:
$(course_li);
$(course_li);
This creates two different elements with source equal to course_li, which are then promptly lost. What you need to do is create the element first, then work with that single element (i.e. don't call $(course_li) more than once). For example,
var course_li = $(sprintf('<li class="draggable course">%s</li>',
course["fields"]["name"]));
course_li.data('pk', course['pk']);
parent.append(course_li);
Note that course_li now holds an element, rather than a string.
try checking to see if the element being created by this call:
$(course_li)
is a single 'li' element, or a div. From the doco:
When the HTML is more complex than a single tag without attributes, as it is in the above example... snip ...Specifically, jQuery creates a new <div> element and sets the innerHTML property of the element to the HTML snippet that was passed in
So it's probably creating a div that you are assigning the data to, so when you select the 'li' itself, you are getting a child of the actual element that you set the data on.

Categories