Last active
May 7, 2016 11:38
-
-
Save darobin/8a128f05106d0e02717b to your computer and use it in GitHub Desktop.
The Twitter Box
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div id="box" contentEditable="minimal"></div> | |
<script> | |
var box = document.getElementById("box") | |
, compoRange = null | |
, compoText = null | |
; | |
function insertText (txt) { | |
var sel = window.getSelection(); | |
if (!sel) return; | |
var range = sel.getRangeAt(0); | |
if (!range) return; | |
sel.deleteFromDocument(); | |
range.insertNode(document.createTextNode(txt)); | |
range.collapse(); | |
} | |
box.addEventListener("beforeinput", function (ev) { | |
insertText(ev.data); | |
}); | |
box.addEventListener("delete", function (ev) { | |
ev.data.deleteContents(); | |
}); | |
box.addEventListener("newline", function (ev) { | |
insertText("\n"); | |
}); | |
box.addEventListener("compositionstart", function (ev) { | |
var sel = window.getSelection(); | |
if (!sel) return; | |
var range = sel.getRangeAt(0); | |
if (!range) return; | |
sel.deleteFromDocument(); | |
compoText = document.createTextNode(ev.data); | |
range.insertNode(compoText); | |
range.collapse(); | |
compoRange = new Range(); | |
compoRange.setStart(compoText, 0); | |
compoRange.setEnd(compoText, compoText.length); | |
compoRange.style.textDecoration = "underline"; | |
}); | |
box.addEventListener("compositionupdate", function (ev) { | |
compoText.data = ev.data; | |
compoRange.setEnd(compoText, compoText.length); | |
}); | |
box.addEventListener("compositionend", function (ev) { | |
compoRange.detach(); | |
compoRange.deleteContents(); | |
insertText(ev.data); | |
}); | |
</script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- | |
Can we make it so that an empty one like this has by default a min-height of | |
line-height and is clickable everywhere for focus? Thanks. | |
--> | |
<div id="box" contentEditable="minimal"></div> | |
<script> | |
var box = document.getElementById("box") | |
, excessRange = null | |
, compoRange = null | |
, compoText = null | |
; | |
// Check that the text does not exceed 140 character, redden it otherwise | |
function checkExcess () { | |
// Range.detach() is used to remove a Range to which styling has been applied. | |
// Maybe it can be done automatically when the object goes out of scope, or through | |
// some other method. | |
if (excessRange) excessRange.detach(); | |
// brutally normalise box to a single Text child node | |
box.textContent = box.textContent; | |
var txtLen = box.textContent.length; | |
if (txtLen <= 140) return; | |
var tooMuch = txtLen - 140; | |
excessRange = new Range(); | |
excessRange.setStart(box.firstChild, txtLen - tooMuch); | |
excessRange.setEnd(box.firstChild, txtLen); | |
// We add a CSSStyleDeclaration on Range | |
excessRange.style.color = "#f00"; | |
excessRange.style.background = "#fe6a65"; | |
} | |
// Insert text at cursor | |
function insertText (txt) { | |
// It is quite possible that Selection.replace(node|text...) would be very nice here | |
var sel = window.getSelection(); | |
if (!sel) return; | |
var range = sel.getRangeAt(0); | |
if (!range) return; | |
// Delete whatever is selected, even if there are multiple ranges. | |
// We assume the UA prevents the selection from extending outside of the | |
// edit box while it has focus (this should be made a normative requirement). | |
sel.deleteFromDocument(); | |
range.insertNode(document.createTextNode(txt)); | |
// We need to explictly collapse because insertion extends the Range | |
range.collapse(); | |
// ISSUE: when you have a multi-range selection due to linear bidi selection being in | |
// effect, what happens when you call deleteFromDocument()? I am *presuming* that after | |
// that operation only one range remains, and it is a collapsed range positioned wherever | |
// the cursor should logically go based on the platform's behaviour. This is | |
// underspecified in Selection | |
checkExcess(); | |
} | |
// Assumptions for beforeinput: | |
// - the input event never gets triggered | |
// - pasting and dropping will trigger this, so we can ignore them | |
// ISSUE: but if something that isn't plain text is pasted/dropped we need to filter it so we | |
// can't ignore. At the same time, if we listen to it we get *both*. Should we just always | |
// filter here? Or modify the data in place in paste/drop without inserting? | |
box.addEventListener("beforeinput", function (ev) { | |
insertText(ev.data); | |
}); | |
// Assumptions for delete: | |
// - it is up to the platform to know specific conventions for delete backwards/forwards, | |
// delete word/line/paragraph/etc., knowing about the selection, and to map this correctly | |
// - the data on the event is a Range | |
box.addEventListener("delete", function (ev) { | |
ev.data.deleteContents(); | |
checkExcess(); | |
}); | |
// Assumptions for newline: | |
// - it is up to the platform to know specific conventions for the insertion of a newline, a | |
// new item, OK the control, or whatever and to map this correctly using modifiers | |
// - (I'm not sure the above is entirely possible though for this case. Still, it beats getting | |
// all the keyboard events…) | |
// We don't care about the details, we just insert a newline | |
box.addEventListener("newline", function (ev) { | |
insertText("\n"); | |
}); | |
// Assumptions for composition events: | |
// - you are guaranteed a compositionend event once you've seen compositionstart, and the order | |
// is deterministic. | |
box.addEventListener("compositionstart", function (ev) { | |
var sel = window.getSelection(); | |
if (!sel) return; | |
var range = sel.getRangeAt(0); | |
if (!range) return; | |
sel.deleteFromDocument(); | |
compoText = document.createTextNode(ev.data); | |
range.insertNode(compoText); | |
range.collapse(); | |
// the ongoing composition is rendered underlined | |
compoRange = new Range(); | |
compoRange.setStart(compoText, 0); | |
compoRange.setEnd(compoText, compoText.length); | |
compoRange.style.textDecoration = "underline"; | |
// ignore excess UI while composing | |
if (excessRange) excessRange.detach(); | |
}); | |
box.addEventListener("compositionupdate", function (ev) { | |
compoText.data = ev.data; | |
compoRange.setEnd(compoText, compoText.length); | |
}); | |
box.addEventListener("compositionend", function (ev) { | |
compoRange.detach(); // might not be needed given we're deleting contents anyway | |
compoRange.deleteContents(); | |
insertText(ev.data); | |
}); | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
i want to use your code for a project using a contenteditable like twiiter, but it does not work. can give me some solution .