Is There a Flexible Way to Modify the Contents of an Editable Element

Is there a flexible way to modify the contents of an editable element?

Your problem consists of two subproblems:

  1. Identify the target element of the contextmenu action.
  2. Insert a custom text fragment at the caret (remove any selection if it is present).

Subproblem 1: Identifying the target element

  • If crbug.com/39507 is resolved, then getting the element is easy. This feature request is almost 5 year old without any progress, so don't get your hopes high on this one.
    Alternative methods require you to break problem 1 in two more subproblems: Identifying the target frame, and then select the target DOM element.

There are several APIs that help with identifying the frame (use a combination of them, choose whichever combination fits best in your situation):

  • The contextMenus.onClicked event provides the tab's ID (tab.id) as a property in an instance of tabs.Tab and the frame's URL (frameUrl) in a separate object.
  • The chrome.tabs.executeScript method can directly run a script in a frame.

    (currently only the top-level frame or all frames, targetting a specific frame is work in progress - crbug.com/63979, planned for Chrome 42).

    Until targetting a specific frame is implemented, you could insert the content script in every frame and compare the URL with frameUrl (or use a combination of the next methods).
  • Assuming that you have already inserted a content script with a chrome.runtime.onMessage listener, use chrome.tabs.sendMessage to send a message to a specific frame identified by frameId (since Chrome 41).
  • Use the chrome.webNavigation.getAllFrames method to get a list of all frames in a tab for a given tabId, then get the frameId of the target frame by filtering the list of frames by a known frameUrl.
  • (in the future (Chrome 42?), contextMenus.onClicked will get the frameId).

Okay, assuming that you have the correct frame, you can simply use document.activeElement to get the target element, because input elements get focused upon click.

Subproblem 2: Inserting a text fragment at the caret

If the target element is a <textarea> or <input>, then you can simply use

// Assume: elem is an input or textarea element.
var YOURSTRING = 'whatever';
var start = elem.selectionStart;
var end = elem.selectionEnd;
elem.value = elem.value.slice(0, start) + YOURSTRING + elem.value.substr(end);
// Set cursor after selected text
elem.selectionStart = start + YOURSTRING.length;
elem.selectionEnd = elem.selectionStart;

Otherwise, you need to know whether there is a content editable element, and if so, remove any selection if existent, and finally put your desired text over there.

var elem = document.activeElement;
if (elem && elem.isContentEditable) {
// YOURSTRING as defined before
var newNode = document.createTextNode(YOURSTRING);

var sel = window.getSelection();
// Remove previous selection, if any.
sel.deleteFromDocument();
// If there is no range in the selection, add a new one, with the
// caret set to the end of the input element to avoid the next error:
//"Failed to execute 'getRangeAt' on 'Selection': 0 is not a valid index."
if (sel.rangeCount === 0) {
sel.addRange(document.createRange());
sel.getRangeAt(0).collapse(elem, 1);
}
// Insert new text
var range = sel.getRangeAt(0);
range.insertNode(newNode);
// Now, set the cursor to the end of your text node
sel.collapse(newNode, 1);
}

Relevant documentation for the web platform APIs used in the last example:

  • Selection
  • Selection.deleteFromDocument()
  • Selection.collapse
  • Range
  • Range.insertNode

How to wrap long lines in contenteditable div with flex

You need to allow the .right to shrink, so if you change flex: 1 0 auto to flex-grow: 1 (or flex: 1 1 auto) the text will wrap

*{margin: 0; padding: 0;}#app {  height: 300px;  display: flex;  max-width: 100%;}.left {  min-width: 200px;  height: 100%;  background: #B1D27C;}.right {  flex-grow: 1;  height: 100%;  background: #ccc;  display: flex;  flex-direction: column;}.msg {  height: 100px;  background: #4E8DF4;}.editor-wrap {  flex: 1;}.editor {  background: #FECF45;  height: 100%;  width: 100%;  word-wrap: break-word;}
<div id="app">  <div class="left"></div>  <div class="right">    <div class="msg"></div>    <div class="editor-wrap">      <div class="editor"How can I wrap long lines here?  contenteditable="true">How can I wrap long lines here? How can I wrap long lines here? How can I wrap long lines here? How can I wrap long lines here? How can I wrap long lines here? How can I wrap long lines here? </div>    </div>  </div></div>

Contenteditable height transition: animate after adding (shift+enter) and removing a line of text

To avoid these issues, I personally use a solution not based on pure CSS animations / transitions which I found always have problems. For example, in your CSS implementation, there is a bounce back effect if using the Enter too fast (you can slow the animation down to see it better).

Bounce back effect

Moreover, new lines handling is different between browsers, some will add <div><br></div>, some versions of IE add only <br>, etc.

I've never been able to fix all these problems or found an implementation fixing all of these so I decided to not modify at all the behavior of the contenteditable, let the browser do is magic which works and instead, react to what's happening.

We don't even have to worry about keys events like Shift + Enter or events like deletion, etc., all of these are natively handled by the navigator.

