vue3 setup - how to access ref element in click event - javascript

I use vue3 composition API and <script setup> .
<textarea v-model="textarea"></textarea>
<button #click="showPreview">click</button>
<p ref="p" v-if="open"></p>
const textarea = ref("");
const p = ref(null);
const open = ref(false);
function showPreview() {
let text = textarea.value.replaceAll(/\n/g, "<br>");
if (p.value) { // p is null
p.value.innerHTML = text;
}
open.value = true;
}
I want to show textarea's text in p tag(not using v-html) when click button.
But p is null in function...
I tried check p variable in onMounted function but it still null.
onMounted(() => console.dir(p))
How can I access ref element when click event?
Thanks in advance for any help.

Use v-show so that the element is in the DOM even when not displayed. With v-if, the <p> element is not rendered when open is false, so the ref is null.
<p ref="p" v-show="open"></p>
However, you should consider using a ref to store the text to display instead of working with the actual HTML elements. You can use the <pre> element to preserve whitespace without needing to replace linebreaks with <br>.
<template>
<textarea v-model="textarea"></textarea>
<button #click="showPreview">click</button>
<pre v-if="result">{{result}}</pre>
</template>
<script setup>
import {ref} from 'vue';
const textarea = ref("");
const result = ref("");
function showPreview() {
result.value = textarea.value;
}
</script>

One way to display "raw" HTML is to use the v-html directive as per the Vue site.
For example:
<script setup>
import { ref } from 'vue'
const textarea = ref("");
const p = ref(null);
const open = ref(false);
const textToDisplay = ref ("");
const showPreview = () => {
textToDisplay.value = textarea.value.replaceAll(/\n/g, "<br>");
open.value = true;
}
</script>
<template>
<textarea v-model="textarea"></textarea>
<button #click="showPreview">click</button>
<p v-if="open"><span v-html="textToDisplay"></span></p>
</template>
But be sure to use this with care and to sanitize any data being displayed in this fashion as per this warning about Cross-Site Scripting:

Related

Why the error alert still displayed event the HTML elements are sanitized?

The below are my use case scenario.
Collect the user input.(the input must be HTML sting element)
Form the HTML element with some style.
After creating the HTML element, need to sanitize it and append it into the target DOM element.
The DOM element rendered the sanitized element. but the error alert stil displayed.
The user input is
<img src="http://url.to.file.which/not.exist" onerror="alert(document.cookie);">
This always shows the alert.
Can someone help me to resolve this?
function createEle() {
const wrapElement = document.createElement('div');
wrapElement.innerHTML = document.getElementById("html-input").value;
const html = showSanitizedHTML(wrapElement);
console.log(html.outerHTML)
document.getElementById("sanitized-html").innerHTML = html.innerHTML;
}
function showSanitizedHTML(value) {
const sanitizedHTML = DOMPurify.sanitize(value.outerHTML);
const tempWrapElement = document.createElement('div');
tempWrapElement.innerHTML = sanitizedHTML;
return tempWrapElement.firstElementChild;
}
<script src="https://cdn.jsdelivr.net/npm/dompurify#2.0.16/dist/purify.min.js"></script>
<textarea id="html-input"></textarea>
<button onclick="createEle()">Show Sanitized HTML</button>
<div id="sanitized-html"></div>
The script content executes on this statement
wrapElement.innerHTML = document.getElementById("html-input").value;
So here is how to fix it
function createEle() {
const wrapElement = document.createElement('div');
wrapElement.innerHTML = DOMPurify.sanitize(document.getElementById("html-input").value);
console.log(wrapElement.outerHTML)
document.getElementById("sanitized-html").innerHTML = wrapElement.innerHTML;
}
<script src="https://cdn.jsdelivr.net/npm/dompurify#2.0.16/dist/purify.min.js"></script>
<textarea id="html-input"></textarea>
<button onclick="createEle()">Show Sanitized HTML</button>
<div id="sanitized-html"></div>
or shorter, assuming you want to keep using the wrapper:
document.getElementById("sanitized-html").innerHTML = wrapElement.innerHTML;
This is the sequence of events:
You read the raw user input
You inject the raw user input into the DOM (wrapElement.innerHTML =)
You read the normalised user input back from the DOM
You pass the normalised user input though DOMPurify.sanitize
You inject the sanitized user input into the DOM
The alert fires due to step 2.
Because you're using unsanitized HTML here:
const wrapElement = document.createElement('div');
wrapElement.innerHTML = document.getElementById("html-input").value;
Don't jump through those hoops, just pass the text directly to DOMPurify:
function createEle() {
const element = showSanitizedHTML(document.getElementById("html-input").value);
// Your `showSanitizedHTML` is returning an **element**, not HTML,
// so you wouldn't use `innerHTML` to put it in the DOM.
const output = document.getElementById("sanitized-html");
output.innerHTML = "";
output.appendChild(element);
}
function showSanitizedHTML(html) {
const sanitizedHTML = DOMPurify.sanitize(html);
// Not sure what the purpose of this wrapper element is, but since
// it only uses sanitized HTML, I left it in place.
const tempWrapElement = document.createElement("div");
tempWrapElement.innerHTML = sanitizedHTML;
return tempWrapElement.firstElementChild;
}
<script src="https://cdn.jsdelivr.net/npm/dompurify#2.0.16/dist/purify.min.js"></script>
<textarea id="html-input"></textarea>
<button onclick="createEle()">Show Sanitized HTML</button>
<div id="sanitized-html"></div>
Or more simply:
function createEle() {
const html = showSanitizedHTML(document.getElementById("html-input").value);
document.getElementById("sanitized-html").innerHTML = html;
}
function showSanitizedHTML(html) {
const sanitizedHTML = DOMPurify.sanitize(html);
return sanitizedHTML;
}
<script src="https://cdn.jsdelivr.net/npm/dompurify#2.0.16/dist/purify.min.js"></script>
<textarea id="html-input"></textarea>
<button onclick="createEle()">Show Sanitized HTML</button>
<div id="sanitized-html"></div>
Or even more simply:
function createEle() {
document.getElementById("sanitized-html").innerHTML = DOMPurify.sanitize(
document.getElementById("html-input").value
);
}
<script src="https://cdn.jsdelivr.net/npm/dompurify#2.0.16/dist/purify.min.js"></script>
<textarea id="html-input"></textarea>
<button onclick="createEle()">Show Sanitized HTML</button>
<div id="sanitized-html"></div>

