ContentEditor Div repositions caret while typing, in React - javascript

I'm having a react application, and building a Text editor using a div tag. in the div I've enabled content-editor True. and binding it with useState variable. That useState variable directly binded with text-editor Div tag with dangerouslySetInnerHTML. like follows.
const [content, setContent] = useState('')
function onChange(e) {
e.preventDefault();
const val = e.target.innerHTML;
setContent(val)
console.log(content);
}
<div contentEditable="true" onInput={onChange} className='Editor' dangerouslySetInnerHTML={content}/>
Here problem is while typing in the editor. caret moves first index (beginning). and adding values at first
like if " type 'apple',
but editor adding like 'elppa'
any solution you know please put in the answer
I've tried many things as chatGPT told but there is no solution working

After some time checking, I don't think you actually need to include dangerouslySetInnerHTML={content}.
Because for each letter you type, its innerHTML will be re-rendered with the letter you last typed, and for each re-render the caret will understandably reposition itself at the start.
Hope it makes sense for you.

Related

html-react-parser will not parse onChange function on select tag

I am attempting to dynamically create multiple select drop downs in a loop. The html-react-parser works fine when there is just pure html in it. However, when I add a onChange function to the select tag, it does not parse that part correctly. It actually seems to just disregard it.
let selectOptions = "<th>Details</th>";
for(var j=0;j<envsToCommitsHolder.length;j++){
selectOptions += "<th><select onChange={(event) => {handleOptionSelection(event.target.selectedOptions)}} id="+realRepoName[j+1]+"><option>"+realRepoName[j+1]+"</option>";
for(var h=0;h<envsToCommitsHolder[j].val.length;h++){
console.log(realRepoName[j+1]);
console.log(envsToCommitsHolder[j].val[h]);
//var value = envsToCommitsHolder[j].val[h];
selectOptions += "<option value="+h+">"+envsToCommitsHolder[j].val[h]+"</option>";
}
selectOptions += "</select></th>";
}
return parse(selectOptions);
Please see the attached image to see how it is being rendered. It puts the javascript outside of the select tag altogether. Is there some other way I should be doing this? I have attached the image of how the select with the onChange is being rendered on the browser ->
problematic part. I also saw there is a "dangerouslySetInnerHTML" to parse string and render html out of it, but I am not sure how to use that or if it will be useful in this case or not. Any help would be greatly appreciated. I am quite stuck with this one.
I have created a sandbox to render the select options dynamically, you can custom the rendering on your own, https://codesandbox.io/s/react-typescript-forked-8psy13?file=/src/App.tsx
Basic React knowledge: useState, useEffect
If y have a question, you can ask me here

React focus div after it has loaded

I'm currently experimenting with React, and I've now run into an issue that I can't seem to solve.
In my application, I use a React library to handle hotkeys, these hotkeys have a scope, so when I want a certain set of hotkeys to be active in a div, I have to wrap that div with a <HotKeys> tag.
I have to toggle some of these divs, so I'll have something along the lines of
isActive ?
<HotKeys ...>
<div ...>...</div>
</HotKeys>
: <div ...>...</div>
I now need to figure out a way to focus the div when it's created. Pretty much every article on the web suggest something like this:
const focusRef = useRef(null);
useEffect(() => {
focusRef.current.focus()
})
return (
isActive ?
<HotKeys ...>
<div ref={focusRef} tabIndex={-1} ...>...</div>
</HotKeys>
: <div ...>...</div>
)
I've tried some variations, including having the div top level (without the <HotKeys> wrapping them), all to no avail.
When I print the focusRef object in the useEffect method, I do get the expected output, focusRef is correctly set and current is populated, but calling the focus method doesn't work. At one point I tried calling the focus method from a button and manually triggering it after the component had fully loaded, it seemed to work (document.activeElement was being changed), then for some reason it stopped working again. All this leads me to believe that somehow the component hasn't fully loaded, despite calling the useEffect hook, which, if I understand correctly, triggers when the element has rendered for the first time/after every change to state.
I'd really appreciate some help with this, since I basically started learning React yesterday.
You must to use an useCallback , because useRef don't notifies you when ref was created
https://reactjs.org/docs/hooks-reference.html#useref
I think I figured it out.
You were right about using the tabIndex but you needed to pass it in as a string like this:
tabIndex={"-1"}
When you first load it a dotted line box surrounds the div that has the ref attached.
check out this code sandbox:
https://codesandbox.io/s/nifty-wildflower-eqjtk?file=/src/App.js
grabbed from this accepted answer where they pass in a string:
Need to put focus on div in react