I choose instead to use 2 elements: one for the actual contenteditable and one for the styling of my contenteditable which will be the one having height animations / transitions based on the actual height of the contenteditable.

To do that, I'm monitoring every events that can change the height of a contenteditable and if the height of my styling element is not the same, I'm animating the styling element.

var kAnimationSpeed = 125;var kPadding = 10;
$('div[contenteditable]').on('blur keyup paste input', function() { var styleElement = $(this).prev();
var editorHeight = $(this).height(); var styleElementHeight = styleElement.height();
if (editorHeight !== styleElementHeight - kPadding * 2) { styleElement.stop().animate({ height: editorHeight + kPadding * 2 }, kAnimationSpeed); }});
.autogrowWrapper {  position: relative;}
.autogrow { border: 1px solid rgb(0, 0, 0); height: 40px; /* line-height + 2 * padding */}
div[contenteditable] { outline: none; line-height : 20px; position: absolute; top: 10px; /* padding */ left: 10px; /* padding */ right: 10px; /* padding */}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="autogrowWrapper"> <div class="autogrow"></div> <div contenteditable="true"></div></div>

HTML5 contenteditable - placing the cursor before a non editable display block element

Use these styles:

.tag {
display:inline-block;
width:100%;
background-color:red;
}

Editable 'Select' element

Nothing is impossible. Here's a solution that simply sets the value of a text input whenever the value of the <select> changes (rendering has been tested on Firefox and Google Chrome):

.select-editable {position:relative; background-color:white; border:solid grey 1px;  width:120px; height:18px;}
.select-editable select {position:absolute; top:0px; left:0px; font-size:14px; border:none; width:120px; margin:0;}
.select-editable input {position:absolute; top:0px; left:0px; width:100px; padding:1px; font-size:12px; border:none;}
.select-editable select:focus, .select-editable input:focus {outline:none;}
<div class="select-editable">
<select onchange="this.nextElementSibling.value=this.value">
<option value=""></option>
<option value="115x175 mm">115x175 mm</option>
<option value="120x160 mm">120x160 mm</option>
<option value="120x287 mm">120x287 mm</option>
</select>
<input type="text" name="format" value=""/>
</div>

jquery editable edit link?

I would guess that the best way to do this would be to trigger the click handlers on the div itself, since it seems to be clicking on the editable element that causes it to change and to receive focus:

$('#my_edit_button_id').click(function() {
$('.my_editable_div').click();
});
$('.my_editable_div').editable(/* my settings */);

NB that I'm not familiar with this plugin -- it's just a reasoned guess based on the docs.

Insert text into editable field from web extension

I would do it like this:

let code = 'document.activeElement.value = "apple";';
browser.tabs.executeScript(parent_tab_id, {"code": code});

By the way, window.close inside a background script is browser.tabs.remove(currentTabId). You can get the current tab id by querying the tabs API (example 2): https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/tabs/query#examples

Get contentEditable div height after typing a new char

Considering that the height of the div won't increase before that the char appear, I can suggest you my workaround. You can fill with the typed letters another div by intercepting the event, then get the hight of this second div and finally, you can place the letter where it should be appear. This increase the input time.

You can test the script here

<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
<style type="text/css">

#title {
border: 1px #3399FF solid;
min-height:50px;
width:100px;
word-wrap:break-word;
}
#title2 {
filter:alpha(opacity=20);
opacity:0.2;
border: 1px #3399FF solid;
min-height:50px;
width:100px;
word-wrap:break-word;
}
</style>
</head>
<body>

<!-- THE DIV CALLED title -->
<div
id="title"
class="title"
contenteditable="true"
style="font-family: Helvetica; font-size:18px; font-weight:bold; background-color:#fff; color:#000; -webkit-tap-highlight-color:rgba(0,0,0,0); margin:14px;"
autocapitalization="off"
autocorrection="off"
autocomplete="off"
></div>
<div
id="title2"
class="title"
contenteditable="true"
style="font-family: Helvetica; font-size:18px; font-weight:bold; background-color:#fff; color:#000; -webkit-tap-highlight-color:rgba(0,0,0,0); margin:14px;"
autocapitalization="off"
autocorrection="off"
autocomplete="off"
></div>
<div id="a"></div>
</body>
<script>

var slowl = 400 //updating time
var lastts = "";//last timestamp value
$("#title").on('keydown keyup change keypress', function(e) {//catch the event
e.preventDefault();//stop the event
if(e.timeStamp-lastts < slowl){//try prevent keypress abuse
return false;
}
lastts = e.timeStamp;
var html=$('#title').text();
console.log(e);
if(e.charCode!=0){//all browsers
char = e.charCode;
}else{
char = e.keyCode;
}
content = $('#title').text()+String.fromCharCode(char); //prepare content
$('#title2').text(content);
var height = $('#title').height();
$('#a').html(height);
setTimeout("$('#title').text($('#title2').text());",slowl);//normalize the sitation

});
</script>
</html>


Related Topics



Leave a reply



Submit