How can I "fake" a url to an html page from an html string?

On my React website, I'd like to generate an image from an html string.
I've found html-to-image which seems stable and popular, but it requires a URL to the page of which I want to take a screenshot of.
How can I generate a "fake" URL to give it?
The HTML string I have is basically a design of the image I want but the content depends on user input.
EDIT: (added the HTML string)
This is my HTML string:
<!DOCTYPE html> <html> <body> <div style="width:500px;margin:0px;padding:0px;background-color:white' \
';"><div> <div style="padding:50px"> <p ' \
'style="color:#9F9F9F;font-size:13px;font-family:verdana;margin-top:0px">VARIABLEVARIABLE</p> <p ' \
'style="color:#3F3F3F;font-size:26px;font-weight:900;margin-top:0px;margin-bottom:18px;font-family' \
':verdana">VARIABLEVARIABLE</p> <p style="color:#3F3F3F;font-size:15px;font-weight:500;margin-top:0px;font-family' \
':verdana">VARIABLEVARIABLE</p> </div> <div style="width:100%;height:10px;background-color:#EFF5F8;"></div><div ' \
'style="padding:50px"> <p style="color:#9F9F9F;font-size:13px;font-family:verdana">VARIABLEVARIABLE</p> <p ' \
'style="color:#3F3F3F;font-size:15px;font-weight:500;margin-top:0px;font-family:verdana">VARIABLEVARIABLE</p> ' \
'</div></div> <div style="width:100%;height:55px;background-color:#313131;margin-right:0px;margin-left' \
':0px;line-height:55px;"> <p style="color:#ffffff;font-size:15px;font-weight:500;font-family:verdana' \
';margin-left:50px">VARIABLEVARIABLE</p> </div> </div> </body> </html>
So I get data fom the server, then replace where I wrote VARIABLEVARIABLE with some of the data and I want to present it to the user as an image.
EDIT 2 :
Reading the comment from #programoholic I think that he's right and I got it wrong. I kept seeing dataUrl but I understand now that it's not an actual URL that needs to be passed, but just a reference to the node in which the HTML exists.
I'm still unsure how to approach this, but it does change the question.
I'll try to set the html to the node once I get the data and then take a snap of that, maybe that'll work.
html-to-string doesn't require any url. Instead you can use the useRef to select the node and then generate the html content as an image like below :
import React, { useRef, useCallback } from 'react';
import './style.css';
import { toPng } from 'html-to-image';
export default function App() {
const ref = useRef();
const onButtonClick = useCallback(() => {
if (ref.current === null) {
return;
}
toPng(ref.current, { cacheBust: true })
.then((dataUrl) => {
const link = document.createElement('a');
link.download = 'my-image-name.png';
link.href = dataUrl;
link.click();
})
.catch((err) => {
console.log(err);
});
}, [ref]);
return (
<div ref={ref}>
<h1>Hello StackBlitz!</h1>
<p>Start editing to see some magic happen :) p</p>
<button onClick={onButtonClick}> download png </button>
</div>
);
}
Here is the. : Stackblitz
html-to-image wants base64 url , so you can create image to base64 with javascript code.
const base64Convertor = (file) => {
return new Promise((resolve, reject) => {
const fileReader = new FileReader();
fileReader.readAsDataURL(file);
fileReader.onload = () => {
resolve(fileReader.result);
};
fileReader.onerror = (error) => {
reject(error);
};
});
};
You can add some JavaScript to replace the contents of some HTML elements with the value of your variable, which can be taken from a URL parameter.
Then pass the URL with the value in the parameter.
Eg: /generate-image.html?variable=Hello
// Get variable from URL query params
const urlSearchParams = new URLSearchParams(window.location.search);
const variable = urlSearchParams.get('variable') || 'default value';
// Update all HTML elements with data-variable
const elements = document.querySelectorAll('[data-variable]');
for (element of elements) {
element.textContent = variable;
}
<h1 data-variable="true"></h1>
<p data-variable="true"></p>

