Is it not possible to use HTML entities in a title attribute? - javascript

The above screengrab is from Firefox. The cursor is hovering over the yellow spot at the left hand side of the image. It is an <img> element (well actually it's an image together with an image map containing a single circular <area> element, but I assume this distinction is unimportant) that has been created and styled in JavaScript, including the application of a title attribute (constructed by cutting and gluing strings). How can I get this to behave and show the intended character, an en dash, instead of –? It works for innerHTML (the text "Barrow-In-Furness" in the top middle-left is a div that was also created using JavaScript, and its innerHTML set.)
Edit: In response to question of Domenic: Here is the JavaScript function that builds and applies the title attribute (in addition to performing other jobs):
var StyleLinkMarker = function (LinkNumber, EltA, EltI) {
var AltText = LocationName[LinkStart[LinkNumber]] +
" to " +
LocationName[LinkEnd[LinkNumber]];
if (!EltA) {
EltA = document.getElementById("link_marker_area" + LinkNumber);
EltI = document.getElementById("link_marker_img" + LinkNumber);
}
if (LinkStatus[LinkNumber] === 9) {
var CanBuyLinkCode = BoardPreviewMode ? 0 : CanBuyLink(LinkNumber);
if (CanBuyLinkCode === 0) {
EltI.src = ImagePath + "icon-buylink-yes.png";
AltText += " (you can buy this " + LinkAltTextDescription + ")";
} else {
EltI.src = ImagePath + "icon-buylink-no.png";
AltText += " (you cannot buy this " + LinkAltTextDescription;
AltText += CanBuyLinkCode === 1 ?
", because you aren't connected to it)" :
", because you would have to buy coal from the Demand Track, and you can't afford to do that)";
}
} else if ( LinkStatus[LinkNumber] === 8 ||
(LinkStatus[LinkNumber] >= 0 && LinkStatus[LinkNumber] <= 4)
) {
EltI.src = ImagePath + "i" + LinkStatus[LinkNumber] + ".png";
if (LinkStatus[LinkNumber] === 8) {
AltText += " (orphan " + LinkAltTextDescription + ")";
} else {
AltText += " (" +
LinkAltTextDescription +
" owned by " +
PersonReference(LinkStatus[LinkNumber]) +
")";
}
} else {
throw "Unexpected Link Status";
}
EltA.alt = AltText;
EltA.title = AltText;
};
LocationName is as follows:
var LocationName = [
"Barrow–In–Furness", "Birkenhead", "Blackburn", "Blackpool",
"Bolton", "Burnley", "Bury", "Colne",
"Ellesmere Port", "Fleetwood", "Lancaster", "Liverpool",
"Macclesfield", "Manchester", "The Midlands", "Northwich",
"Oldham", "Preston", "Rochdale", "Scotland",
"Southport", "Stockport", "Warrington & Runcorn", "Wigan",
"Yorkshire"
];

You aren't setting the title attribute, you are setting the title property, which expects text and not HTML (although the setAttribute method also expects a text string).
Generally speaking, when dealing with DOM manipulation, you provide text and not HTML. .innerHTML is the notable exception to this rule.

Here's an easy way to convert from HTML to text:
function convertHtmlToText(value) {
var d = document.createElement('div');
d.innerHTML = value;
return d.innerText;
}
Your code could then be updated to this:
EltA.title = convertHtmlToText(AltText);

Related

clear createElement from page using removeChild

I am trying to figure out how I can clear the <p> elements that are generated from a for loop before the for loop starts.
Essentially. I have a webpage where someone searches something and a list of results are shown. However if I search for something else, the new results get appended instead of clearing the old results first.
Here is the code:
async function parseitglinkquery() {
var queriedresults = await getitglinkquery();
console.log(queriedresults);
const output = document.querySelector('span.ms-font-mitglue');
output.removeChild("createditginfo"); \\tried to clear the <pre> here and it failed
for (let i = 0; i < queriedresults.length; i++) {
let text = "Company: " + JSON.stringify(queriedresults[i]["data"]["attributes"].organization-name) + "<br>"+
"Name: " + JSON.stringify(queriedresults[i]["data"]["attributes"].name) + "<br>" +
"Username: " + JSON.stringify(queriedresults[i]["data"]["attributes"].username).replace("\\\\","\\") + "<br>" +
"Password: " + JSON.stringify(queriedresults[i]["data"]["attributes"].password);
let pre = document.createElement('p');
pre.setAttribute("id", "createditginfo")
pre.innerHTML = text;
pre.style.cssText += 'font-size:24px;font-weight:bold;';
output.appendChild(pre);
console.log(typeof pre)
}
}
I tried to create a try and catch block where it would try to clear the <p> using removeChild() but that didn't seem to work either.
async function parseitglinkquery() {
var queriedresults = await getitglinkquery();
console.log(queriedresults);
const output = document.querySelector('span.ms-font-mitglue');
try {
output.removeChild("createditginfo");
}
catch(err){
console.log(err)
}
for (let i = 0; i < queriedresults.length; i++) {
let text = "Company: " + JSON.stringify(queriedresults[i]["data"]["attributes"].organization-name) + "<br>"+
"Name: " + JSON.stringify(queriedresults[i]["data"]["attributes"].name) + "<br>" +
"Username: " + JSON.stringify(queriedresults[i]["data"]["attributes"].username).replace("\\\\","\\") + "<br>" +
"Password: " + JSON.stringify(queriedresults[i]["data"]["attributes"].password);
let pre = document.createElement('p');
pre.setAttribute("id", "createditginfo")
pre.innerHTML = text;
pre.style.cssText += 'font-size:24px;font-weight:bold;';
output.appendChild(pre);
console.log(typeof pre)
}
}
You only have to clear the output-node right before the loop using the innerHTML-property.
output.innerHTML = '';
https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML
There are other ways too, if you want to remove only specific childs. You can make use of Node.childNodes together with a loop. With this, you have the opportunity to remove only specific children.
[...output.childNodes].forEach(childNode => {
output.removeChild(childNode)
});
// or specific
[...output.childNodes].forEach(childNode => {
// remove only <div>-nodes
if (childNode.nodeName == 'DIV') {
childNode.remove();
}
});
https://developer.mozilla.org/en-US/docs/Web/API/Node/childNodes
https://developer.mozilla.org/en-US/docs/Web/API/Node/removeChild
https://developer.mozilla.org/en-US/docs/Web/API/Element/remove
The answer above is correct, but I believe that the original code and the answer can be further improved:
for variables that do not change, use const instead of let - this helps explaining the intention.
there seems to be a bug - if you have an attribute called "organization-name", you cannot access it as a property (...["attributes"].organization-name), but you can use array access instead: ...["attributes"]["organization-name"]. Otherwise, the code end up effectively as ...["attributes"].organization - name
when you have long property paths that are repeated a lot (like queriedresults[i]["data"]["attributes"] in your case), consider assigning them to a local variable.
This makes the code more readable and is actually better for performance (because of less array lookups):
// old code
let text = "Company: " + JSON.stringify(queriedresults[i]["data"]["attributes"].organization-name) + "<br>"+
"Name: " + JSON.stringify(queriedresults[i]["data"]["attributes"].name) + "<br>";
// new code
const attrs = queriedresults[i]["data"]["attributes"];
let text = "Company: " + JSON.stringify(attrs['organization-name']) + "<br>"+
"Name: " + JSON.stringify(attrs.name) + "<br>";
you are creating several pre's in a loop, this is ok, however the element id must be different! Each id must be unique in the whole page:
// wrong
for (let i = 0; i < queriedresults.length; i++) {
...
pre.setAttribute("id", "createditginfo")
...
}
// right/better
for (let i = 0; i < queriedresults.length; i++) {
...
pre.setAttribute("id", "createditginfo" + i) // "i" for uniqueness
...
}
you can use innerText, in which case you do not need to encode the attributes, plus it simplifies the code:
const pre = document.createElement('p');
pre.innerText = [
"Company: " + attrs["organization-name"],
"Name: " + attrs.name,
"Username: " + attrs.username, // presumably you don't need to decode "/" anymore :)
"Password: " + attrs.password
].join("\n") // so we declared a bunch of lines as an array, then we join them with a newline character
Finally, regarding the original question, I see three main ways:
simply clearing the contents of the parent with output.innerHtml = ''
iterating over each child and removing it with output.removeChild(childPre)
you can keep references to the generated pre's (eg, store each element in an array) and remove the later with point 2, but this is less automatic but in some cases it might be more efficient if you have a tone of elements at that same level.

Current Alternative To .fontcolor() method in Javascript

I was given this task with some existing code to change the string color of each of three selector.value(s) that is output onto an input element to three different colors. The code boils the three selectors into a single output variable. Without destroying the code, I cannot figure out how to select each individual variables prior to condensing them.
If I could use the fontcolor() method, my life would be great but it's 2018 and I can't. Is there any way you can think of to solve this issue?To clarify, I need to alter the colors of the strings that belong to output(red), select1.value(blue) and select2.value(black.
Most of the action for this is happening in the parseOutput() function but I'm just stuck and don't think it's possible without rewriting the entire program.
function updateSelector(result){
var options = result.options;
var elementId = "select" + result.element;
var logger = document.getElementById('logger');
var selector = document.getElementById(elementId);
//logger.innerHTML = JSON.stringify(elementId);
selector.innerHTML = options;
selector.disabled = false;
}
google.script.run.withSuccessHandler(updateSelector).processOptions(0);
plate();
function resetAll(){
for (var i = 0;i<3;i++){
var selector = document.getElementById('select' + i);
selector.disabled = true;
selector.innerHTML = "";
}
google.script.run.withSuccessHandler(updateSelector).processOptions(0);
}
function finalSelection(){
var output = document.getElementById('out');
//output.focus();
output.select();
}
function plate(){
var plate = document.getElementById('plate');
plate.innerHTML = atob('Q3JhZnRlZCBieTogWmFjaGFyeSBTdGFjaG93aWFr');
}
//Adds the location as initial output, followed by divider, application, and issue if select1 is selected
//else statement added so if select0 is [Costco Website Name], to ommit the " - "
function parseOutput(){
var output = "";
if (select1.value.length > 0 && select0.value !== "[Costco Website Name]"){
output = output + ' - ' + select1.value + ' // ' + select2.value;
} else{
output = output + select1.value + ' // ' + select2.value;
}
out.value=output.trim();
}
And this is the Div that displays the output:
<div class="wide"><p><input class="wide" type="readonly" id="out" onfocus="this.select();"></p></div>
A modern replacement for fontcolor would use a span and a style (or class), e.g.:
function modernFontColor(str, color) {
return '<span style="color: ' + color + '">' + str + '</span>';
}
or
function modernFontClass(str, cls) {
return '<span class="' + cls + '">' + str + '</span>';
}
...where the class defines the styling.

Trying to get this string to appear in a paragraph

Trying to get this string I have in JavaScript to appear in a paragraph in my HTML page by mousing over another paragraph.
function showInfo()
{
for (i = 0; i < object2; i = i + 1)
{
var myParagraph = "Name of Business: " + info.insurance[i].name + "\nState: " + info.insurance[i].state + "\nDiscount: " + info.insurance[i].discount + "\n" + "(" + i + 1 + "of" + object2 + ")"
}
}
myDiscount.addEventListener("mouseover", showInfo, false);
myDiscount.addEventListener("mouseout", showInfo, false);
<p id="discount">Show me the discounts!</p>
<p id="myP"></p>
If you want to show the next element of the info.insurance array each time you mouse over the paragraph, you shouldn't be using a for loop. That will do it all at once, not once for each mouseover. You need to put the counter in a global variable, and just increment it each time you call the function.
Yuo show it by assigning it to the innerHTML of the paragraph. You also need to use <br> rather than \n to make newlines (unless the style of the paragraph is pre).
var insurance_counter = 0;
function showInfo() {
var myParagraph = "Name of Business: " + info.insurance[insurance_counter].name + "<br>State: " + info.insurance[insurance_counter].state + "<br>Discount: " + info.insurance[insurance_counter].discount + "<br>(" + (insurance_counter + 1) + "of" + object2 + ")";
document.getElementById("myP").innerHTML = myParagraph;
insurance_counter++;
if (insurance_counter >= object2) { // wrap around when limit reached
insurance_counter = 0;
}
}

How to leave a placeholder within a variable to be filled later using Javascript?

On paper this paper this seems like a very simple operation, but for some reason Javascript does not seem to like it. Basically I have the following code:
var news = "<b>" + place_name + ", " + county + ""<img id = 'centre' src=" + picture + ">" + "</b><ul><br>";
The general idea is that picture is a variable that will be filled later via:
news.picture = entry2.picture;
which is a link to provide to the img source. However, when I do:
console.log(news.picture);
The variable remains undefined. Is this the correct way to go about things?
That's not the way you are supposed to do that. You have to have your variables set and then you can construct a string like that.
What you need now, is basically a function, like this:
var createNews = function(place_name,county,picture) {
return "<b>" + place_name + ", " + county + "<img id = 'centre' src=" + picture + ">" + "</b><ul><br>";
}
var news = createNews("Place","county","pic.jpg");
console.log(news);
Or you can do it like this, if you prefer:
var createNews = function(obj) {
return "<b>" + obj.place_name + ", " + obj.county + "<img id = 'centre' src=" + obj.picture + ">" + "</b><ul><br>";
}
var news = {
place_name : "Someplace",
county : "Somewhere",
picture : "foo.png"
};
var newsItem = createNews(news);
console.log(newsItem);
news is a variable made out of strings and variables.
So you cant use news.picture.
Though you can make the variable a function object.
var news = function() }
this.picture = "something";
this.getString = function() {
return this.picture+"some string";
};
};
Then you can get and set the picture variable inside news with news.picture and get the string with news.getString().

