Context: I'm working on a tampermonkey script to enrich some GWT pages witnin a 3rd party app, no access to sourcecode nor to servers
Problem: I need to extract the value of a textarea element using plain JavaScript (see above as to why)
HTML as I see now: inside a series of table>tr>td>div>div>div
<textarea class="gwt-TextArea" style="height: 400px; width: 600px;" id="MyTarget"></textarea>
issue:
the html of the textarea does NOT contain any text, what I see must be something else
as expected document.getElementById("MyTarget").value==""
Value is the property you want and should work without any problems.
Here is a quick example showing how to use it.
document.getElementById('MyTarget').addEventListener("keyup", function() {
let value = document.getElementById('MyTarget').value;
document.getElementById('MyOutput').innerHTML = value;
})
<textarea class="gwt-TextArea" style="height: 100px; width: 200px;" id="MyTarget" onkeyup="window.keyup"></textarea>
<div id="MyOutput">
</div>
Related
I'm making a javascript code editor for users on my site. One of the features I built was a custom console.
Users can write console.log in their code and the logged string gets appended to a div on the page doing something like this:
function toConsole(str) {
var myconsole = document.getElementById("console-text");
var message = document.createElement("span");
message.append(str);
myconsole.append(message);
}
str is set to whatever string the user inputs into console.log. Can appending this string run malicious code on my page? (The .append() jQuery api page says 'yes' but I can't seem to get it to interpret anything I write as html)
If so how can I prevent this and how can I test to make sure it's safe?
You can use text() to insert the content as a textNode, which will cause it not to be rendered by the page as markup, but as plain text.
var stringContainingHtmlAndJavascript = '<div><b>This will be bold</b></div>';
$(document.body).text(stringContainingHtmlAndJavascript);
$(document.body).append(stringContainingHtmlAndJavascript);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
As Taplar said, there are different ways.
You can also add the HTML to a temporarily-tag and extract the text afterwards.
See my third example to do this.
$(function() {
var stringContainingHtmlAndJavascript = '<div><b>This will be bold</b></div>';
$('.test1').append(stringContainingHtmlAndJavascript);
$('.test2').text(stringContainingHtmlAndJavascript);
$('.test3').text($('<i/>').append(stringContainingHtmlAndJavascript).text());
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="test1"></div>
<div class="test2"></div>
<div class="test3"></div>
I will suggest you .parseHTML() with a bemol, as stated in the documentation.
.parseHTML(), as the method name says, parses a string to interpret the HTML.
The third argument keepScripts is defaulted to false... Setting it to true would open the gates wide to scripts.
So it "normally removes" the script tags. If no HTML or text is found at all, it returns undefined (like demo case #3). So you problably will need to add an if condition to avoid appending the text "undefined".
So... In the demo below, I used your posted "script to append" quite as-is... I just added the HTML parsing method.
IMPORTANT, case #1 to #4 are safe... But #5 is a breach. If there is an inline on[event] attribute in the parsed HTML, it will go through the "script filter" and may execute.
$(".console_ok").on("click",function(){
toConsole( $(this).prev(".console_input").val() );
});
function toConsole(str) {
str = $.parseHTML(str)[0];
var myconsole = document.getElementById("console-text");
var message = document.createElement("span");
message.append(str);
myconsole.append(message);
}
input{
width: 60em;
}
#console-text{
height:8em;
width:20em;
background-color: #bbb;
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
console.log test #1:<input class="console_input" value="Just text">
<button class="console_ok">OK</button><br>
<br>
console.log test #2:<input class="console_input" value="<h1>Some HTML</h1>">
<button class="console_ok">OK</button><br>
<br>
console.log test #3:<input class="console_input" value="<script>alert('A script!!!');</script>">
<button class="console_ok">OK</button><br>
<br>
console.log test #4:<input class="console_input" value="<div style='height:20px;background-color:red;'><script>alert('A script!!!');</script>And some <b>bold</b> text...</div>">
<button class="console_ok">OK</button><br>
<br>
console.log test #5:<input class="console_input" value="<img src='invalid-path' onerror='alert(`JS EXECUTES HERE!!!`);'>">
<button class="console_ok">OK</button><br>
<br>
My console:<br>
<div id="console-text"></div>
(Please run in ful page mode)
CodePen
You will notice the [0] after $.parseHTML(str)... It's to get the DOM element from the jQuery object, as your function is plain JS. Your function could be written like this too (does the exact same thing):
function toConsole(str) {
str = $.parseHTML(str);
var myconsole = $("#console-text");
var message = $("<span>");
message.append(str);
myconsole.append(message);
}
jquery.append() and jquery.html() are not secure against XSS attack Unless you sanitize the data when you want to display the that to the user.
function encodeHTML(s) {
return s.replace(/&/g, '&').replace(/</g, '<').replace(/"/g, '"');}
but jquery.text() is safe because it does not render the html code and brings it in raw.
I had already searched multiple site but all of them is using plugin,
I want to make a input tag to do multi select
which is this, without using third party plugin is that possible Just using pure javascript or some JQUERY in case is impossible to write that function.
Here is something to get you going:
function addTag(tag) {
if(tag.value==undefined) return;
var tags=document.getElementById("tags");
var span=document.createElement("span");
span.textContent=tag.value;
tag.value="";
span.style.backgroundColor="whitesmoke";
span.style.margin="2px";
span.style.padding="2px";
span.setAttribute("onclick", "this.remove()");
tags.append(span);
}
<div id="line" style="border:1px solid blue;padding:2px;">
<span id="tags"></span>
<input id="tag" onchange="addTag(this)">
</div>
This code will add anything entered in input as a tag in tags.
Clicking the tag will remove it.
I'm looking into creating a text editor for my site and really liked how Google Keep does their text input. At first look based on the HTML, they don't appear use input fields / text areas but rather some sort of javascript mode of input that takes text input and generates the HTML equivalent that text and places it in the DOM. So is it likely that they built their own input functionality to allow them and the user to manipulate the content they put in? Or is it more likely that they have an all purpose input field or something that captures the data and it's just hidden from view?
This is all I see when I go looking into the DevTools
<div contenteditable="true" aria-multiline="true" role="textbox" class="notranslate IZ65Hb-YPqjbf h1U9Be-YPqjbf" tabindex="0" spellcheck="true" dir="ltr">
This is their "input field"
<br>
That renders html
<br>
Based on the text that's entered
<br>
But I want to know how I should be capturing this text
</div>
The contenteditable="true" and role="textbox" tell the DOM to treat the <div> element like a <textbox>. According to the WAI-ARIA Standards, this approach allows those with reading impairments to navigate the screen more easily, as a screen reader would have a better idea of what elements are on the page.
Without seeing the rendered DOM, I can't be 100% sure what Google is doing, but it is very easy to manipulate the DOM based on user input as such. In the following example, I'm using targeting the input element, and then creating an onkeyup function that writes the content in a secondary 'output' <div>. This second <div> mirrors the input in the example, though can be coded to send the input to another page (or database) with AJAX, included elsewhere on the page, or styled to format the input in a nicer fashion.
// Initial text
document.getElementById('output').innerHTML = document.getElementsByClassName('h1U9Be-YPqjbf')[0].innerHTML
// On change
document.getElementsByClassName('h1U9Be-YPqjbf')[0].onkeyup = function() {
document.getElementById('output').innerHTML = document.getElementsByClassName('h1U9Be-YPqjbf')[0].innerHTML
}
.h1U9Be-YPqjbf {
border: 1px solid blue;
}
#output {
margin-top: 20px;
border: 1px solid red;
}
<div contenteditable="true" aria-multiline="true" role="textbox" class="notranslate IZ65Hb-YPqjbf h1U9Be-YPqjbf" tabindex="0" spellcheck="true" dir="ltr">
This is their "input field"
<br> That renders html
<br> Based on the text that's entered
<br> But I want to know how I should be capturing this text
</div>
<div id="output"></div>
Hope this helps! :)
I thinks behind the scenes they uses Firebase three way binding.
You can achieve the result with Angular and Firebase. Both are free to get started.
More here link
I have a text with an input field. I want the field to start as blank, and when clicked upon, set the input's text to its correct value (saved in the "name" field, for instance).
If I do it this way, it works fine:
Buy <input type="text" name="eggs" onclick="this.value=this.name;"> tomorrow.
However, if I try to clean the DOM and move the function to a separate javascript file, it stops working:
HTML:
Buy <input type="text" name="eggs" onclick="showname(this);"> tomorrow.
JS:
function showname(el) {
el.value = el.name;
}
function showname(el){
el.value = el.name;
}
.closeform{
width: 70px;
}
.closeform input {
width: 70px;
}
.closeform button {
width: 70px;
}
Buy
<span class="closeform">
<input type="text" name="eggs" onclick="showname(this);">
</span>
tomorrow.
I'm very new to Javascript - what am I missing here?
You say in your question:
However, if I try to clean the DOM and move the function to a separate javascript file, it stops working
Let's say you have 2 actual files in the same folder:
myscript.js contents:
function showname(el) { el.value = el.name; }
index.html contents:
<!DOCTYPE html>
<html><head><title>Demo</title>
<script src="myscript.js"></script>
</head><body>
Buy <input type="text" name="eggs" onclick="showname(this);"> tomorrow.
</body></html>
OR
<!DOCTYPE html>
<html><head><title>Demo</title>
</head><body>
Buy <input type="text" name="eggs" onclick="showname(this);"> tomorrow.
<script src="myscript.js"></script>
</body></html>
That should work perfectly...
However, in the comments you say:
I tried it with Fiddle - maybe the problem is in Fiddle interface.
That is where your problem was....
There is no separate javascript-file in jsfiddle.
The three code-blocks (html, js, css) get merged into one file.
Right-click the result-window in jsfiddle and look at the generated file.
Then notice the options (top right corner) from jsfiddle: by default the code is wrapped in an onload-method (suiting to the library you selected or window.onload if you are not using a library).
You can however place the script in the head or body, thereby not wrapping your code inside a function's scope (which then closes over the containing identifiers).
See http://jsfiddle.net/wf55a5qb/ for a working example.
The reason your example stack-snippet worked here on StackOverflow is that it's snippet-editor does not wrap the javascript codeblock in a (onload-like) function (when it combines the three code-blocks).
Having said and explained this, I do encourage you to set your events (Using obj.addEventListener/obj.attachEvent or the direct elm.onevent) from the/a script once the elements (that your script manipulates, place script as last element of the html-body) or page (using window.onload/etc) has loaded.
I posted this to clear up what actually went wrong so you don't make false models in your head about how javascript works (like "an external script runs in it's own scope" which no-one claimed but might be an assumption you might make) whilst still learning it!
Everything in JavaScript has a scope. Where you are defining your function, it is not visible to the input so the input doesn't know that function even exists. You can use window to make the function visible to it:
<input type="text" name="eggs" onclick="window.showname(this);"/>
window.showname = function (el)
Fiddle
I don't recommend global functions though. So then what else?
You can use the onclick function in JavaScript. To find elements in JavaScript, you use selectors. I'm using getElementById() this will get an element by it's id. A list of selectors are here
<input id="my_input" type="text" name="eggs"/>
Then in JavaScript:
document.getElementById('my_input').onclick = function () {
//Use this to refer to the element
this.value = this.name;
};
Fiddle
When doing this. Make sure all your code is wrapped in a window.onload. This will make sure the code is run at the right time:
window.onload = function () {
//Your code
};
JSFiddle automatically puts your code in this.
I am trying to make use of this Syntax highlighter. I have tried to implement their example and I always seem to get this error in firebug:
place is not a function
else place(div);
Here is my code, I thought it was a path issue, but everything looks right:
<textarea id="code1" rows="20" cols="20">
select * from where this = done
</textarea>
<script type="text/javascript" src="codemirror/js/codemirror.js"></script>
<script type="text/javascript">
var editor = new CodeMirror('code1', {
height: "150px",
parserfile: "codemirror/contrib/sql/js/parsesql.js",
stylesheet: "css/sqlcolors.css",
textWrapping: true
});
</script>
If you look at the source code of that example page, its similar to mine yet, the text in the text area doesn't get highlighted and I always get that error.
Thanks all for any help
Change the code that initializes CodeMirror to the following and I think it will work:
var editor = CodeMirror.fromTextArea('code1', {
height: "150px",
parserfile: "codemirror/contrib/sql/js/parsesql.js",
path: "codemirror/js/",
stylesheet: "css/sqlcolors.css",
textWrapping: true
});
The important parts are using CodeMirror.fromTextArea rather than new CodeMirror and providing a value for path in the object passed into CodeMirror.fromTextArea.