Page Jumps on Component Re-Render (ReactJS/Gatsby)

Describe the Problem
I am making a very simple ReactJS/Gatsby website for someone and I am having an issue with one of my functional styled components when it re-renders. The problem is that it is causing the window to jump (scroll) after the re-render is complete.
The re-render is triggered by the user clicking on a span element (enclosed in an li element) which fires a function.
The list of li elements is determined by the state of the component. The overall parent component has a fixed height which is why I am having trouble diagnosing the issue.
What I Expect to Happen
The component to re-render and the window's scroll position to remain where it was when the user initiated it.
What Actually Happens
When the user clicks the element the page appears to jump (scroll). Sometimes it does so and remains in the new position, sometimes it does so and then returns to the original scroll position.
What I've Tried
I've tried following advice from other questions which suggest using event.preventDefault() and others which suggest moving the styling out of the component itself and, instead, opting for using classes.
Neither of these solutions worked.
I have managed to definitively find that the issue is due to setActiveTabs -- which causes the re-render of the ul element -- as logging window.scrollY both prior to it firing and after it completes displays a different value.
Edit 2:
I have managed to figure out that the issue is with making the list items targetable. It seems that either adding the tabIndex="0" attribute or making the li child an interactive element causes this bug.
Does anyone know a way around this?
Edit
The full frontend source code can be found in the following GitHub repo: https://github.com/MakingStuffs/resinfusion
In order to solve the issue I needed to prevent the clicked element from being targeted on the re-render. In order to do this I edited the clickHandler so that it uses element.blur() after setting the state.
The click handler is as follows:
const forwardClickHandler = event => {
setLoading(true)
const clickedSlug =
event.target.closest("button") !== null
? event.target.closest("button").getAttribute("data-slug")
: event.target.children[0].getAttribute("data-slug")
const categoryObject = getNeedle(clickedSlug, categories, "slug")
const subCatObject = getNeedle(clickedSlug, subCategories, "slug")
const serviceObject = getNeedle(clickedSlug, services, "slug")
const associatedChildren = getAssociatedChildren(
categoryObject
? categoryObject
: subCatObject
? subCatObject
: serviceObject
)
setBgImage(associatedChildren[0].thumb.localFile.childImageSharp.fluid)
setActiveTabs(associatedChildren)
event.target.blur()
return setTimeout(() => setLoading(false), 1000)
}

How do I apply css to text separated by commas inside of a textarea tag? (React.js Project)