Unpack array of objects into strings to display on a template in meteor

I have a collection that has within it an object that holds an array of yet more objects that need to be unpacked to display in a template eventually.
The item in the sub-object is an order, and the order contains an array of line items.
I can get the order out fine, and see the array of line items no problem, but that's where I come unstuck.
I thought the following would work (using js to convert them into an array of strings to then display somehow)
Template.editEvent.helpers({
lineItems: function(req) {
var order = req.order;
console.log(order);
var lines;
var count = 1;
_.each(order, function(item) {
var string;
string +count++ + '. ';
if(item.age) { // we have a cloathing string
string += item.age + " " + item.sex + " " + item.Season + " " + "Number: " + item.Number;
lines.push(string);
}
else if(item.pushers) {
string += "Pushers needed: " + item.pushers;
lines.push(string);
}
else if(item.cots) {
string += "Cots needed: " + item.pushers;
lines.push(string);
}
else if(items.extra) {
string = "Extra info: " + item.extra;
lines.push(string);
}
else {
string = "Don't know this field";
lines.push(string);
}
console.log(lines);
});
return lines;
}
})
Where the tests are to see if the line item starts with the field shown (because the line items can be different).
However, the _.each is throwing up on the client, (it works fine in the startup code, so I guess from that its server only?)
Am I barking up the wrong tree here, should this embedded object be a new collection? If I am right, how do I go about displaying the returned string array (only just thought of this) in the template anway?
You are not initializing lines. Use:
var lines = [];
also, what is: string +count++ + '. '? did you mean string += count++ + '. '? If so then you also need to initialize string, e.g., var string = "";
From your comment, I get the sense that what you really want is to show the list reactively in the template. For that, you'd probably want to directly use a transform. Here is how that could work. Alternatively you could wrap your code into a Deps.autorun.
HTML (e.g., edit_event.html):
<template name="editEvent">
{{lines}}
</template>
Javascript (e.g., edit_event.js):
Template.editEvent.lines = function() {
var order = Orders.find(
{id: Session.get('currentOrder')},
{limit: 1,
transform: function(order) {
console.log(order);
var lines;
var count = 1;
_.each(order, function(item) {
var string = count++ + '. ';
if(item.age) { // we have a cloathing string
string += item.age + " " + item.sex + " "
+ item.Season + " " + "Number: " + item.Number;
lines.push(string);
}
else if(item.pushers) {
string += "Pushers needed: " + item.pushers;
lines.push(string);
}
else if(item.cots) {
string += "Cots needed: " + item.pushers;
lines.push(string);
}
else if(items.extra) {
string = "Extra info: " + item.extra;
lines.push(string);
}
else {
string = "Don't know this field";
lines.push(string);
}
console.log(lines);
});
return lines;
}
})
}

Categories

Resources