Please how can I dynamically add html element (like span) to elements in a react component. I want to implement something like this
let text1 = '"Text one sentence.';
let text2 = 'Text two sentence';
const textElement1 = document.querySelector(".text-1");
const textElement2 = document.querySelector(".text-2");
let textSpan;
function setText(t) {
t == text1 ? (textElement1.innerHTML = "") : (textElement2.innerHTML = "");
t.split("").map((x) => {
let charElement = document.createElement("span");
let charNode = document.createTextNode(x);
charElement.appendChild(charNode);
t == text1
? textElement1.appendChild(charElement)
: textElement2.appendChild(charElement);
});
textSpan = document.querySelectorAll("span");
}
function setFontWeight() {
textSpan.forEach((element) => {
let position = element.getBoundingClientRect();
// Calculate The Distance Between Cursor And Target Elements
let distance = Math.ceil(
Math.sqrt((position.x - pointerX) ** 2 + (position.y - pointerY) ** 2)
);
// The Longer The Distance The Lower The Font Weight
element.setAttribute(
"style",
`font-variation-settings: 'wght' ${900 - distance * 2};`
);
});
}
This is the react component I have created
function TopSidePane(){
return(
<div onMouseMove={(e)=>Hover(e)} className="top-side-pane">
<div className="text-container">
<h1 className="text-1">{text1}</h1>
<h1 className="text-2"> {text2}</h1>
</div>
<div className="custom-pointer" ></div>
</div>
);
}
Anytime there is a mouse movement on the component (if it get to the text inside the component) I want to be able to add span elements to the elements with the class names 'text-1' and 'text-2'. After I add the elements, I call the setFontWeight to make the weight of the hovered letters bold.
Please any help on how to implement this in react. I am new to react.
Related
So my issue here is quite simple but you don't have to understand the others codes just only the useEffect() parts..
My custom mousecursor text is doubling when I tried to hover the text
here is the lines of codes.
const cursorIntro = document.querySelector(".cursor");
const options = document.querySelector(".introduction .nav-options");
options.addEventListener("mousemove", function s(e) {
var rect = options.getBoundingClientRect();
var x = e.clientX - rect.left; //x position within the element.
var y = e.clientY - rect.top;
cursorIntro.style.left = x + "px";
cursorIntro.style.top = y + "px";
});
function OnSelect() {
const optionsSelection = document.querySelectorAll(".options");
optionsSelection.forEach((elem, i) => {
// console.log(elem.children[1].children[0].children[0])
elem.children[1].children[0].children[0].addEventListener(
"mouseleave",
() => {
cursorIntro.removeChild(cursorIntro.lastChild);
// cursorIntro.innerHTML = ""
}
);
elem.children[1].children[0].children[0].addEventListener(
"mouseenter",
() => {
// elem.children[1].children[0].children[0].classList.add('')
const createElement = document.createElement("h4");
createElement.innerText =
elem.children[1].children[0].children[0].dataset.name;
cursorIntro.appendChild(createElement);
}
);
});
}
OnSelect();
As you see I have a custom mousecursor on it and because that is where I want to append the text when it hover the text elements.
This is inside the useEffect() when I'm calling it...but one that I wasn't sure is that I only call back once the addEventListener per each.
The reason I used createElement because if I used innerHTML without using a createElement I can't add another some items because my plan here is to added something more in mousecursor
THIS IS THE CODEPEN
go to index.js and replace StrictMode to React.Fragment, in dev mode react re-renders twice
Hi I'm working on a drag and drop component website editor.
So I added a button when you click it it shown an input element and your input becomes the text of an paragraph element that's pushed to an array everytime you do it
My problem is that I want to make it so that when you click with the mouse the mouse x,y position becomes the left,top property of the paragraph element
But it sends me an error that says: Cannot read properties of undefined (reading 'left')
I already have the code that gets the mouse position so don't worry about it
Here's the code
//imports
import {mouse} from '/mouse.js'
//variables
let text_data_holder = document.getElementById('text-field')
let text_data = ''
//Creating text
let text_array = []
text_button.addEventListener('click', function() {
text_data_holder.style.display = 'block'
})
text_data_holder.addEventListener('change', function(e) {
e.preventDefault()
text_data += e.target.value
for(let i = 0; i < 1; i++){
text_array[i] = document.createElement('p')
.innerText = text_data
text_data_holder.after(text_array[i])
console.log(text_data)
text_array[i].left = mouse.x
}
})
I'd like to use pure JS to check if some String, the textareas .innerHTML = newContent below, contains some tag (h1in my case) at the beginning (=as first child). What would be the best way to do this?
Thanks!
function submitNewSectionContent(e) {
for (var i = 0; i < sections.length; i++)
let newHeading = document.getElementById('edit-title').value;
/* edit-title is text-input*/
let newContent = document.getElementById('edit-sectionText').innerHTML;
/* edit-sectionText is textarea */
if (newContent.indexOf('<h1>') > -1 && newContent.indexOf('<h1>') < 10) { /* <h1> is at beginning so replace with newHeading */
let toberemoved = newContent.match('<h1>.*<\/h1>');
newContent = newContent.replace(toberemoved[0], '').trim();
sections[i].innerHTML = '<h1>'+newHeading+'</h1>' + sections[i].innerHTML;
} else { /* newContent has no h1 as first child, so add h1 from newHeading */
sections[i].innerHTML = '<h1>'+newHeading+'</h1>' + newContent;
}
}
}
Problem with Regular expressions is they do not really work well with HTML. So Your best bet is to convert it to a DOM fragment and do the manipulations and convert it back. Only issue with this method really is you can lose formatting. There are libraries out there that can pretty print HTML.
function updateHeadline(txt) {
const ta = document.querySelector("textarea");
const data = ta.value; // read value, not innerHTML
const temp = document.createElement('div'); // temp div to hold html
temp.innerHTML = data; // set the html to the temp element
let firstChild = temp.firstElementChild // look at the dom
if (!firstChild || firstChild.tagName!=="H1") { // see if we have an h1
firstChild = document.createElement("h1") // if not create one
temp.prepend(firstChild) // add it to the front
}
firstChild.innerHTML = txt // set the new text of the h1
ta.value = temp.innerHTML // put the content back into the textarea
}
const btn = document.querySelector("button");
btn.addEventListener("click", function (e) {
e.preventDefault()
updateHeadline(document.querySelector("#text").value)
})
textarea {
width: 300px;
height: 200px;
}
<textarea>
<p>Some other text</p>
<p>Some more text</p>
</textarea>
<input value="foo" id="text"/>
<button>Set</button>
You could use regex like so, (updated based on comment)
if( /^\s*<h1>/gi.test(stringToTest) ) {
//logic here
}
It checks if the stringToTest begins with ^ tag
See here : https://regex101.com/r/vSo4sL/1
convert to dom, and parse dom
this portion of code makes it possible to treat a chain to retrieve titles placed in the H1 tag and (on the fly) treat the string of characters.
It's easily expandable for future processing : or tag processing or other ...!
commented code
<html>
<script>
var s="<H1>Hey Title</H1>\n Hello,\n other title <H1>Green!</H1>\n Ipsum dolore sit...";
console.log(s);
console.log("-------------------------");
var partialDoc = document.createElement( 'html' );
partialDoc.innerHTML = s;
var parsed='';
var titles=[];
treatment(partialDoc);
console.log("\n-------------------------\n");
console.log("parsed",parsed);
console.log("\n-------------------------\n");
console.log("s var contains "+titles.length+ " H1 tag");
console.log("titles "+titles);
function treatment(root) {
var child = root.firstChild;
while (child) {
// child.nodeName = H1 | H2 | P etc...
// child.nodeType = 1
// catch H1
if (child.nodeName=='H1') {
// append your title,
parsed+=" [H1 FOUND content= {"+child.innerText+"} H1])";
// or
// parsed+="<H1>"+child.innerText+"<H1>";
// add your own process here
// add this title in array
// or what you want...
titles.push(child.innerText);
// next part of document
child = child.nextSibling;
continue;
}
// capture other text than H1
if (child.nodeType==3) { // Node Type Text
parsed+=child.nodeValue;
}
if (child.nodeType==1) { // Node Type ELEMENT, : sub nodes...
treatment(child);
}
// continue the rest of doc
child = child.nextSibling;
}
}
</script>
</html>
One way you could do it is: Node.firstElementChild which will avoid giving child node as #text for white-spaces and Node.nodeName
let firstChild = document.getElementById('edit-sectionText').firstElementChild;
if(firstChild.nodeName === "H1"){
firstChild.innerHTML = "Replacement Value"
}
Note & Update: The earlier api that I had suggested Node.firstChild will not prevent white-spaces which gives #text node and comments as #comment node.
2nd Way: Node.children and picking the first child out of it should have a similar result to Node.firstElementChild.
let elem = document.getElementById('edit-sectionText');
if(elem){
let firstChild = elem.children[0];
}
Update based on comments: Using Dom Parser Interface
The interface allows to parse XML or HTML source from a string based on the mime type provided for its method parseFromString(string, mimeType)
It will give the top level #document node with parsed HTML from the string where if exists <h1> or <H1> at the beginning would be the first child of body and subsequently can be tested via tagName property.
Note: Takes care of preceding HTML comments and spaces at the beginning but a caveat is doesn't check fully closed tags ex: var s = \t <h1>I am a heading <h1> here the <h1> was never closed and in the result will two fully formed headings at the body with content : I am a heading and ""
let textAreaString = document.getElementById("edit-sectionText").value;
const domParser = new DOMParser();
const parsedDoc = domParser.parseFromString(textAreaString, "text/html");
if (parsedDoc.body.firstElementChild.tagName === "H1") {
//yes it starts with <h1> or <H1>
}
I am working on a javascript that act as a google translate do when you right click on a page and pick the translate to english option,
My problem is I am stuck on the algorithm phase that supposed to reach every text in the DOM of a site (not mine),
in some cases there are nested tags and inside there is a text that needs to be changed to another language
Really need your help to know how can I do it
var elements = document.querySelector('.ocms-container').querySelectorAll('*');
var elementsToArray= Array.from(elements);
let array = [...elementsToArray];
for (var i = 0; i < array.length; i++) {
console.log(array[i].textContent);
}
This is what I've tried already but I want to know where is every text that I am replacing needs to be replaced at
The recursive example below will find nodes containing text and with no children.
<html>
<head>
<title>Example</title>
</head>
<body>
<div>
text1
</div>
<div>
<p>text2</p>
<span>text3 span</span>
</div>
<div>
<div>
text4
</div>
<div>
<ul>
<li>
li1
</li>
<li>
li2
</li>
<li>
li3
</li>
</ul>
</div>
</div>
</body>
<script>
class DOMNodeToTranslate {
constructor(elName, elText) {
this.elName = elName;
this.elText = elText;
}
translate() {
// translation algorithm
}
// ...other methods
}
function findTextNodes(root) {
if (root.childNodes.length > 1) {
root.childNodes.forEach(node => {
findTextNodes(node);
let nodeText = node.textContent.replace(/(^(\s|\n|\t)+)|((\s|\n|\t)+$)/gm, '');
if (nodeText != '' && node.childNodes.length == 1 && node.nodeName != 'SCRIPT') {
let elObject = new DOMNodeToTranslate(node.nodeName, nodeText);
toTanslate.push(elObject);
}
});
}
}
let toTanslate = [];
let rootElement = document.querySelector('body');
findTextNodes(rootElement);
console.log(toTanslate);
</script>
You need to loop for each childNode starting from a root element, so the way you can do it by checking if current node has childNodes, now to get the text inside a node, you can check their nodeType, please check this out NodeType Documentation, this will help you to understand which kind of nodes the DOM have, so a working code to collect all text nodes could be:
const root = document.querySelector('body');
const queue = [root];
const textNodes = [];
const NODE_TEXT = 3;
while(queue.length) {
const node = queue.shift();
const children = node.childNodes;
if (children && children.length > 0) {
const childrenAsArray = Array.prototype.slice.call(children);
Array.prototype.push.apply(queue, childrenAsArray);
}
if (node.nodeType === NODE_TEXT) {
textNodes.push(node);
}
}
// finally you can console.log all the text nodes collected
console.log(textNodes);
If you run this code into any site in the Chrome console dev tools, you can have all text text node into the site.
Hope you can find it useful.
EDIT
in order to get the text value for each node you should first verify some rules before getting the text, so let' get started.
exclude the script, 'style', 'noscript' and code tags
check if the current node has one child node
check if the child node is a text node (NODE_TEXT = 3)
check if the child node is not empty
So let's start, by adding these rules
const root = document.querySelector('body');
const queue = [root];
const textNodes = [];
const NODE_TEXT = 3;
const exclude = ["SCRIPT", "STYLE", "CODE", "NOSCRIPT"]; // add exclude tags
// check if node is not the exclude one
function isNodeAllow(node){
return exclude.indexOf(node.tagName) === -1;
}
// define a helper function to check if has one child node
function hasOneChildNode(node){
return node.childNodes && node.childNodes.length === 1;
}
// check if node is not the exclude one
function isChildNodeText(node){
return node.childNodes[0].nodeType === NODE_TEXT;
}
// check if node is not the exclude one
function isNotEmpty(node){
return Boolean(node.innerText.trim());
}
while(queue.length) {
const node = queue.shift();
const children = node.children;
if (children && children.length > 0) {
const childrenAsArray = Array.prototype.slice.call(children);
Array.prototype.push.apply(queue, childrenAsArray);
}
if (isNodeAllow(node) && hasOneChildNode(node) && isChildNodeText(node) && isNotEmpty(node)) {
// here you can translate the text.
// node.innerText will return the text to be translated
textNodes.push(node);
}
}
// finally you can have all the collected text into the site
console.log(textNodes)
I hope it can help you.
The HTML code looks like this
<div id="txtarea" contenteditable="true">Some text</div>
I have to insert some new text based on some event at a particular position in the above div.
The event calls the function say updateDiv(txt, positon). For example it says
updateDiv("more ",5);
So the div should become be
<div id="txtarea" contenteditable="true">Some more text</div>
I tried a lot of javascript and jquery but nothing seem to work.
If the content of your editable <div> always consists of a single text node, this is relatively simple and you can use the code below.
var div = document.getElementById("txtarea");
var textNode = div.firstChild;
textNode.data = textNode.data.slice(0, 5) + "more " + textNode.data.slice(5);
Otherwise, you'll need to read about DOM Ranges (note that they are not supported in IE < 9) and use something like this answer to create a range corresponding to character indices within the content and then use insertNode().
var div = document.getElementById("txtarea");
var range = createRangeFromCharacterIndices(div, 5, 5);
range.insertNode(document.createTextNode("more "));
Here's how I did it:
var position = 5,
txt = "more ";
var current = $("#txtarea").html();
//alert(current.substring(0, position))
var endVal = current.substring(position);
var newValue = current.substring(0, position) + txt + endVal;
$("#txtarea").html(newValue);
jsfiddle displaying it 'in action'.
Edit: Updated the jsfiddle with the approach listed in a comment above to this post. Pretty slick!
use this function :
String.prototype.splice = function( position, newstring ) {
return (this.slice(0,position) + newstring + this.slice(position));
};
and use this function as :
var oldstr=$('#txtarea').html();
var newstr='more';
var position = 5;
$('#txtarea').html(oldstr.splice(position , newstr);