Blazor, get Input value from Javascript created DOM Element - javascript

I have been working with Blazor quite a bit (https://blazorboilerplate.com) but I have a bit of a issue that has stumped me tonight with adding some custom D3 code to my blazor pages. The D3 / Javascript code creates several DOM input elements and I wish to retrieve the values of these created elements so I can save a DTO to my database with those values. How can I do this and what is the most efficient way? Should I just create a JSInterop method to return the input values?
domInput.attr("#ref", function (d3) {return d3.key});
I tried creating "#ref" attributes so I could use the ElementReference but D3 errors when I try to append an attribute that begins with '#'

After some more research from Mr. Magoo's comment you cannot interact with DOM that was / is created by JS and / or modified by JS. To get around this though you can create a JS function to return your data. So I created a helper method that returns a JSON string of my data. Then I call that JS from my Blazor code and use Newtonsoft to Deserialize it. The d3 code could easily be changed to vanilla javascript or JQuery to get the DOM elements value / innerHTML.
Blazor Code
var data = await JsRuntime.InvokeAsync<string>("JsInteropFunc", null);
dataDto = Newtonsoft.Json.JsonConvert.DeserializeObject<DataObjectDto>
(data);
Javascript Code, this case uses d3 to get the DOM Element with a class name to get the text form the DOM element:
window.JsInteropFunc = function() {
function cleanStr(data){
return data.replace("$","").replace(",","").replace("%","");
}
return '{ totalSales: "' + cleanStr(d3.select(".totalSales").text()) + '"' +
', annualSales: "' + cleanStr(d3.select(".annualSales").text()) + '"' +
', profitMargin: "' + cleanStr(d3.select(".profitMargin").text()) + '"' +
'}' ;
},

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.

Correct way to convert your JavaScript function into a string so it can be inserted into innerHTML

This is what I am doing: I am building a fun in house API Voting System. I am using a client side snippet insert onto page
Like this:
<script src="domain.com/api/scripts/main.js"></script>
<div id="content-wrap" id="ac1e435e-c564-48f8-9f45-338616e7a789"></div>
Now in my main .JS I do all ajax request and modify the #content-wrap with creating new elements and inserting additional JS required to run Voting System.
However big issue I am experiencing is when I write JavaScript that I need to insert into #content-wrap I am currently writing it like this:
script.innerHTML = "$(someting).on('click', funciton(){"
+ "$.ajax({type: 'post',"
+ " url: '" + base + "/api/request', data: $('form').serialize(), "
+ "success: function(response){";
As you can see that can cause lot of issues as I build on it.
What is better way to accomplish this or is there a way i can just write my script / code and do something like this.
script.innerHTML = ConvertToString(script.js) OR ConvertToString(function X);
ConvertToString is just an expression I am using to explain what I would like to do instead of what I am doing.
Thank you, I am open to any suggestions.
I also must do this in plain JavaScript or with jQuery library so any suggestions to use VueJs, AngularJS or React will be considered as future references.
Thank you again
Additional explanation:
I would like to insert into my script element JavaScript snippet. But my snippet is about 30 lines long currently and might get bigger with time so it is very difficult to code with all the + " code " on every line that I write so that it can be inserted with innerHTML into element and executed on Client end.
So I would instead like to do something like this
element.innerHTML = mysnippetcode // but with out using + "" on each line like shown above
OR
element.append(snippet)
I hope this makes it little more clear
Solution that worked for me was using back ticks to wrap my sinppet and insert it into innerHTML of the element..
Just use the function's name without the () to convert it to a string:
function foo() {
var a = 10;
var b = 20;
var c = a + b;
return c;
}
document.write(foo);
The document.write will result in this string:
function foo() { var a = 10; var b = 20; var c = a + b; return c; }
If you only want the function's body, then you could just normally remove the first and last characters of the string.
I am not entirely sure this is what you wanted, if not, please make yourself more clear.
Alternatively, you could do an eval([insert function code here]) and there would be no need to add the code to the innterHTML of the script, read up on that function if you haven't heard of it.
Or if you want to create a function from a string, you can use new Function([name] ,[function body string]) if you need arguments you have to sandwich them between the 2 parameters.
But my snippet is about 30 lines long currently and might get bigger with time > so it is very difficult to code with all the + " code " on every line that I
write
You can use template literals if you want multi-line strings in Javascript, you simply have to replace your quotes with backticks.
See this MDN page if you are interested, or even this StackOverflow answer.

AngularJS making ng-bind-html to a dynamically constructed object name

I need to set the within the HTML code a binding to a dynamically created name, something like:
<div ng-bind-html="MyVariable_{{counter}}">
and in the controller I'm using the following code:
var the_string = 'MyVariable_' + p ;
var MyHTML = '<font size="' + p + '">This is text with size depending on the index</font>' ;
var dummy = $parse(the_string);
dummy.assign($scope, $sce.trustAsHtml(MyHTML));
Clarification Note:{{counter}} within the HTML is the parameter "p" passed to the javascript code.
The problem appears to be within the HTML... AngularJS does not like the syntax I'm using within the HTML (i.e. ="MyVariable_{{counter}}"). Is there any way to accomplish this?
Thanks.
Use a function instead
ng-bind-html="getHtml(counter)"
And your javascript would look like this
function getHtml(counter) {
return $scope['MyVariable_' + counter];
}

Convert php's htmlspecialchars() with javascript

I have a variable written from PHP into my javascript (using json_encode), that looks a little like this:
mappoints[x]['about'] = 'Administrator: Foo Barlt;br />Telephone: 555-4202<br />Email: bert#hotmail.com<br />Website: www.domain.com'
That I am using for a google maps map point. Using the json_encode seems to be very picky about what characters I can and cannot enter into it, so i am wondering how I can convert those special html characters into real html characters using javascript?
update
The way i am building my variable is:
var description = "<h3 style='margin: 0; padding: 0;'>" + mappoints[x]['name'] + "</h3><b>Director:</b> " + mappoints[x]['director'] + "<br/>" + mappoints[x]['about'];
The HTML in the varaible is all fine, until I add the about index. No function I have attached or tried yet seems to give me proper HTML.
You can use the dom to decode those entities for you.
mappoints[x]['about'] = 'Administrator: Foo Barlt;br />Telephone: 555-4202<br />Email: bert#hotmail.com<br />Website: www.domain.com'
mappoints[x]['about'] = $('<div/>').append(mappoints[x]['about']).text();
http://jsfiddle.net/5FTCX/
Basically when you add the html to the dom it will show the entities as the characters they represent, using .text() you can receive the data back as you'd see it in the browser as text, not html with the entities. If you want back the html you can use .html() e.g..
Would it be okay with :
return mystring.replace(/&/g, "&").replace(/>/g, ">").replace(/</g, "<").replace(/"/g, """);
From here : Convert special characters to HTML in Javascript
Just because #Musa's idea was great but needed some re-interpreting on my side, I wish to post a quick function here, that will handle htmlspecialchars great, based on #Musa's design :
function htmlspecialchars_decode(string) {
$('body').append("<span style='display:none;visibility:hidden;' id='tempText'>" + string + "</span>");
var result = $('#tempText').text();
$('#tempText').remove();
return result;
};
try this
decodeURIComponent(str) or `unescape(str)` or `decodeURI(str)`

JQuery - Fastest possible way of appending list items to list

I have some jquery that works fine, but I'd like to highly optimize it. Basically I'm
doing standard appending list items to unordered lists. Can anyone recommend the fastest
way to optimise the following code e.g. createDocumentFragment ?
for (key in data) {
li = $('<li><span class="item">' + data[key]["Name"] + '</span><img src=' + options.deleteIcon + ' alt="remove" class="delete"/></li>');
$('.item', li).data('ID', data[key]["Id"]);
$(list).append(li);
}
var sb = new Array();
for (key in data) {
sb.push('<li><span class="item" id="', data[key]['Id'], '">', data[key]["Name"], '</span><img src=', options.deleteIcon, ' alt="remove" class="delete"/></li>')}
$(list).append(sb.join(""));
I would suggest reducing the number of writes to the DOM to just one. By that, I mean storing the list into a temporary variable and then appending the entire list in a single operation. Also, instead of using .attr to set the ID of each element, you can use concatenation as you have used it to set the text of each LI.
var tmpList = '';
for (key in data) {
li = '<li><span class="item" id="' + data[key]['Id'] + '">' + data[key]["Name"] + '</span><img src=' + options.deleteIcon + ' alt="remove" class="delete"/></li>';
tmpList += li;
}
// if you are appending to an existing list, use append
// if you have just built one up from scratch, just use `.html`
$(list).append(tmpList);
I would recommend reading this:
Optimizing JavaScript For Execution Speed
From the article:
Unlike other programming languages,
JavaScript manipulates web pages
through a relatively sluggish API, the
DOM. Interacting with the DOM is
almost always more expensive than
straight computations. After choosing
the right algorithm and data structure
and refactoring, your next
consideration should be minimizing DOM
interaction and I/O operations.
For a start, you could make one large chunk of li elements and then append them in a single operation instead of appending them one at time.
Thanks everyone, the one point everyone seems to miss is that I'm associating the Id with the DOM element ... using the jquery.data method. This isn't to be confused the the ID attribute for the span element. Because of this I'm not sure how the concatenation would work as I understand that I need to have a reference to the DOM element to set the data on it. Is this correct?

Categories