I'm building a simple WYSIWYG editor in React and I've implemented all of the different button functionalities except this last one: selecting text and turning it into a hyperlink.
I need to preface this with: I'm not interested in answers like "just use React-Quill" or "just use react-draft-wysiwyg".
So the type of feature I'm trying to implement can be seen on https://quilljs.com/ - scroll down until you see the demo editor, then select some text, click the "link" button, and a secondary window will pop up containing a text input that allows you to enter a URL. Click SAVE, and the originally selected text will turn into a hyperlink.
I'm approaching this like so:
Using document.execCommand('createLink', false, myURL) to handle the actual hyperlink creation
When my url entry window pops up, I save the currently selected range by saving off the result of document.getSelection().getRangeAt(0)
When the SAVE button is clicked, I restore the range by grabbing the document selection again, clearing all ranges with sel.removeAllRanges() and then calling sel.addRange(mySavedRange)
Note: all of this range saving and restoring is necessary, because when the user clicks on the text input on the pop-up window to enter a url, the document selection clears.
So here's what I'm seeing happen:
Instead of the selected text turning into a hyperlink (in this case I was selecting the word 'this',) the url is just thrown at the end of the line of text.
For reference, after trying my own code, I decided to try the functions implemented here: https://gist.github.com/dantaex/543e721be845c18d2f92652c0ebe06aa
I still see the same issue.
Any thoughts?
An extra note: I'm building an electron app, so I'm not as concerned with "cross-browser" compatibility. If it works in Chrome, then great!
My solution: the issue was that the contenteditable div needed to be focused.
So I changed my approach:
Upon clicking "Save", instead of using the document.execCommand right away, I would update a global state field, like enteredHyperlinkText (I'm using Reactn)
I added a ref to my editor with useRef and an effect to my editor with useEffect (I'm using React Hooks) the effect would only re-run when enteredHyperlinkText updates, and it will handle focusing on the editor, restoring the selection, and calling document.execCommand.
Note: focus the contenteditable div before restoring the selection.
Related
I am new to ReactJS and I am working on a webpage that has the regular posting and commenting functionalities.
Currently, we are using a ReactJS Rich-Text editor called React Quill for the comment-inputing text box.
With React Quill, it is possible to insert a string that works like a link, and on clicking the string, it essentially redirects to the linked url. It is a pretty standard behavior of the "link" toolbar button, and I am just using stackoverflow's rich-text editor to type this very question :)
However, instead of directing to the linked url, we want to have some custom the behaviors when the text is clicked, like triggering a method that opens a modal window on the webpage.
The important thing is: when user clicks the text, some custom defined Reactjs function is triggered. It would not be going to the linked url like as default behavior.
Is it possible with Quill?
Is it possible with some other Rich-Text Editor in ReactJS ?
Since I am typing my question about Rich-text editor using a Rich-text editor, I figured it is most clear if I just give this example:
So in the comment-inputing text box of our app, it is like when you have this text:
this_is_some_text
and on clicking it, instead of going to google.com, you open a modal window to show some message on this same page, or trigger an http request call, or any custom behavior.
I have included a simple homemade WYSIWYG editor into my HTML Javascript App by using a contentEditable DIV (let's call this EDITDIV) and a series of edit buttons in another DIV. Although far from brilliant it works for me and my users.
My problem is whenever I need to fix the text in EDITDIV which I do by simply setting contentEditable=false, I still want to allow the app users to be able select some of their previously editable text in EDITDIV and perform not edit operations on it, e.g. to use it as a search string.
To do this the users need to be able to select some of the text in EDITDIV and then click outside of EDITDIV. At this point the selection is no more. My question is how do I save the selection to use later on. I could do this every time the selection changes, or at the point when the focus on EDITDIV is lost. I tried both techniques. I cannot find an event (e.g. onselect, onselectionchange) that worked reliably for the first method and for the second method, setting contentEditable to false stops the focus, so the onblur event never happens.
Help much appreciated.
I am in the process of creating a chrome extension that allows you to click on context menu options when you are in an editable element. Clicking a context menu option automatically places some text where the cursor is.
The problem I am having is that the process for placing the text differs based on where the text is being placed. For example, If the text is being placed in a textarea, (like the one I am typing in now), the process is different than if I need to put the text in, say, a YouTube comment box, which is its own custom div and does not support the operations that I would use when editing the contents of a normal textarea
In my search for a flexible solution that would work the same for all elements that are in the editable category of the chrome.contextMenus API, I thought of the following Idea:
I should be able to do what I want if I store my variable in the system clipboard with document.execCommand('copy') and then paste it in wherever the cursor is with document.execCommand('paste')
The downside here is that the user would loose whatever they used to have in their clipboard.
I originally planned on just pasting the original contents into my own textarea, then restoring it once I am done with the clipboard, but there are 2 problems with this approach:
The user would loose whatever formatting they had originally
This would only work for text, not images.
Is there a way that I can save the contents of the clipboard that would allow me to copy them back to the clipboard at later time without the user noticing any modification to the content?
If I have any glaring misconceptions, please correct me as I am new to
JS, the DOM and HTML
I am making an extension that stores the selected text from the currently active webpage into localstorage, then when the user click on this selected text in my extension's popup, the extension will fire chrome.tabs.create and open the website where the selected text was selected. These functions work, but I don't know how to trigger the 'find' function when the new tab opens. I want the newly created tab to highly the selected text that my extension stored. I think there are two ways to do this...
somehow trigger the 'find' function that the browser by default has. The one with 'Ctrl+F" or 'Command+F" triggers and insert the selected text in there
edit the HTML of the newly created page by highlighting the selected text.
new_source = { "url" : tab[0].url, "title" : tab[0].title, "quote" : selectedQuote, "id" : idSource};
sources.push(new_source);
localStorage["sources"] = JSON.stringify(sources);
This is how I'm storing my information
You can't trigger Chrome's native find dialog, but you can invoke window.find(). The main differences between the native dialog and find() are
find() only highlights one of the matches in the page, whereas the
native dialog highlights all. To be precise, find() will begin by
highlighting the match nearest to the top of the document, and
repeated invocations will move it down the page.
find() will highlight the selected text in with the default blue color, whereas Chrome's find dialog highlights its matches in orange. However, this can be mimicked by modifying the background CSS property of the ::selection pseudo-class.
Depending on your use case this might be sufficient.
However, if you want to highlight a specific quote on the page, and need to account for possible duplicates of that quote, then it's a bit more tricky, and I'm not sure it can be done perfectly. You'll want to get the precise location of the selected text using window.getSelection(), find a way to identify its startNode and endNode across page reloads (if they have ids, this is easy, but if not you'll have to resort to hacks), and then when the page is reopened, use Selection.addRange() to restore it.
I want to be able to have default text like "Enter content here..." appear when the editor first loads, but I want this text to disappear when the user clicks/focuses on the content area. Then, if they "blur" (move out) of the content area without inputting anything, I want the "default text" to re-appear in the editor.
After Googling and looking through TinyMCE's wiki, it looks like there are onActivate and onDeactivate events that should partially do this; however, the wiki page for onDeactivate has a disclaimer stating that it is not a true "blur" method, plus I was not able to get the onActivate events to work (using FF 3.5 at least).
Has anyone else found a solution to this? I'm using a stock TinyMCE install and have jQuery loaded for my other JS tasks for the site I'm building, so I'm open to some jQuery wizardry to make this happen if there's nothing available in the TinyMCE API.
Thanks,
Seth
the onNodeChange tinyMCE event will fire if the user tabs into the editor. use tinyMCE's onMouseDown to detect a click. between these two events you should be able to determine when the user has activated the editor. use $(body).click() in the main page to determine when the user clicks out of the editor and blurs it.
i would also shy away from putting the default text as the actual value of the editor. instead, i would make the iframe/body of the editor be transparent and put the default value behind it in an absolutely positioned div. using the above triggers, just show()/hide() that div when you want the default value to [dis]appear.
Hmmm, tricky one...
here's an idea you might like to try:
We have established the onActivate works fine, so hook up the code for that... now, for onDeactivate...
tinyMCE stores it's content in the original (now hidden) textarea it replaces. That's how the content gets sent to the server when the form is posted.
Now, to blur away from the editor, a user has to click on something else on the page. using jQuery you can attach a $("body").click() function that checks the content of the hidden textarea (using $(id_of_hidden_textarea).val()). If the content is empty, set the content to "Enter content here..." in both the textarea (using val()) and the MCE instance (using tinyMCE.setContent()).
The $("body").click() function would not fire when clicking on the editor because it's in an iframe.