Add any text in any position within a text entry!

It’s as usual the great @NigelG who has the solution. In the original post, his code is much easier to insert into a ‘Run javascript’ step from the ‘Toolbox’ plugin. I added a multiline input with the id ‘textarea’ and used this code in the Run Javascript step (written by Nigel) :

function insertAtCaret(areaId, text) {
	var txtarea = document.getElementById(areaId);
	if (!txtarea) { return; }

	var scrollPos = txtarea.scrollTop;
	var strPos = 0;
	var br = ((txtarea.selectionStart || txtarea.selectionStart == '0') ?
		"ff" : (document.selection ? "ie" : false ) );
	if (br == "ie") {
		txtarea.focus();
		var range = document.selection.createRange();
		range.moveStart ('character', -txtarea.value.length);
		strPos = range.text.length;
	} else if (br == "ff") {
		strPos = txtarea.selectionStart;
	}

	var front = (txtarea.value).substring(0, strPos);
	var back = (txtarea.value).substring(strPos, txtarea.value.length);
	txtarea.value = front + text + back;
	strPos = strPos + text.length;
	if (br == "ie") {
		txtarea.focus();
		var ieRange = document.selection.createRange();
		ieRange.moveStart ('character', -txtarea.value.length);
		ieRange.moveStart ('character', strPos);
		ieRange.moveEnd ('character', 0);
		ieRange.select();
	} else if (br == "ff") {
		txtarea.selectionStart = strPos;
		txtarea.selectionEnd = strPos;
		txtarea.focus();
	}

	txtarea.scrollTop = scrollPos;
	}

insertAtCaret('textarea', "foo");

Hello all! I have been looking for a long time for a solution to this issue. Found a solution.
Install this plugin

2 Likes

@richardosborne14, thanks for providing this! This is the only way I’ve been able to make this work.

Unfortunately, I haven’t been able to make it work with Bubble’s Rich Text Editor, though. :frowning: Any chance you or @NigelG know a trick to make that work?

Did you get this to work in the RTE, @eLPDev? I’m trying to add styled merge tags in the editor that you can click to delete like so:

1 Like

Hi @sydney22,

Have you succeeded in making your example working?

If so, I would like to know how :slight_smile:

Hi, @bart. To answer your question- kinda. I got the functionality down pat, but styling the text in the RTE will have to be done with a Quill extension. Here’s what I did in the interim:

  1. Download the Insert at cursor plugin
  2. Draw your buttons on the editor
  3. Create a click button workflow w/ the Insert at cursor action
  4. Set the Attribute ID on the input field
  5. For my text placeholder, I used double braces {{your_text}}
  6. Lastly, I did a :findandreplace on the text input to find the {{your_text}} and replace with database data

Hope that helps!

1 Like

Hi,

it is a pleasure to contribute, after all what all forum-active Bubblers gave.

A possible solution to inject a desired text at the cursor position in the Bubble’s Rich Text Editor (RTE), is to leverage on the magical Toolbox Plugin.

In particular, in our specific case we needed to allow our Users to tag / mention other User in the comments, which we absolutely needed to be RTE.



The process from the User point of view is:

  1. In the comment section of the App, the User add “@” in the RTE
  2. A popup shows up where the User can select the User to tag
  3. The tagged-User’s name is added in the RTE with a specific style (blue and underlined for ex.) - naturally, it is injected where the cursor was before.


Mirroring this logic and implementing it in the App, we have divided the process the same way as it is structured the from User point of view point:

  1. On the page where there is the RTE, the below “First Part JS” adds a listener through the Toolbox Plugin so that, when the user add “@” to the RTE:
    A) the opening of the popup got triggered - for ex. in the below “First Part JS” we have used a button as a bridge $(‘#ButtonMentionPeople’).click();
    B) the anchorNodeID referencing the “@” the User has added in the RTE, is passed to a JavascriptToBubble element, in order to be used in the “Second Part JS below”

  2. When the ButtonMentionPeople is clicked, the popup shows up with the RG of other Users inside the App. This button can also be made 0x0px in order to don’t influence the App UI.

  3. In the popup, when a User from the RG get clicked, the “Second Part JS” injects the tagged-User’s name thanks to the 2 piece of information that are passed to this JS as input: the tagged-User’s name, and the anchorNodeID that the “First Part JS” has created.




