Find the line containing the selected text within a JavaScript code

I am working on a contentEditable div where users can enter multi-line text. I need to be able to inspect the line that the user is currently typing in when they press enter. Is there a way to retrieve the context of that specific line (or all lines)?

Is it possible to use something like

window.getSelection().lineContent
for this purpose?

Currently, I am using

window.getSelection().anchorNode.textContent
, but this only retrieves the content of the current node, not the entire line. I anticipate that the user will press enter to move to the next line and I want to determine if the next line should be indented (my main goal is to identify whether there is a "tab" at the beginning of the line).

EDIT: Here is my current code:

document.getElementById('sampleeditor').addEventListener("keydown", fSubsTab );

function fSubsTab () {      
    e = window.event
    if ( false ) {
    } else if ( e.keyCode == 13 ) {
        e.preventDefault();
        if (!window.getSelection) return;
        sel = window.getSelection();
        node_offset = sel.anchorOffset
        node_text = sel.anchorNode.textContent

        // The problem is how would I get the content of the
        // current line between last line break and next one,
        // or until the end
    }
}

EDIT 2: This has been solved. Please see the answer below.

Answer №1

If I grasp the meaning of your inquiry correctly, you can achieve the desired outcome by combining:

  1. document.activeElement to retrieve the active text element
  2. document.activeElement.selectionStart
    to obtain the cursor position
  3. document.activeElement.value.split("\n")[line]
    for converting the cursor into the active line

