Why does jQuery not seem to add text to nested spans? - javascript

I have tried this in a couple of different orders, and neither works. I start off like this:
var messageHtml = "<span class='message-bundle' id='" + randomId + "'></span>";
$(".visualizer").append(messageHtml);
randomId is a random number. Then I set the top and left positioning for the element (it is set to position: absolute).
Then I .append() the following empty spans to this span:
$(".message-bundle#" + randomId).append("<span class='usertag' id='" + randomId + "'></span><br/>" +
"<span class='message' id='" + randomId + "'></span>");
I keep them empty initially because I want to sanitize their contents against XSS exploits. So far, everything is fine. Then I add sanitized text to these nested spans.
$(".message-bundle .usertag#" + randomId).text(msgObj.usertag);
$(".message-bundle .message#" + randomId).text(msgObj.message);
This never works. I have tried printing out the .text() of these two elements, but they contain empty strings. Why is this? Can I somehow sanitize the strings and add them inline when creating the .usertag and .message spans above (instead of using .text())?

ids need to be unique throughout the entire document, and you’re duplicating them. It’s an inappropriate use of them anyway, though; you should work with elements more and HTML less.
var message =
$('<span>', { class: 'message-bundle' })
.append($('<span>', { class: 'usertag', text: msgObj.usertag }))
.append($('<span>', { class: 'message', text: msgObj.message }));
$('.visualizer').append(message);
This appends elements that you can manipulate as proper objects from the moment they’re created, rather than by re-selecting them after adding HTML.

IDs must be unique within the document - you're assigning the same ID to multiple elements. (Or is that just over-simplified code?)

Related

How can I use innerText instead of innerHTML in dynamically created HTML elements?