Desired result:
I'm trying to highlight text separated by commas inside of a textarea tag (in a React project).
I really can't figure out how to accomplish this, yet every solution I've come across online uses jQuery (I really don't want to inject jQuery halfway through my project).
This is the function I currently have to trim the input values and split them:
handleTags = async (event) => {
let tags = event.target.value.toLowerCase().trim().replace(/[^\w,-]/g, '')
tags = tags.split(",")
await this.setState({post: {...this.state.post, tags: tags}})
}
And this is the JSX for the textarea within my render() {...} function:
<Card.Footer className="quickpost-tags-container">
<textarea
onChange={event => this.handleTags(event)}
className="postbox-tags-textarea"
placeholder="science, non-fiction, etc..."
contentEditable
suppressContentEditableWarning>
</textarea>
</Card.Footer>
I'd appreciate any help.
So I understand that this question was a bit meh, didn't make much sense and made me sound like a massive noob (it was like 4 in the morning). I was trying to get the tags that the user would put in to render the same as [refer to the image in the question]
I was able to conditionally render a span element above the textarea which would render each tag within a span element, dynamically. This achieved the desired result.
Hopefully, this will be helpful to someone looking to solve a similar problem.

Populating icons in Sencha Touch selectfield

I've got a pretty simple problem whose solution turns out not to be that simple at all.
I want to add images in front of each option of a selectfield. To be more accurate, I want to add images to the picker it triggers, and also to the selectfield's current value.
For the sake of simplicity, I'll create a little example:
Let's say, you want a user to choose between one of the four playing card suits Diamonds, Hearts, Spades and Clubs. To support visual recognition, you want to prepend the corresponding symbol to each suit name, so it could look something like this:
My first choice of a sencha touch component, that enables selecting from a given set of options naturally was selectfield. Unfortunately, it only seems to be able to display pure text, and nothing more. After digging into the sencha touch sources, I finally came up with half a solution. Basically, I pass the selectfield a custom defaultPhonePickerConfig, in which the corresponding picker(that is used by the selectfield to display the options) gets assigned a custom itemTpl. The itemTpl does the rest, namely adding some html to display the image:
defaultPhonePickerConfig: {
listeners: {
initialize: function() {
var slots = this.query('pickerslot');
Ext.each(slots, function(slot) {
slot.setItemTpl('<img src="someImage.jpg"> {text}');
});
},
change: function() {
// reconstruct the selectfield's change handler,
// since it gets overwritten
var iconSelect = Ext.Viewport.query('#IconSelect')[0];
iconSelect.onPickerChange.apply(iconSelect, arguments);
}
}
}
A working fiddle for this solution can be found here.
My solution isn't that bad, but there's a slight cosmetical problem, that's just not acceptable to me: The icons are only displayed in the picker (lower part of the screenshot above), but not the selectfield itself (upper, grayed out part) when the option was selected. And there seems to be no good way to add an icon to the selectfield's current value aswell.
And that's the main concern of my question: What good way is there to add an icon to both the picker's options and also to the selecfield's current value? Do I maybe just have to add relatively little code to my existing solution or should I take a whole nother approach?
Every contribution is appreciated. Thank you!
Update:
After some hacking around, I found a way (an ugly one) to prepend an icon to the selectfield itself. It is mostly based on brutal HTML DOM manipulation: I now also define event handlers for the selectfield itself (change and painted). On initialization and every time the value is changed, my handlers search the generated DOM for the underlying <input> and mess around with it. (That bad boy is probably the reason why we can't assign HTML in the first place, since the framework changes its value attribute. And value on the other hand can only contain plain text.)
This is how I define the selectfield's listeners:
listeners: {
change: function () {
var pickerDOM = document.querySelector('#' + this.getId() + ' input[name="picker"]');
PickerIcons.app.prependIconToSelectfield(arguments[1], pickerDOM);
},
painted: function () {
// Initialize an icon on creation
var pickerDOM = document.querySelector('#' + this.getId() + ' input[name="picker"]');
PickerIcons.app.prependIconToSelectfield(this.getValue(), pickerDOM);
}
}
The corresponding function prependIconToSelectfield() just defines some CSS:
prependIconToSelectfield: function (optValue, domElement) {
var iconUrl = this.getIconUrl(optValue);
domElement.style.backgroundImage = 'url(' + iconUrl + ')';
domElement.style.backgroundSize = '20px 20px';
domElement.style.backgroundRepeat = 'no-repeat';
domElement.style.backgroundPosition = 'left center';
domElement.style.paddingLeft = '30px';
}
Check out this fiddle for a working example.
This is still no good solution to me, since doing this kind of hackish DOM manipulation is way too rogue for my taste. I don't know what the side effects of actively messing around in the DOM could be in bigger projects, and don't want to learn it the hard way. So, I'm still looking for a cleaner solution.
First kudos on working so hard sencha touch is extremely hard to manipulate when you try to do something out of the box. Having said that let me try & propose a solution for what you want.
A selectfield in sencha has the following DOM tag structure.
div.x-field-select
div.x-field-input
input.x-input-el
div.x-clear-icon
div.x-field-mask
Now concentrate on the x-clear-icon it is normally hidden since a selectfield does not need a clear button. First write a css class for it to show it(display: block). This would display it with an X button similar to text field & it will be positioned towards the right corner. You can through css position it to the left and on change of the select field you can change its background to what you want. It is not a very straight forward solution but i have tried it for a similar problem & it works. Judging from what you have done above i think you can do it. All the best.
Hope my solution helps.

Categories

Resources