Switch Content of two Textareas using JavaScript

I have two textareas and I like to switch the content of these, so content of the first textarea shall be the content of the second textarea and vice versa. Following code just copy the content of the first textarea into the second, but the second step is not performed, so both textareas comprise the same content afterwards. No error occurs.
function switch_text_content(){
var src_text_memory = src_form.src_message.value;
var trgt_text_memory = trgt_form.trgt_message.value;
console.log(src_text_memory);
src_form.src_message.innerHTML = trgt_text_memory;
trgt_form.trgt_message.innerHTML = src_text_memory;
//switch_text_content2(trgt_text_memory);
}
You are doing in wrong way because you are using .innerHTML to set value instead you can to use .value property to set value of textarea. Like Below Example:
const switchBtn = document.querySelector('#switch-btn');
const firstTextarea = document.querySelector('#first-textarea');
const secondTextarea = document.querySelector('#second-textarea');
switchBtn.addEventListener('click', function() {
const firstContent = firstTextarea.value;
const secondContent = secondTextarea.value;
firstTextarea.value = secondContent;
secondTextarea.value = firstContent;
});
<textarea id="first-textarea">First Content</textarea>
<textarea id="second-textarea">Second Content</textarea>
<br/>
<button type="button" id="switch-btn">Switch</button>

Why isn't JavaScript displaying the text box value on page

I Want the code to get the number the user has entered and display it on the screen. I know that its just a dumb mistake that I have made.
function run() {
const quest = document.getElementById('quest');
const data = quest.value;
const element = document.createElement('div').innerHTML = data
const store = document.getElementById('store');
store.appendChild(element)
}
body {
text-align: center;
}
<h1>Calucator</h1>
<input type="number" name="" id="quest">
<button onclick="run()">=</button>
<div id="store"></div>
As the comments in your question pointed - document.createElement('div').innerHTML = data is not a document node element
And use .textContent instead of .innerHTML - otherwise you'll introduce an XSS vulnerability.
function run() {
const quest = document.getElementById("quest");
const data = quest.value;
const yourElement = document.createElement("div");
yourElement.textContent = data
const store = document.getElementById("store");
store.appendChild(yourElement);
}
What i changed in the function?
I first create the element, and then set it's text content. Finally i append it to the element you wanted.

How to add new line on JS append html

I have some node list, and I am trying to get some values from this list.
It works fine but I can't append the values in new lines and everything rendered together.
<div class="newinsert"></div>
<script>
const indiv = document.querySelector('.newinsert')
const flist = document.querySelectorAll('someclass')
const listClean = [...flist]
console.log(listClean);
listClean.forEach(list=> {
const html = `${list.innerHTML} `
indiv.append(html)
})
</script>
I tried adding <br> on html var but it just prints <br> with ""
\n doesn't work too
Thanks in advance.
EDIT: ok fixed it by
indiv.insertAdjacentHTML('beforeend', ${html} < br >)
append function receive string or HTMLNode element (more info)
but if your purpose is just to learn,you can simply replace InnerHtml content with your Html;
or concatenate it to the current content;
const indiv = document.querySelector('.newinsert')
const flist = document.querySelectorAll('someclass')
const listClean = [...flist]
console.log(listClean);
listClean.forEach(list=> {
const html = `${list.innerHTML}<br> `
indiv.innerHTML = html
//or
indiv.innerHTML = indiv.innerHTML+html // if you effectly want to append conent to current dev content
})
<div class="newinsert"></div>

Categories