I use Javascript to dynamically create a lot of elements. Divs, images, spans, etc.
Here is an example piece of code that my JS would run:
infoCell.innerHTML = "Submitted by " + "<a href='/user/" + this.poster + "'><img src='" + this.poster_avatar_src + "' class='avatarimg'> <span style='color:blue'>" + this.poster + "</span> </a>in " + "<span style='color:blue; font-weight: 900;'><a href='/h/" + href + "'>" + this.topic + "</a></span>"
This was written early in my JS development, but now I realize that it can very quickly become very insecure, as almost all of the javascript variables being inserted into the HTML are written by the user with no limitations to character usage, etc.
How can I go through my javascript and change all of these so they still function, but without worrying about users inserting script into my site?
I am fine rewriting a lot but I would like to not do this again. I have about 90 innerHTML DOM modifications in my main JS file (typescript).
you could try to use a combination of document.createElement and HTMLElement.append
an example for the first <a> tag:
function makeElem (tagname, properties) {
let elem = document.createElement(tagname);
for (const key in properties) {
elem[key] = properties[key];
}
return elem;
}
infoCell.append("Submitted by ");
let a = makeElem("a", {href:'/user/"' + this.poster + '"'});
a.replaceChildren(makeElem("img", {'src':this.poster_avatar_src, 'className':'avatarimg'}), makeElem("span", {'textContent':this.poster,'style':'color:blue;'}));
infoCell.append(a);
this might not be the easiest but it should work, the reason for the "makeElem" function is purely convenience and you don't necessarily need it
There are a few approaches.
One is to use a sanitizer to translate all of the dynamic values into properly escaped strings before interpolation - but you'd have to be sure you get it right, otherwise there could still be problems.
Another way is to construct the element structure, then insert the dynamic strings at the appropriate points, eg:
const cell = document.createElement('div');
cell.innerHTML = `
Person info
<div class="name"></div>
<div class="age"></div>
`;
cell.querySelector('.name').textContent = name; // where name is dynamic
cell.querySelector('.age').textContent = age; // where age is dynamic
But this can be tedious if you have a lot of dynamic values to insert.
A third way (and one that I'd recommend for serious applications) is to use a framework to handle it for you. For example, in React, the above "cell" could be made like:
const Cell = ({ name, age }) => (
<div>
Person info
<div class="name">{name}</div>
<div class="age">{age}</div>
</div>
);
It takes some learning and getting used to, but once you get going it's a lot easier to read and write than other approaches.

Template Literals How To Make a ?Universal Rule?

I'm new to coding but I am using template literals in a project using css variables. this example sets all the variables in one shot inside a function. this is referring to inputs which all have an eventlistener on them.
document.documentElement.style.setProperty(`--${this.name}`, this.value + suffix);
I want to set another rule for a span to display the values. right now I am doing each one individually like this.
heightDisplay.innerHTML = height.value + 'px';
widthDisplay.innerHTML = width.value + 'px';
all the span's ids will be " this.name + 'Display' "
I want to right a rule that sets them all at once using the literals(similar to the rule that sets the variables) instead of writing 30 lines of code.
I can't figure out the syntax to add Display on there and i don't know where to put the back ticks.
I assume this is possible, since pretty much everything in Javascript is.
Thanks for your time.
I assume that you have an iteration where this Code-Line is Executed:document.documentElement.style.setProperty(--${this.name}, this.value + suffix);
In this Iteration you can set the Values of your spans like this:
var Span = document.querySelector(`[id*= ${this.name}]`);
Span.innerHTML = this.value+ "px";
Edit:
The querySelector - function gets a css-selector as a parameter. the [] - Brackets is a css selector that gets an element with an atribute (in this case id) and a value (this.name). The *= between them means, that you can select an element that has a substring in the value of the atribute.

jQuery Finding Element By Id With Space And Via Variable

I am able to locate an element of an element through an id and add a class when the ID is hard coded, e.g:
var tableId = el.id;
$('#' + tableId).find("[id='Checkout On']").addClass('highlight');
However, I want to pass 'Checkout On' as a variable, e.g:
var tableId = el.id;
var childEl = col.id;
$('#' + tableId).find("[id=" + childEl + "]").addClass('highlight');
However this doesn't seem to work.
Update:
Completely understand IDs should not have spaces however this is not something I am able to resolve.
You've left out the quotes in the version using the variable:
$('#' + tableId).find("[id='" + childEl + "']").addClass('highlight');
// ------------------------^---------------^
But note that an id with a space in it is invalid. From the spec:
3.2.5.1 The id attribute
The id attribute specifies its element's unique identifier (ID). [DOM]
The value must be unique amongst all the IDs in the element's home subtree and must contain at least one character. The value must not contain any space characters.
(my emphasis)
That means that even if this works on one browser, there's no guarantee it'll work in another, or even in the next minor release of the one where it worked. (I bet it will, but there's no reason to tempt things like that...)
You should never use whitespaces for any id or class names. Go with snake_case or camelCase: checkout_on or checkoutOn
If you want to select an element with ID use the #... selector. It's better. Like this:
var tableId = el.id;
var childEl = col.id;
$('#' + tableId).find("#" + childEl ).addClass('highlight');
NOTE: IDs can not have spaces. Check this for more info.

How does JQuery empty() method work?

I know that the empty method removes all children in the DOM element.
In this example however, why does removing the empty method result in duplicate entries:
and putting it in results in a normal page:
var renderNotesList = function()
{
var dummyNotesCount = 10, note, i;
var view = $(notesListSelector);
view.empty();
var ul = $("<ul id =\"notes-list\" data-role=\"listview\"></ul>").appendTo(view);
for (i=0; i<dummyNotesCount; i++)
{
$("<li>"+ "" + "<div>Note title " + i + "</div>" + "<div class=\"list-item-narrative\">Note Narrative " + i + "</div>" + "" + "</li>").appendTo(ul);
}
ul.listview();
};
I don't know why empty() doesn't work but I found this
... so until this is sorted everyone should just use:
el.children().remove(); instead of el.empty();
( jQuery.empty() does not destroy UI widgets, whereas jQuery.remove() does (using UI 1.8.4) )
Without seeing how your JavaScript is being used in your page, I suspect that you must be calling the renderNotesList() function twice and thus generating to unordered lists.
When you use the .empty() method, you are removing the first ul list, so you only see one instance. Without the call to .empty(), you retain both.
However, I can't say where or how this is happening in you web page without seeing more, but at least you now have some idea of what to look for.
Demo Fiddle
I built a demo using your JavaScript, but I was sort of guessing as to how you are using it.
Fiddle: http://jsfiddle.net/audetwebdesign/UVymE/
Footnote
It occurred to me that the function ul.listview() may actually be appending a second copy of the ul to the DOM. You need to check the code or post it for further review.

jQuery selector after dynamically created element

I am trying to get an dynamically created element using a jQuery selector but it is returning an empty array.
The first thing I am doing is grabbing an empty div:
var packDiv = document.getElementById('templates');
packDiv.innerHTML = "";
then adding items to it in a loop:
packDiv.innerHTML = packDiv.innerHTML + "<img id='" + thumbName + "' src='thumbs/" + thumbName + "'/>";
after the loop finishes I try to select an item using:
console.log($("#"+thumbName));
and it returns the empty array. All the things I search on show to use .on but all the examples show that is to set event handlers.
My question is how do I format a selector for dynamically created elements?
Assuming thumbName is a file name, for example foo.jpg, it would not be parsed by jQuery as you would expect. .jpg part of the name is treated as a class name, and since you are not providing this class name for that element, jQuery returns an empty array - it does not find anything matching your selector. You are actually searching for an element with id foo and class name jpg.
The way I would go with this is something along these lines:
var packDiv = $('#templates');
packDiv.empty();
//inside a loop
packDiv.append("<img class='" + thumbName.replace(/\./g,'') + "' src='thumbs/" + thumbName + "'/>");
console.log($("."+thumbName.replace(/\./g,'')));

Categories