Is there a flexible way to modify the contents of an editable element?
Your problem consists of two subproblems:
- Identify the target element of the contextmenu action.
- 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 withframeUrl
(or use a combination of the next methods). - Assuming that you have already inserted a content script with a
chrome.runtime.onMessage
listener, usechrome.tabs.sendMessage
to send a message to a specific frame identified byframeId
(since Chrome 41). - Use the
chrome.webNavigation.getAllFrames
method to get a list of all frames in a tab for a giventabId
, then get theframeId
of the target frame by filtering the list of frames by a knownframeUrl
. - (in the future (Chrome 42?),
contextMenus.onClicked
will get theframeId
).
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).
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
Calculate Position of Selected Text JavaScript/Jquery
Prevent Select2 from Flipping the Dropdown Upward
Customize Ng-Repeat in Angularjs for Every Nth Element
How to Force Jquery to Center an Element When It Is Dragged to and Snapped to Another Container
How to Get Horizontal Scrollbars at Top and Bottom of a Div
Access the -Webkit- Vendor Prefix in JavaScript
How to Workaround: IE6 Does Not Support CSS "Attribute" Selectors
Show a Bootstrap Modal If Option Is Selected
How to Remove the Parent Element Using Plain JavaScript
Specify Parent Divs Opacity But Make It Not Affect Children HTML Elements
Serving High Res Images to Retina Display
How to Customize Ant.Design Styles
Javascript: Hiding and Showing Div Tag with a Toggle Button
Can't Figure Out Why Page Loads at Bottom? Angular Ui-Router Autoscroll Issue