document.addEventListener('keyup', (e) => {
  if (e.code === 'Enter' || e.code === 'NumpadEnter') {
    if (document.activeElement.type === 'textarea') {
      let start = $(document.activeElement).prop("selectionStart");
      let line = document.activeElement.value.substr(0, document.activeElement.selectionStart).split("\n").length - 2;
      console.log(`Enter key pressed on line ${line + 1}:`);
      console.log(document.activeElement.value.split("\n")[line]);
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<textarea rows='5'>
foo
bar
foobar
barfoo
</textarea>

Answer №2

After analyzing all the parameters and rules put forth in the initial query, it appears there are a number of potential challenges associated with making this function properly.

The primary concern arises from utilizing the contentEditable attribute as the method for editing HTML. Other solutions and examples suggest using elements like activeElement and selectionStart. While these alternatives seem viable, complications may arise when dealing with mixed content, including HTML that could modify the active element under certain circumstances. Initially considering the use of innerText or innerHTML, it became apparent that they too posed significant issues.

In the provided code snippet, using innerText effectively separates each line but fails to capture any embedded HTML code within the main element. On the other hand, utilizing innerHTML retrieves the HTML code successfully. However, due to the behavior of contentEditable, lines get fragmented by a mere <div> tag, which might also be nested inside the element, leading to conflicts.


Alternative Approach

While I am unsure about any restrictions on what can be implemented with the existing code, my suggestion would be to ditch the contentEditable attribute and instead load the element's contents into a <textarea> for editing. By incorporating the technique from 0stone0, you should be able to achieve the desired outcome.

The example below demonstrates how clicking on an element loads its contents into a <textarea>, thereby replacing the original content. Upon hitting the enter key within the <textarea>, it displays the line number and text from the preceding line. Furthermore, upon clicking outside the <textarea>, the script retrieves the new code and inserts it back into the parent element.

document.querySelector("#sampleeditor").addEventListener("click", function() {
  _EditEl(this);
  this.removeEventListener('click', arguments.callee);
});

function _EditEl(pEl) {
  let curContent = pEl.innerHTML;

  // Embed current content into textarea
  pEl.innerHTML = `<textarea id="tmpEdit" style="width: 100%; height: 4em;">${curContent}</textarea>`;
  document.querySelector("#tmpEdit").focus();//
  document.querySelector("#tmpEdit").addEventListener("keyup", function(e) {
    if(e.keyCode == 13) {
      let start = document.activeElement.selectionStart;
      let line = document.activeElement.value.substr(0, document.activeElement.selectionStart).split("\n").length - 2;
      console.log(`Pressed enter on line ${line + 1}:`);
      console.log(document.activeElement.value.split("\n")[line]);
    }
  });
  
  document.querySelector("#tmpEdit").addEventListener("blur", function(e) {
    let parentEl = this.parentElement;
    parentEl.innerHTML = this.value;
    parentEl.addEventListener("click", function() {
      _EditEl(this);
      this.removeEventListener('click', arguments.callee);
    });
  });
}
<p>Lorem Ipsum is simply dummy text of the printing and typesetting industry.</p>
<div id="sampleeditor">Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of <b><i>classical Latin literature</i></b> from 45 BC, making it over <span>2000 years old</span>.</div>
<p>The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested.</p>
<p>"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."</p>

Answer №3

By utilizing a custom function to create a unique identifier, embedding it into the text, and then removing it, I managed to solve this issue. Although the code may appear a bit cluttered due to the number of functions employed, here is an overview of the process:

// Custom function for replacing characters with or without case sensitivity
String.prototype.replaces = function(str, replace, incaseSensitive) {
    if(!incaseSensitive){
        return this.split(str).join(replace);
    } else { 
        // Replace this section with regex for better performance

        var strLower = this.toLowerCase();
        var findLower = String(str).toLowerCase();
        var strTemp = this.toString();

        var pos = strLower.length;

        

        while((pos = strLower.lastIndexOf(findLower, pos)) != -1){
            tcounter++
        
            strTemp = strTemp.substr(0, pos) + replace + strTemp.substr(pos + findLower.length);
            pos--;
            if (pos<0) {
                break
            }

        }
        return strTemp;
    }
};

// Function for generating a uuid to be used later on
function uuidv4() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
    });
}

// For handling line breaks from innerhtml and converting them to text with line breaks
function innerHTMLtoText ( inner ) {
        text = inner
        prelb = uuidv4() + uuidv4() + uuidv4() + uuidv4()
        
        prelb_list = [];

                    
         // Lines of code for modifying text
        
  
        
        return text
    }

// Main function: generates a uuid, inserts it at caret position, checks text of line, and removes the uuid 
document.getElementById('sampleeditor').addEventListener("keyup", function(e) {
    if(e.keyCode == 13) {
        texteditor = document.getElementById('sampleeditor')
  
        e.preventDefault();

       
            
        sel = window.getSelection();


   
    
        range = sel.getRangeAt(0);
        range.collapse(true);
            

            
    
        span.appendChild(document.createTextNode(' '));
        theSpanId = "span_" + uuidv4() + uuidv4() + uuidv4() + uuidv4()
       
        span.id = theSpanId


      
            
        changeText.innerHTML = idToRemove
    
    


        fullLine = linePart1 + linePart2

       

   
   

      

   

   

  
    

});

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The module cannot be located, despite my efforts to reinstall and verify all addresses, the error still persists

Error in showStudent component: Module not located: Unable to resolve '@material-ui/data-grid' in 'C:\Users\USER\Desktop\Database\client\src\components\showStudent' The code in the showstudent ...

Is the appearance of the Select Box altered when using Opera browser?

Here is the outcome I am hoping for. It displays correctly in Chrome, IE and FF: However, this is what I see when using Opera: Check out the demo site: Can anyone offer assistance? ...

Creating a dropdown menu utilizing JavaScript/jQuery arrays

Looking to create an HTML select menu using a JavaScript array where the keys are used as values for options. The challenge is when entering numbers as keys, they should be accepted in the code. a([selected="No of rooms", 1="1", 2="2", 3="3", 4="4", 5="5" ...

Is there a way to save a base64 image to an excel file?

I need assistance with exporting Excel Charts from NVd3 using Angularjs. Here is the code I have been trying: (jsfiddle) <button id="myButtonControlID">Export Table data into Excel</button> <div id="divTableDataHolder"> <table> ...

What is the best way to place content in a single div without it being divided into several separate boxes

Here is my code snippet: <div class="col-md-9"> <div id="statbox"> {% for obj in product_type %} {% for obj1 in vastu %} <script type="text/javascript"&g ...

Creating an array of logos in ReactJS with the help of TailwindCSS

After seeing multiple implementations of this feature, I find myself struggling to search for a solution. I can only access the HTML and CSS through inspecting elements, wondering if others are facing the same issue as me. Typically, we aim to implement t ...

Transforming a dynamic HTML layout into a non-responsive one: "RESPONSIVE NO MORE"

I'm currently working with an HTML document that has a responsive design. However, I now need to make it non-responsive. Can someone advise me on the most efficient and fastest way to do this? I attempted using min-width for both html and body, but ...

What could be the reason for an ASP.NET Core application not loading within an iframe on the same domain?

I am facing an issue with my ASP.NET Core MVC website, which is embedded as the src of an IFRAME within a portal. Both the portal and the .NETCore application share the same domain (e.g., site.portal.domain / portal.domain). Upon entering the portal, I en ...

Removing styling from a table header and specific table data cells with CSS

I am trying to create an HTML table where I only want to select cells that are not part of the thead section and do not have a specific class. I am having trouble with the selector for this particular case. table :not(thead):not(.cell-class) { backgro ...

Is there a way to implement jQuery.closest() using DOM manipulation or pure JavaScript?

Here is the HTML I am attempting to target. Given this HTML structure: <table class="non-unique-identifier table"> <tr><td><div id="unique-identifier"></div></td></tr> </table> I am trying to select #unique ...

Experience the magic of CSS Sprite once it's been successfully uploaded!

Visit the website for testing here Located at the bottom of the page are two images with hover effects - one labeled "Contact us" and the other "Jobs Available!" During local testing, these images are visible. However, once uploaded to a server, they dis ...

The performance of the Ionic app is significantly hindered by lagging issues both on Google

After starting to work with the ionic framework, I noticed a significant change in performance when testing an android app on Chrome for the first time. It was fast and responsive until I added a button that led to a screen with navigation bars, side men ...

After running the JavaScript function, the temporary display of textbox is initiated

I am trying to implement a feature where a textbox is shown or hidden when a user clicks on a filter icon in the header of a gridview. Initially, the textbox is hidden but when the icon is clicked, it briefly appears before disappearing again as if the pag ...

Transferring live data between AJAX-triggered pop-up windows

Utilizing modals frequently in my application is a common practice. There are instances where I need to transfer data from one modal box to another. For instance: Suppose there is a table listing different car manufacturers (Audi, BMW, Honda, etc). Each r ...

When using Angular2, I have found that I am unable to extract the body data from JSONP responses. However, I have discovered that this issue

Initially, I developed the SERVER using spring-boot framework. The code for this looks like: public class App { @RequestMapping("/") @ResponseBody String home(HttpServletRequest request) { String aa=request.getParameter("callback"); System.out.pri ...

Unleashing the power of conditional exports in package.json

Within my package.json, I define an exports section: "exports": { "import": "./dist/a.es.js", "require": "./dist/b.umd.js" }, However, during development, I wish to use different pa ...

Is it possible to trigger a directive using a function and then access the generated style?

My directive is designed to randomly select a color and assign it to a new user as an avatar. The random color generation and directive functionality are working as expected, but I'm looking to expand the functionality further and need some assistance ...

Continuous animation for a sequence of overlapping images with smooth transitions

Currently, I am in the process of developing a JavaScript script that will cycle through a series of images within the same <div>. The goal is to create a seamless animation effect with the image transitions. Although the images are cycling through, ...

The form validation feature in NestJS using Class Validator appears to be malfunctioning

Hey everyone, I've been working on validating incoming form data using the class validator package and DTO. Here's my DTO: import { IsString, IsPhoneNumber, IsEnum, MinLength } from 'class-validator'; export class CreateAgentDto { @ ...

The chaotic world of Android WebKit versions 2.x and 3.x

I have been working on an Android app and my goal is to make it compatible with Android versions 2.2 and above. Currently, due to issues with the WebView control, I am restricted to targeting Android 4.0 and higher. The app primarily uses HTML, CSS, Java ...