UI ELEMENTS AND WORKFLOW ACTION TO ADD TO THE APP

A) The Rich Text Editor to inject the desired string into - NB: important to put the ID Attribute used in the “Second Part JS”

B) The “JavascriptToBubble” element to place in the Frontend Design UI of the App

C) The “Run Javascript” Action to add in the Workflow section for the “First Part JS” as a listener - for ex. in a “When the page is loaded…” WF

D) The Button to place in the Frontend Design UI of the App (in this ex., the clicked element is even a text, the important thing is to put its ID Attribute = ButtonMentionPeople, or whatever id you use in the “First Part JS”)

E) The Popup with the RG User, so the Current User can choose the User to tag

F) The “Run Javascript” Action to add in the Workflow section for the “Second Part JS” triggered when the User select the User to tag from the RG inside the Popup




FIRST PART JS

$(window).keydown(function(e) {
    if (e.key === '@') {
        e.preventDefault();   // the "@" here is blocked before it is written down, it will be added just couple of lines below

        var richTextEditor = document.getElementById('CommentRichText');
        var selection = window.getSelection();
        var anchorNode = selection.anchorNode;

        if (anchorNode && richTextEditor.contains(anchorNode)) {

            // Compose the string the user has inject (in this case "@") with a timestamp in millisecond as id
            var timestamp = new Date().getTime();
            var stringToInsert = '<span style="color: blue; text-decoration: underline;" id="' + timestamp + '">@</span>'; 

            // Create HTML element from markup
            var parser = new DOMParser();
            var doc = parser.parseFromString(stringToInsert, 'text/html');
            var newNode = doc.body.firstChild;
        
            // Inject the element in the Rich Text Editor (in particular, where the user have cursor in that moment)
            var range = selection.getRangeAt(0);
            range.deleteContents();
            var startContainer = range.startContainer;
            var startOffset = range.startOffset;
            startContainer.splitText(startOffset);
            startContainer.parentNode.insertBefore(newNode, startContainer.nextSibling);
            range.selectNodeContents(newNode.nextSibling);

            // Save the ID in order to utilize it in the Second Part of the Process
            var anchorNodeId = timestamp.toString();
        }

        console.log(anchorNodeId);

        bubble_fn_outputAnchorID(anchorNodeId);

        $('#ButtonMentionPeople').click();   // trigger the click of the button that opens up the popup with the RG Users inside it
    }
});

SECOND PART JS

var anchorNodeId = properties.param1;   // parameter 1 passed to the second WF Action (see screenshot E above)
var userName = properties.param2;   // parameter 2 passed to the second WF Action (see screenshot E above)

if (anchorNodeId) {

    console.log("anchorNodeId found");

    var richTextEditor = document.getElementById('CommentRichText');   // Attribute ID of your Rich Text Editor 
    var anchorNode = document.getElementById(anchorNodeId); // Select the desired element through the ID

    if (anchorNode) {

        console.log("anchorNode element found");

        // Compose the string to inject and style it as you prefere (in this case it is styled as blue and underlined)
        var stringToInsert = '<span style="color: blue; text-decoration: underline;">@' + userName + '</span>';  

        // Create HTML element from markup
        var parser = new DOMParser();
        var doc = parser.parseFromString(stringToInsert, 'text/html');
        var newNode = doc.body.firstChild;

        // Replace the element injected in the First Part of the Process with the new desired element (in this case "@UserName")
        anchorNode.replaceWith(newNode);

        // Update the Selection
        var selection = window.getSelection();
        var range = document.createRange();
        range.selectNodeContents(newNode.nextSibling);

        // Add a space in the node after the injected one (so, when the user keep writing after the mention, the style of the text should be the one as before the injection)
        newNode.nextSibling.textContent = ' ' + newNode.nextSibling.textContent;

        // Set the cursor in the node after the injected one, in particular after the just-added space after the injected string
        range.setStart(newNode.nextSibling, 1);
        range.setEnd(newNode.nextSibling, 1);

        // Update the selection
        selection.removeAllRanges();
        selection.addRange(range);       
    }
}
1 Like

Thank you! I get a bug from the first javascript. Can you share the editor? Thank you!

Hi hoke,

unfortunally it is a business app and i cannot share the editor, what kind of bug are you getting?