Open Safari context menu BELOW text selection - javascript

I need to show a toolbar ABOVE a text selection in an editor. That's easy.
Unfortunately Safari on iOS seems to prefer opening its copy/paste/formatting context menu above text selections as well. But Facebook seems to have figured out a way to avoid this:
But after spending almost two hours with a remote debugger trying to figure out how the heck they achieved this, I'm giving up. I just can't figure it out.
I have built a very barebone prototype which displays a toolbar above a text selection (attached below). This works fine on my MacBook (using Chrome in this case):
But as the next screenshot demonstrates, Safari's context menu on an iPad conflicts with my toolbar. They open in almost the same position, making it impossible to interact with my toolbar.
I'm really keen to know how Facebook solved it. Any help would be greatly appreciated. Thank you :-)
Barebone prototype:
var toolbar = document.querySelector("div.Toolbar");
toolbar.style.display = "none";
document.onselectionchange = function()
{
var sel = getSelection();
if (sel.rangeCount > 0)
{
var ran = sel.getRangeAt(0);
if (ran.collapsed === false)
{
var rect = ran.getBoundingClientRect();
var rectTop = rect.top;
var rectCenter = rect.left + (rect.width / 2);
toolbar.style.display = "";
toolbar.style.left = (rectCenter - (toolbar.offsetWidth / 2)) + "px";
toolbar.style.top = (rectTop - toolbar.offsetHeight) + "px";
if (parseFloat(toolbar.style.top) < 0)
{
toolbar.style.top = "0px";
}
if (parseFloat(toolbar.style.left) < 0)
{
toolbar.style.left = "0px";
}
}
else
{
toolbar.style.display = "none";
}
}
else
{
toolbar.style.display = "none";
}
}
body
{
padding: 2em;
}
div.Toolbar
{
display: inline-block;
position: fixed;
background: orange;
box-shadow: 0px 0px 0.2em #333;
padding: 0.3em;
}
div[contenteditable="true"]
{
width: 600px;
height: 300px;
border: 1px solid gray;
padding: 0.5em;
}
<div class="Toolbar">
Buttons go here
</div>
<div contenteditable="true">
Try selecting some of this text in the editor. The toolbar shows up above the text selection as intended. But on an iPad we also get Safari's context menu which opens on top of our custom toolbar. Facebook has successfully solved this somehow - but how the heck did they do it?
</div>

I found the solution.
Quick answer: Add a "button" with an onclick or onmousedown handler.
Expanding on the solution:
I decided to go ahead with my project and simply attach the toolbar at the top of the viewport instead. But as the project progressed, I wanted to take a second shot at this problem - and this time the Context Menu did not overlap with my custom toolbar. It turned out that the mere presence of an element (<div> element representing a button in this case) with an onclick or onmousedown handler registered, was enough to make Safari realize that this is probably something where UI collisions should be avoided. Quite frankly I'm not a fan of this kind of "A.I", but at least there is an explaination now.
var button = document.createElement("div");
button.className = "ToolbarButton ToolbarButtonBold";
button.onmousedown = function() { /* ... */ }; // This saves the day
toolbar.appendChild(button);

Related

Dynamically change the height of the textarea element [duplicate]

There was another thread about this, which I've tried. But there is one problem: the textarea doesn't shrink if you delete the content. I can't find any way to shrink it to the correct size - the clientHeight value comes back as the full size of the textarea, not its contents.
The code from that page is below:
function FitToContent(id, maxHeight)
{
var text = id && id.style ? id : document.getElementById(id);
if ( !text )
return;
var adjustedHeight = text.clientHeight;
if ( !maxHeight || maxHeight > adjustedHeight )
{
adjustedHeight = Math.max(text.scrollHeight, adjustedHeight);
if ( maxHeight )
adjustedHeight = Math.min(maxHeight, adjustedHeight);
if ( adjustedHeight > text.clientHeight )
text.style.height = adjustedHeight + "px";
}
}
window.onload = function() {
document.getElementById("ta").onkeyup = function() {
FitToContent( this, 500 )
};
}
A COMPLETE YET SIMPLE SOLUTION
Updated 2022-08-30
(Added support for single row multiple textbox by default)
The following code will work:
On key input.
With pasted text (right click & ctrl+v).
With cut text (right click & ctrl+x).
With pre-loaded text.
With all textarea's (multiline textbox's) site wide.
With Firefox (v31-109 tested).
With Chrome (v37-108 tested).
With IE (v9-v11 tested).
With Edge (v14-v108 tested).
With IOS Safari.
With Android Browser.
With JavaScript strict mode.
OPTION 1 (With jQuery)
This option requires jQuery and has been tested and is working with 1.7.2 - 3.6.3
Simple (Add this jQuery code to your master script file and forget about it.)
$("textarea").each(function () {
this.setAttribute("style", "height:" + (this.scrollHeight) + "px;overflow-y:hidden;");
}).on("input", function () {
this.style.height = 0;
this.style.height = (this.scrollHeight) + "px";
});
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.6.3.min.js"></script>
<textarea placeholder="Type, paste, cut text here...">PRELOADED TEXT.
This JavaScript should now add better support for IOS browsers and Android browsers.</textarea>
<textarea placeholder="Type, paste, cut text here..."></textarea>
Test on jsfiddle
OPTION 2 (Pure JavaScript)
Simple (Add this JavaScript to your master script file and forget about it.)
const tx = document.getElementsByTagName("textarea");
for (let i = 0; i < tx.length; i++) {
tx[i].setAttribute("style", "height:" + (tx[i].scrollHeight) + "px;overflow-y:hidden;");
tx[i].addEventListener("input", OnInput, false);
}
function OnInput() {
this.style.height = 0;
this.style.height = (this.scrollHeight) + "px";
}
<textarea placeholder="Type, paste, cut text here...">PRELOADED TEXT. This JavaScript should now add better support for IOS browsers and Android browsers.</textarea>
<textarea placeholder="Type, paste, cut text here..."></textarea>
Test on jsfiddle
OPTION 3 (jQuery Extension)
Useful if you want to apply further chaining to the textareas, you want to be auto-sized.
jQuery.fn.extend({
autoHeight: function () {
function autoHeight_(element) {
return jQuery(element)
.css({ "height": 0, "overflow-y": "hidden" })
.height(element.scrollHeight);
}
return this.each(function() {
autoHeight_(this).on("input", function() {
autoHeight_(this);
});
});
}
});
Invoke with $("textarea").autoHeight()
UPDATING TEXTAREA VIA JAVASCRIPT
When injecting content into a textarea via JavaScript, append the following code to invoke the function in option 1.
$("textarea").trigger("input");
PRESET TEXTAREA HEIGHT
To fix the initial height of the textarea you will need to add another condition:
const txHeight = 16;
const tx = document.getElementsByTagName("textarea");
for (let i = 0; i < tx.length; i++) {
if (tx[i].value == '') {
tx[i].setAttribute("style", "height:" + txHeight + "px;overflow-y:hidden;");
} else {
tx[i].setAttribute("style", "height:" + (tx[i].scrollHeight) + "px;overflow-y:hidden;");
}
tx[i].addEventListener("input", OnInput, false);
}
function OnInput(e) {
this.style.height = 0;
this.style.height = (this.scrollHeight) + "px";
}
<textarea placeholder="Type, paste, cut text here...">PRELOADED TEXT. This JavaScript should now add better support for IOS browsers and Android browsers.</textarea>
<textarea placeholder="Type, paste, cut text here..."></textarea>
This works for me (Firefox 3.6/4.0 and Chrome 10/11):
var observe;
if (window.attachEvent) {
observe = function (element, event, handler) {
element.attachEvent('on'+event, handler);
};
}
else {
observe = function (element, event, handler) {
element.addEventListener(event, handler, false);
};
}
function init () {
var text = document.getElementById('text');
function resize () {
text.style.height = 'auto';
text.style.height = text.scrollHeight+'px';
}
/* 0-timeout to get the already changed text */
function delayedResize () {
window.setTimeout(resize, 0);
}
observe(text, 'change', resize);
observe(text, 'cut', delayedResize);
observe(text, 'paste', delayedResize);
observe(text, 'drop', delayedResize);
observe(text, 'keydown', delayedResize);
text.focus();
text.select();
resize();
}
textarea {
border: 0 none white;
overflow: hidden;
padding: 0;
outline: none;
background-color: #D0D0D0;
}
<body onload="init();">
<textarea rows="1" style="height:1em;" id="text"></textarea>
</body>
If you want try it on jsfiddle
It starts with a single line and grows only the exact amount necessary. It is ok for a single textarea, but I wanted to write something where I would have many many many such textareas (about as much as one would normally have lines in a large text document). In that case it is really slow. (In Firefox it's insanely slow.) So I really would like an approach that uses pure CSS. This would be possible with contenteditable, but I want it to be plaintext-only.
jQuery solution
adjust the css to match your requirements
css...
div#container textarea {
min-width: 270px;
width: 270px;
height: 22px;
line-height: 24px;
min-height: 22px;
overflow-y: hidden; /* fixes scrollbar flash - kudos to #brettjonesdev */
padding-top: 1.1em; /* fixes text jump on Enter keypress */
}
javascript...
// auto adjust the height of
$('#container').delegate( 'textarea', 'keydown', function (){
$(this).height( 0 );
$(this).height( this.scrollHeight );
});
$('#container').find( 'textarea' ).keydown();
OR alternative for jQuery 1.7+...
// auto adjust the height of
$('#container').on( 'keyup', 'textarea', function (){
$(this).height( 0 );
$(this).height( this.scrollHeight );
});
$('#container').find( 'textarea' ).keyup();
I've created a fiddle with the absolute minimum styling as a starting point for your experiments...
http://jsfiddle.net/53eAy/951/
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Textarea autoresize</title>
<style>
textarea {
overflow: hidden;
}
</style>
<script>
function resizeTextarea(ev) {
this.style.height = '24px';
this.style.height = this.scrollHeight + 12 + 'px';
}
var te = document.querySelector('textarea');
te.addEventListener('input', resizeTextarea);
</script>
</head>
<body>
<textarea></textarea>
</body>
</html>
Tested in Firefox 14 and Chromium 18. The numbers 24 and 12 are arbitrary, test to see what suits you best.
You could do without the style and script tags, but it becomes a bit messy imho (this is old style HTML+JS and is not encouraged).
<textarea style="overflow: hidden" onkeyup="this.style.height='24px'; this.style.height = this.scrollHeight + 12 + 'px';"></textarea>
Edit: modernized code. Changed onkeyup attribute to addEventListener.
Edit: keydown works better than keyup
Edit: declare function before using
Edit: input works better than keydown (thnx #WASD42 & #MA-Maddin)
jsfiddle
The best solution (works and is short) for me is:
$(document).on('input', 'textarea', function () {
$(this).outerHeight(38).outerHeight(this.scrollHeight); // 38 or '1em' -min-height
});
It works like a charm without any blinking with paste (with mouse also), cut, entering and it shrinks to the right size.
Please take a look at jsFiddle.
Found an one liner from here;
<textarea name="text" oninput="this.style.height = ''; this.style.height = this.scrollHeight +'px'"></textarea>
If you don’t need to support IE8 you can use the input event:
var resizingTextareas = [].slice.call(document.querySelectorAll('textarea[autoresize]'));
resizingTextareas.forEach(function(textarea) {
textarea.addEventListener('input', autoresize, false);
});
function autoresize() {
this.style.height = 'auto';
this.style.height = this.scrollHeight+'px';
this.scrollTop = this.scrollHeight;
window.scrollTo(window.scrollLeft,(this.scrollTop+this.scrollHeight));
}
Now you only need to add some CSS and you are done:
textarea[autoresize] {
display: block;
overflow: hidden;
resize: none;
}
Usage:
<textarea autoresize>Type here and I’ll resize.</textarea>
You can read more about how it works on my blog post.
You're using the higher value of the current clientHeight and the content scrollHeight. When you make the scrollHeight smaller by removing content, the calculated area can't get smaller because the clientHeight, previously set by style.height, is holding it open. You could instead take a max() of scrollHeight and a minimum height value you have predefined or calculated from textarea.rows.
In general you probably shouldn't really rely on scrollHeight on form controls. Apart from scrollHeight being traditionally less widely-supported than some of the other IE extensions, HTML/CSS says nothing about how form controls are implemented internally and you aren't guaranteed scrollHeight will be anything meaningful. (Traditionally some browsers have used OS widgets for the task, making CSS and DOM interaction on their internals impossible.) At least sniff for scrollHeight/clientHeight's existance before trying to enable the effect.
Another possible alternative approach to avoid the issue if it's important that it work more widely might be to use a hidden div sized to the same width as the textarea, and set in the same font. On keyup, you copy the text from the textarea to a text node in hidden div (remembering to replace '\n' with a line break, and escape '<'/'&' properly if you're using innerHTML). Then simply measuring the div's offsetHeight will give you the height you need.
autosize
https://github.com/jackmoore/autosize
Just works, standalone, is popular (3.0k+ GitHub stars as of October 2018), available on cdnjs) and lightweight (~3.5k). Demo:
<textarea id="autosize" style="width:200px;">a
J b
c</textarea>
<script src="https://cdnjs.cloudflare.com/ajax/libs/autosize.js/4.0.2/autosize.min.js"></script>
<script>autosize(document.querySelectorAll('#autosize'));</script>
BTW, if you are using the ACE editor, use maxLines: Infinity: Automatically adjust height to contents in Ace Cloud 9 editor
As a different approach, you can use a <span> which adjusts its size automatically. You will need make it editable by adding the contenteditable="true" property and you're done:
div {
width: 200px;
}
span {
border: 1px solid #000;
padding: 5px;
}
<div>
<span contenteditable="true">This text can be edited by the user</span>
</div>
The only issue with this approach is that if you want to submit the value as part of the form, you'll have to do so by yourself in JavaScript. Doing so is relatively easy. For example, you can add a hidden field and in the onsubmit event of the form assign the value of the span to the hidden field which will be then automatically submitted with the form.
There is a slightly different approach.
<div style="position: relative">
<pre style="white-space: pre-wrap; word-wrap: break-word"></pre>
<textarea style="position: absolute; top: 0; left: 0; width: 100%; height: 100%"></textarea>
</div>
The idea is to copy the text from textarea into the pre and let CSS make sure that they have the same size.
The benefit is that frameworks present simple tools to move text around without touching any events. Namely, in AngularJS you would add a ng-model="foo" ng-trim="false" to the textarea and ng-bind="foo + '\n'" to the pre. See a fiddle.
Just make sure that pre has the same font size as the textarea.
Has anyone considered contenteditable? No messing around with scrolling,a nd the only JS I like about it is if you plan on saving the data on blur... and apparently, it's compatible on all of the popular browsers : http://caniuse.com/#feat=contenteditable
Just style it to look like a text box, and it autosizes... Make its min-height the preferred text height and have at it.
What's cool about this approach is that you can save and tags on some of the browsers.
http://jsfiddle.net/gbutiri/v31o8xfo/
var _auto_value = '';
$(document).on('blur', '.autosave', function(e) {
var $this = $(this);
if ($this.text().trim() == '') {
$this.html('');
}
// The text is here. Do whatever you want with it.
$this.addClass('saving');
if (_auto_value !== $this.html() || $this.hasClass('error')) {
// below code is for example only.
$.ajax({
url: '/echo/json/?action=xyz_abc',
data: 'data=' + $this.html(),
type: 'post',
datatype: 'json',
success: function(d) {
console.log(d);
$this.removeClass('saving error').addClass('saved');
var k = setTimeout(function() {
$this.removeClass('saved error')
}, 500);
},
error: function() {
$this.removeClass('saving').addClass('error');
}
});
} else {
$this.removeClass('saving');
}
}).on('focus mouseup', '.autosave', function() {
var $this = $(this);
if ($this.text().trim() == '') {
$this.html('');
}
_auto_value = $this.html();
}).on('keyup', '.autosave', function(e) {
var $this = $(this);
if ($this.text().trim() == '') {
$this.html('');
}
});
body {
background: #3A3E3F;
font-family: Arial;
}
label {
font-size: 11px;
color: #ddd;
}
.autoheight {
min-height: 16px;
font-size: 16px;
margin: 0;
padding: 10px;
font-family: Arial;
line-height: 20px;
box-sizing: border-box;
-o-box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
overflow: hidden;
display: block;
resize: none;
border: 0;
outline: none;
min-width: 200px;
background: #ddd;
max-height: 400px;
overflow: auto;
}
.autoheight:hover {
background: #eee;
}
.autoheight:focus {
background: #fff;
}
.autosave {
-webkit-transition: all .2s;
-moz-transition: all .2s;
transition: all .2s;
position: relative;
float: none;
}
.autoheight * {
margin: 0;
padding: 0;
}
.autosave.saving {
background: #ff9;
}
.autosave.saved {
background: #9f9;
}
.autosave.error {
background: #f99;
}
.autosave:hover {
background: #eee;
}
.autosave:focus {
background: #fff;
}
[contenteditable=true]:empty:before {
content: attr(placeholder);
color: #999;
position: relative;
top: 0px;
/*
For IE only, do this:
position: absolute;
top: 10px;
*/
cursor: text;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<label>Your Name</label>
<div class="autoheight autosave contenteditable" contenteditable="true" placeholder="Your Name"></div>
The following works for cutting, pasting, etc., regardless of whether those actions are from the mouse, a keyboard shortcut, selecting an option from a menu bar ... several answers take a similar approach but they don't account for box-sizing, which is why they incorrectly apply the style overflow: hidden.
I do the following, which also works well with max-height and rows for minimum and maximum height.
function adjust() {
var style = this.currentStyle || window.getComputedStyle(this);
var boxSizing = style.boxSizing === 'border-box'
? parseInt(style.borderBottomWidth, 10) +
parseInt(style.borderTopWidth, 10)
: 0;
this.style.height = '';
this.style.height = (this.scrollHeight + boxSizing) + 'px';
};
var textarea = document.getElementById("ta");
if ('onpropertychange' in textarea) { // IE
textarea.onpropertychange = adjust;
} else if ('oninput' in textarea) {
textarea.oninput = adjust;
}
setTimeout(adjust.bind(textarea));
textarea {
resize: none;
max-height: 150px;
border: 1px solid #999;
outline: none;
font: 18px sans-serif;
color: #333;
width: 100%;
padding: 8px 14px;
box-sizing: border-box;
}
<textarea rows="3" id="ta">
Try adding several lines to this.
</textarea>
For absolute completeness, you should call the adjust function in a few more circumstances:
Window resize events, if the width of the textarea changes with window resizing, or other events that change the width of the textarea
When the textarea's display style attribute changes, e.g. when it goes from none (hidden) to block
When the value of the textarea is changed programmatically
Note that using window.getComputedStyle or getting currentStyle can be somewhat computationally expensive, so you may want to cache the result instead.
Works for IE6, so I really hope that's good enough support.
I used the following code for multiple textareas. Working fine in Chrome 12, Firefox 5 and IE 9, even with delete, cut and paste actions performed in the textareas.
function attachAutoResizeEvents() {
for (i = 1; i <= 4; i++) {
var txtX = document.getElementById('txt' + i)
var minH = txtX.style.height.substr(0, txtX.style.height.indexOf('px'))
txtX.onchange = new Function("resize(this," + minH + ")")
txtX.onkeyup = new Function("resize(this," + minH + ")")
txtX.onchange(txtX, minH)
}
}
function resize(txtX, minH) {
txtX.style.height = 'auto' // required when delete, cut or paste is performed
txtX.style.height = txtX.scrollHeight + 'px'
if (txtX.scrollHeight <= minH)
txtX.style.height = minH + 'px'
}
window.onload = attachAutoResizeEvents
textarea {
border: 0 none;
overflow: hidden;
outline: none;
background-color: #eee
}
<textarea style='height:100px;font-family:arial' id="txt1"></textarea>
<textarea style='height:125px;font-family:arial' id="txt2"></textarea>
<textarea style='height:150px;font-family:arial' id="txt3"></textarea>
<textarea style='height:175px;font-family:arial' id="txt4"></textarea>
A bit corrections. Works perfectly in Opera
$('textarea').bind('keyup keypress', function() {
$(this).height('');
var brCount = this.value.split('\n').length;
this.rows = brCount+1; //++ To remove twitching
var areaH = this.scrollHeight,
lineHeight = $(this).css('line-height').replace('px',''),
calcRows = Math.floor(areaH/lineHeight);
this.rows = calcRows;
});
I Don't know if anyone mention this way but in some cases it's possible to resize the height with rows Attribute
textarea.setAttribute('rows',breaks);
Demo
Here is an angularjs directive for panzi's answer.
module.directive('autoHeight', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
element = element[0];
var resize = function(){
element.style.height = 'auto';
element.style.height = (element.scrollHeight)+'px';
};
element.addEventListener('change', resize, false);
element.addEventListener('cut', resize, false);
element.addEventListener('paste', resize, false);
element.addEventListener('drop', resize, false);
element.addEventListener('keydown',resize, false);
setTimeout(resize, 100);
}
};
});
HTML:
<textarea ng-model="foo" auto-height></textarea>
I know a short and correct way of implementing this with jquery.No extra hidden div needed and works in most browser
<script type="text/javascript">$(function(){
$("textarea").live("keyup keydown",function(){
var h=$(this);
h.height(60).height(h[0].scrollHeight);//where 60 is minimum height of textarea
});});
</script>
Some of the answers here don't account for padding.
Assuming you have a maxHeight you don't want to go over, this worked for me:
// obviously requires jQuery
// element is the textarea DOM node
var $el = $(element);
// inner height is height + padding
// outerHeight includes border (and possibly margins too?)
var padding = $el.innerHeight() - $el.height();
var originalHeight = $el.height();
// XXX: Don't leave this hardcoded
var maxHeight = 300;
var adjust = function() {
// reset it to the original height so that scrollHeight makes sense
$el.height(originalHeight);
// this is the desired height (adjusted to content size)
var height = element.scrollHeight - padding;
// If you don't want a maxHeight, you can ignore this
height = Math.min(height, maxHeight);
// Set the height to the new adjusted height
$el.height(height);
}
// The input event only works on modern browsers
element.addEventListener('input', adjust);
An even simpler, cleaner approach is this:
// adjust height of textarea.auto-height
$(document).on( 'keyup', 'textarea.auto-height', function (e){
$(this).css('height', 'auto' ); // you can have this here or declared in CSS instead
$(this).height( this.scrollHeight );
}).keyup();
// and the CSS
textarea.auto-height {
resize: vertical;
max-height: 600px; /* set as you need it */
height: auto; /* can be set here of in JS */
overflow-y: auto;
word-wrap:break-word
}
All that is needed is to add the .auto-height class to any textarea you want to target.
Tested in FF, Chrome and Safari. Let me know if this doesn't work for you, for any reason. But, this is the cleanest and simplest way I've found this to work. And it works great! :D
You can use JQuery to expand the textarea while typing:
$(document).find('textarea').each(function () {
var offset = this.offsetHeight - this.clientHeight;
$(this).on('keyup input focus', function () {
$(this).css('height', 'auto').css('height', this.scrollHeight + offset);
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<textarea name="note"></textarea>
<div>
Those who want to achieve the same in new versions of Angular.
Grab textArea elementRef.
#ViewChild('textArea', { read: ElementRef }) textArea: ElementRef;
public autoShrinkGrow() {
textArea.style.overflow = 'hidden';
textArea.style.height = '0px';
textArea.style.height = textArea.scrollHeight + 'px';
}
<textarea (keyup)="autoGrow()" #textArea></textarea>
I am also adding another use case that may come handy some users reading the thread, when user want to increase the height of text-area to certain height and then have overflow:scroll on it, above method can be extended to achieve the mentioned use-case.
public autoGrowShrinkToCertainHeight() {
const textArea = this.textArea.nativeElement;
if (textArea.scrollHeight > 77) {
textArea.style.overflow = 'auto';
return;
}
else {
textArea.style.overflow = 'hidden';
textArea.style.height = '0px';
textArea.style.height = textArea.scrollHeight + 'px';
}
}
my implementation is very simple, count the number of lines in the input (and minimum 2 rows to show that it's a textarea):
textarea.rows = Math.max(2, textarea.value.split("\n").length) // # oninput
full working example with stimulus: https://jsbin.com/kajosolini/1/edit?html,js,output
(and this works with the browser's manual resize handle for instance)
This code works for pasting and select delete also.
onKeyPressTextMessage = function(){
var textArea = event.currentTarget;
textArea.style.height = 'auto';
textArea.style.height = textArea.scrollHeight + 'px';
};
<textarea onkeyup="onKeyPressTextMessage(event)" name="welcomeContentTmpl" id="welcomeContent" onblur="onblurWelcomeTitle(event)" rows="2" cols="40" maxlength="320"></textarea>
Here is the JSFiddle
I recommend the javascript library from http://javierjulio.github.io/textarea-autosize.
Per comments, add example codeblock on plugin usage:
<textarea class="js-auto-size" rows="1"></textarea>
<script src="http://code.jquery.com/jquery-2.1.0.min.js"></script>
<script src="jquery.textarea_autosize.min.js"></script>
<script>
$('textarea.js-auto-size').textareaAutoSize();
</script>
Minimum required CSS:
textarea {
box-sizing: border-box;
max-height: 160px; // optional but recommended
min-height: 38px;
overflow-x: hidden; // for Firefox (issue #5)
}
MakeTextAreaResisable that uses qQuery
function MakeTextAreaResisable(id) {
var o = $(id);
o.css("overflow-y", "hidden");
function ResizeTextArea() {
o.height('auto');
o.height(o[0].scrollHeight);
}
o.on('change', function (e) {
ResizeTextArea();
});
o.on('cut paste drop keydown', function (e) {
window.setTimeout(ResizeTextArea, 0);
});
o.focus();
o.select();
ResizeTextArea();
}
None of the answers seem to work. But this one works for me:
https://coderwall.com/p/imkqoq/resize-textarea-to-fit-content
$('#content').on( 'change keyup keydown paste cut', 'textarea', function (){
$(this).height(0).height(this.scrollHeight);
}).find( 'textarea' ).change();
Accepted answer is working fine. But that is lot of code for this simple functionality. The below code will do the trick.
$(document).on("keypress", "textarea", function (e) {
var height = $(this).css("height");
var iScrollHeight = $(this).prop("scrollHeight");
$(this).css('height',iScrollHeight);
});
An example implementation with React:
const {
useLayoutEffect,
useState,
useRef
} = React;
const TextArea = () => {
const ref = useRef();
const [value, setValue] = useState('Some initial text that both wraps and uses\nnew\nlines');
// This only tracks the auto-sized height so we can tell if the user has manually resized
const autoHeight = useRef();
useLayoutEffect(() => {
if (!ref.current) {
return;
}
if (
autoHeight.current !== undefined &&
ref.current.style.height !== autoHeight.current
) {
// don't auto size if the user has manually changed the height
return;
}
ref.current.style.height = "auto";
ref.current.style.overflow = "hidden";
const next = `${ref.current.scrollHeight}px`;
ref.current.style.height = next;
autoHeight.current = next;
ref.current.style.overflow = "auto";
}, [value, ref, autoHeight]);
return (
<textarea
ref={ref}
style={{
resize: 'vertical',
minHeight: '1em',
}}
value={value}
onChange={event => setValue(event.target.value)}
/>
);
}
ReactDOM.render(<TextArea />, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>
A simple way to do using React.
...
const textareaRef = useRef();
const handleChange = (e) => {
textareaRef.current.style.height = "auto";
textareaRef.current.style.height = textareaRef.current.scrollHeight + "px";
};
return <textarea ref={textareaRef} onChange={handleChange} />;

How to add a touch event (right, left and down at the screen) in javascript?

I'm studying js and making a Tetris game. I already made it in PC dispositives but I want to add some touch events to the game works on mobile dispositives. But I'm stuck at this:
function touchEnd(e) {
if (Math.abs(offset[0]) > Math.abs(offset[1])) {
playerMove(-1);
}
else if (Math.abs(offset[0]) < Math.abs(offset[1])) {
playerDrop();
}
}
(I'm having trouble with this code above)
My idea is when the player touches (not dragging) at the left of the screen (canvas), the playerMove(-1) works, when you touch at the right, the playerMove(1); works, and when you touch at the bottom of the screen, the playerDrop() works. How can I do this? Thank you.
This is an example on how you can split an HTMLElement and send actions based on that
let box;
document.addEventListener('DOMContentLoaded', ()=>{
box = document.querySelector('#test');
box.addEventListener('click', chooseSide);
});
function chooseSide(e){
const {clientX, clientY} = e;
const {clientHeight, clientWidth} = box
if(clientY > (clientHeight/ 2)){
console.log('bottom')
}else if(clientX < (clientWidth/ 2)){
console.log('left')
} else{
console.log('right')
}
}
#test {
box-sizing: border-box;
width: 100vmin;
height: 100vmin;
border: 10px solid #f00;
}
<canvas id="test"></canvas>
You can change the value of the Y comparison, and give more or less space to what the "bottom" is

Prevent user from copying text on mobile browsers

I'm trying to develop a typing speed competition using JavaScript. People should write all the words they see from a div to a textarea.
To prevent cheating (like copying the words from div) one way is check the written words only when a keyboard key is down, but I was wondering if there is a way to prevent the user from copying the text in a browser?
What I have tried so far:
Disable right click (didn't work on mobile browsers)
Show an alert using the onmousedown event in all the page (it didn't work either)
Using any libraries is OK.
You can simply make the text into an image.
<style type="text/css">
div.image {
width: 100px;
height: 100px;
background-image: url-to-your-image;
}
</style>
To generate the images you can use a server side script as in the aswers of this question
or something like this:
<?php
header("Content-type: image/png");
$im = #imagecreate(210, 30)
or die("Cannot Initialize new GD image stream");
$background_color = imagecolorallocate($im, 255, 255, 255);
$text_color = imagecolorallocate($im, 0, 0, 0);
imagestring($im, 4, 5, 5, "This is a test", $text_color);
imagepng($im);
imagedestroy($im);
?>
Test here
You can prevent the user from actually selecting the text so it can not be copied - however I'd still combine this with paste detection as others recommended
.noselect {
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
<p>this can be selected</p>
<p class="noselect">this can NOT be selected</p>
But the user can still open the page source and copy it from there.
One crazy way of doing this is, laying out another absolutely positioned element on top of this. But this will disallow clicking of links too! May be you can do it with position: relative and a higher z-index.
.content {position: relative;}
.content .mask {position: absolute; z-index: 1; width: 100%; height: 100%;}
.content a {position: relative; z-index: 3;}
<div class="content">
<div class="mask"></div>
<p>Pages that you view in incognito tabs won’t stick around in your browser’s history, cookie store or search history after you’ve closed <strong>all</strong> of your incognito tabs. Any files that you download or bookmarks that you create will be kept. Learn more about incognito browsing</p>
</div>
Try using the touch or longpress events.
<!DOCTYPE html>
<html>
<head>
<script>
function absorbEvent_(event) {
var e = event || window.event;
e.preventDefault && e.preventDefault();
e.stopPropagation && e.stopPropagation();
e.cancelBubble = true;
e.returnValue = false;
return false;
}
function preventLongPressMenu(node) {
node.ontouchstart = absorbEvent_;
node.ontouchmove = absorbEvent_;
node.ontouchend = absorbEvent_;
node.ontouchcancel = absorbEvent_;
}
function init() {
preventLongPressMenu(document.getElementById('theimage'));
}
</script>
</head>
<body onload="init()">
<img id="theimage" src="http://www.google.com/logos/arthurboyd2010-hp.jpg" width="400">
</body>
</html>
Source
Try putting a transparent div over the text.
I have used jQuery here.
That should work.
var position = $('#textInHere').position();
$('#noClickThroughThis').css({
height: ($('#textInHere').height()),
width: ($('#textInHere').width()),
position: 'absolute',
top: position.top,
left: position.left,
'z-index': 100
});
Here is a fiddle
http://jsfiddle.net/lacrioque/tc4bwejn/
It's easy to disable the paste feature by using jQuery. For example, if you have an edit field like this one:
<p id='someInput' contenteditable='true'>Here is the text</p>
Then, this piece of jQuery code will disable the pasting feature on it:
$('#someInput').on('paste', function(e) {
return false;
});
A good way to work out if a user is cheating is to compare the current input length to the last input length. You can use a data attribute to store the previous value (or length):
<textarea class="typing-only" data-temp=""></textarea>
jQuery:
$(document).on('input', '.typing-only', function(){
if((this.value.length - 1) > $(this).data('temp').length){
alert('Cheat!');
}
$(this).data('temp', this.value);
});
JSFiddle demo
pointer-events: none
CSS pointer-events allows you to control the interaction between an element and the mouse. When set to none, the element is never the target of mouse events.
MDN definition page
You can try using :after tag and styling it with content: "Text"; in css, AFAIK you cannot select :before and :after's content.
Thanks for your amazing solutions. I tested all of them, and in short some of them worked only on a PC, some only on Chrome and Firefox and some only on Safari, but unfortunately none of them worked 100%.
Although #Max answer might be safest, I didn't tag with PHP in the question because if I use this solution dealing with answers, it will be hard because I don't have access to words on the client side!
So the ultimate solution I came with was combining all of the provided answers plus some new methods (like clearing the clipboard every second) into a jQuery plugin. Now it works on multiple elements too and worked 100% on PC browsers, Firefox, Chrome, and Safari.
What this plugin does
Prevent pasting (optional)
Clearing clipboard (looks like it doesn't work well)
Absorbs all touch events
Disable right click
Disable user selections
Disable pointer events
Add a mask with a z-index inside any selected DOM
Add a transparent div on any selected DOM
A jsFiddle:
(function($) {
$.fn.blockCopy = function(options) {
var settings = $.extend({
blockPasteClass : null
}, options);
if(settings.blockPasteClass){
$("." + settings.blockPasteClass ).bind('copy paste cut drag drop', function (e) {
e.preventDefault();
return false;
});
}
function style_appender(rule){
$('html > head').append($('<style>'+rule+'</style>'));
}
function html_appender(html){
$("body").append(html);
}
function clearClipboard() {
var $temp = $("#bypasser");
$temp.val("You can't cheat !").select();
document.execCommand("copy");
}
function add_absolute_div(id) {
html_appender("<div id='noClick"+id+"' onclick='return false;' oncontextmenu='return false;'> </div>");
}
function absorbEvent_(event) {
var e = event || window.event;
e.preventDefault && e.preventDefault();
e.stopPropagation && e.stopPropagation();
e.cancelBubble = true;
e.returnValue = false;
return false;
}
function preventLongPressMenu(node) {
node.ontouchstart = absorbEvent_;
node.ontouchmove = absorbEvent_;
node.ontouchend = absorbEvent_;
node.ontouchcancel = absorbEvent_;
}
function set_absolute_div(element,id){
var position = element.position();
var noclick = "#noClick" + id;
$(noclick).css({
height: (element.height()),
width: (element.width()),
position: 'absolute',
top: position.top,
left: position.left,
'z-index': 100
})
}
$("body").bind("contextmenu", function(e) {
e.preventDefault();
});
//Append needed rules to CSS
style_appender(
"* {-moz-user-select: none !important; -khtml-user-select: none !important; -webkit-user-select: none !important; -ms-user-select: none !important; user-select: none !important; }"+
".content {position: relative !important; }" +
".content .mask {position: absolute !important ; z-index: 1 !important; width: 100% !important; height: 100%!important;}" +
".content a {position: relative !important; z-index: 3 !important;}"+
".content, .content .mask{ pointer-events: none;}"
);
//Append an input to clear the clipboard
html_appender("<input id='bypasser' value='nothing' type='hidden'>");
//Clearing clipboard Intervali
setInterval(clearClipboard,1000);
var id = 1;
return this.each( function() {
//Preventing using touch events
preventLongPressMenu($(this));
//Add CSS preventer rules to selected DOM & append mask to class
$(this).addClass("content").append("<div class='mask'></div>");
//Append an absolute div to body
add_absolute_div(id);
//Set position of the div to selected DOM
set_absolute_div($(this),id);
id++;
});
}
}(jQuery));
Usage
$(document).ready(function(){
$(".words").blockCopy({
blockPasteClass : "noPasting"
});
});
HTML for demo:
<div class="words">Test1: Can you copy me or not?</div><br>
<div class="words">Test2: Can you <br> copy me or not?</div><br>
<textarea class="words">Test3: Can you <br>copy me or not?</textarea><br>
<textarea class="noPasting" placeholder="Test1: Paste content if you can" ></textarea><br>
<textarea class="noPasting" placeholder="Test2: Paste content if you can" ></textarea>
Let me know your opinions. Thanks.
Sources
Answers to this question
Copy text to clipboard
Add CSS rule using jQuery
A simpler solution than the accepted one would be to simply use a canvas element with filltext
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.fillText("Can't copy this", 5, 30);
<canvas id="myCanvas"></canvas>
JSFiddle example
You can return false on jQuery's cut copy paste events.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
$(document).on("cut copy paste", function(event){
return false;
});
</script>
<textarea>Try to copy my text</textarea>

Twitter-style autocomplete in textarea

I am looking for a Javascript autocomplete implementation which includes the following:
Can be used in a HTML textarea
Allows for typing regular text without invoking autocomplete
Detects the # character and starts autocomplete when it is typed
Loads list of options through AJAX
I believe that this is similar to what Twitter is doing when tagging in a tweet, but I can't find a nice, reusable implementation.
A solution with jQuery would be perfect.
Thanks.
Another great library which solves this problem At.js (deprecated)
Source
Demo
They are now suggesting the Tribute library
https://github.com/zurb/tribute
Example
I'm sure your problem is long since solved, but jquery-textcomplete looks like it would do the job.
Have you tried this
GITHUB: https://github.com/podio/jquery-mentions-input
DEMO/CONFIG: http://podio.github.io/jquery-mentions-input/
It is pretty simple to implement.
I've created a Meteor package for this purpose. Meteor's data model allows for fast multi-rule searching with custom rendered lists. If you're not using Meteor for your web app, (I believe) you unfortunately won't find anything this awesome for autocompletion.
Autocompleting users with #, where online users are shown in green:
In the same line, autocompleting something else with metadata and bootstrap icons:
Fork, pull, and improve:
https://github.com/mizzao/meteor-autocomplete
Try this:
(function($){
$.widget("ui.tagging", {
// default options
options: {
source: [],
maxItemDisplay: 3,
autosize: true,
animateResize: false,
animateDuration: 50
},
_create: function() {
var self = this;
this.activeSearch = false;
this.searchTerm = "";
this.beginFrom = 0;
this.wrapper = $("<div>")
.addClass("ui-tagging-wrap");
this.highlight = $("<div></div>");
this.highlightWrapper = $("<span></span>")
.addClass("ui-corner-all");
this.highlightContainer = $("<div>")
.addClass("ui-tagging-highlight")
.append(this.highlight);
this.meta = $("<input>")
.attr("type", "hidden")
.addClass("ui-tagging-meta");
this.container = $("<div></div>")
.width(this.element.width())
.insertBefore(this.element)
.addClass("ui-tagging")
.append(
this.highlightContainer,
this.element.wrap(this.wrapper).parent(),
this.meta
);
var initialHeight = this.element.height();
this.element.height(this.element.css('lineHeight'));
this.element.keypress(function(e) {
// activate on #
if (e.which == 64 && !self.activeSearch) {
self.activeSearch = true;
self.beginFrom = e.target.selectionStart + 1;
}
// deactivate on space
if (e.which == 32 && self.activeSearch) {
self.activeSearch = false;
}
}).bind("expand keyup keydown change", function(e) {
var cur = self.highlight.find("span"),
val = self.element.val(),
prevHeight = self.element.height(),
rowHeight = self.element.css('lineHeight'),
newHeight = 0;
cur.each(function(i) {
var s = $(this);
val = val.replace(s.text(), $("<div>").append(s).html());
});
self.highlight.html(val);
newHeight = self.element.height(rowHeight)[0].scrollHeight;
self.element.height(prevHeight);
if (newHeight < initialHeight) {
newHeight = initialHeight;
}
if (!$.browser.mozilla) {
if (self.element.css('paddingBottom') || self.element.css('paddingTop')) {
var padInt =
parseInt(self.element.css('paddingBottom').replace('px', '')) +
parseInt(self.element.css('paddingTop').replace('px', ''));
newHeight -= padInt;
}
}
self.options.animateResize ?
self.element.stop(true, true).animate({
height: newHeight
}, self.options.animateDuration) :
self.element.height(newHeight);
var widget = self.element.autocomplete("widget");
widget.position({
my: "left top",
at: "left bottom",
of: self.container
}).width(self.container.width()-4);
}).autocomplete({
minLength: 0,
delay: 0,
maxDisplay: this.options.maxItemDisplay,
open: function(event, ui) {
var widget = $(this).autocomplete("widget");
widget.position({
my: "left top",
at: "left bottom",
of: self.container
}).width(self.container.width()-4);
},
source: function(request, response) {
if (self.activeSearch) {
self.searchTerm = request.term.substring(self.beginFrom);
if (request.term.substring(self.beginFrom - 1, self.beginFrom) != "#") {
self.activeSearch = false;
self.beginFrom = 0;
self.searchTerm = "";
}
if (self.searchTerm != "") {
if ($.type(self.options.source) == "function") {
self.options.source(request, response);
} else {
var re = new RegExp("^" + escape(self.searchTerm) + ".+", "i");
var matches = [];
$.each(self.options.source, function() {
if (this.label.match(re)) {
matches.push(this);
}
});
response(matches);
}
}
}
},
focus: function() {
// prevent value inserted on focus
return false;
},
select: function(event, ui) {
self.activeSearch = false;
//console.log("#"+searchTerm, ui.item.label);
this.value = this.value.replace("#" + self.searchTerm, ui.item.label) + ' ';
self.highlight.html(
self.highlight.html()
.replace("#" + self.searchTerm,
$("<div>").append(
self.highlightWrapper
.text(ui.item.label)
.clone()
).html()+' ')
);
self.meta.val((self.meta.val() + " #[" + ui.item.value + ":]").trim());
return false;
}
});
}
});
body, html {
font-family: "lucida grande",tahoma,verdana,arial,sans-serif;
}
.ui-tagging {
position: relative;
border: 1px solid #B4BBCD;
height: auto;
}
.ui-tagging .ui-tagging-highlight {
position: absolute;
padding: 5px;
overflow: hidden;
}
.ui-tagging .ui-tagging-highlight div {
color: transparent;
font-size: 13px;
line-height: 18px;
white-space: pre-wrap;
}
.ui-tagging .ui-tagging-wrap {
position: relative;
padding: 5px;
overflow: hidden;
zoom: 1;
border: 0;
}
.ui-tagging div > span {
background-color: #D8DFEA;
font-weight: normal !important;
}
.ui-tagging textarea {
display: block;
font-family: "lucida grande",tahoma,verdana,arial,sans-serif;
background: transparent;
border-width: 0;
font-size: 13px;
height: 18px;
outline: none;
resize: none;
vertical-align: top;
width: 100%;
line-height: 18px;
overflow: hidden;
}
.ui-autocomplete {
font-size: 13px;
background-color: white;
border: 1px solid black;
margin-bottom: -5px;
width: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea></textarea>
http://jsfiddle.net/mekwall/mcWnL/52/
This link will help you
I could not find any solution that matched my requirements perfectly, so I ended up with the following:
I use the jQuery keypress() event to check for the user pressing the # character.
If this is the case, a modal dialog is shown using jQuery UI. This dialog contains an autocomplete text field (many options can be used here, but I recommmend jQuery Tokeninput)
When the user selects an option in the dialog, a tag is added to the text field and the dialog is closed.
This is not the most elegant solution, but it works and it does not require extra keypresses compared to my original design.
Edit
So basically, we have our large text box where the user can enter text. He should be able to "tag" a user (this just means inserting #<userid> in the text). I attach to the jQuery keyup event and detect the # character using (e.which == 64) to show a modal with a text field for selecting the users to tag.
The meat of the solution is simply this modal dialog with a jQuery Tokeninput text box. As the user types here, the list of users is loaded through AJAX. See the examples on the website for how to use it properly. When the user closes the dialog, I insert the selected IDs into the large text box.
Recently i had to face this problem and this is how i nailed down...
Get the string index at the cursor position in the textarea by using selectionStart
slice the string from index 0 to the cursor position
Insert it into a span (since span has multiple border boxes)
Get the dimensions of the border box using element.getClientRects() relative to the view port. (here is the MDN Reference)
Calculate the top and left and feed it to the dropdown
This works in all latest browsers. haven't tested at old ones
Here is Working bin
Another plugin which provides similar functionality:
AutoSuggest
You can use it with custom triggers or you can use it without any triggers. Works with input fields, textareas and contenteditables. And jQuery is not a dependency.
I would recommend the textcomplete plugin. No jQuery dependency. You may need bootstrap.css to refer, but I recommend to write your own CSS, lighter and simple.
Follow the below steps to give it a try
npm install #textcomplete/core #textcomplete/textarea
Bind it to your input element
const editor = new TextareaEditor(inputEl);
const textcomplete = new Textcomplete(editor, strategy, options);
Set strategy(how to fetch suggestion list) and options(settings to configure the suggestions) according to your need.
JS version
Angular Version
This small extension seems to be the closest at least in presentation to what was asked. Since it's small, it can be easily understood and modified. http://lookapanda.github.io/jquery-hashtags/
THIS should work. With regards to the # kicking off the search, just add (dynamically or not) the symbol to the beginning of each possible search term.

difference in mouse events on img/div

I have a little tool on a web page that can be clicked and dragged along a horizontal line. First I had the drag handle as an img. I had some problems with layout and realised later that it would probably be better to use a div with an img background.
So I changed the drag handle to use a div, and I discovered a pretty obvious error in my javascript code. I had the onMove and onUp events attached to the handle itself. So for example, if I clicked on the drag handle div and then moved my mouse upwards out of the div (as the div only moves left and right), it then doesn't catch the onUp or onMove events.
But the thing I don't understand, is why it worked perfectly when I was using an img tag. Is this a bug or is there something funny about images that makes them behave this way? Is it future-safe to leave use an img and leave the code as is?
When drag starts (onDown or
onClick), you should actually
dynamically attach onUp and onMove
events to document instead of
handle. Just don't forget to clear
them onUp.
However, there should be no difference
between <img> and <div> in this
case.
-- EDIT --
I stand corrected. Seems, there is slight difference in img and div handling in FF since FF3. It enables you to drag images to your computer, but we can get around it :).
Anyway, I have made some slight modifications to your script, cleaned it up a bit, and it should work now in all browsers. I have tested it on my computer with Firefox 3.5.5, Safari 4.0.3, Google Chrome, and Opera 10.1.
However, I don't have a way to test it on IE, so please report results.
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Page Title</title>
<script>
window.onload = function() { //After page is loaded this script will start automaticly
var divEl = document.getElementById("myDiv"); // Locate element with id "myDiv"
var imgEl = document.getElementById("myImg"); // Locate element with id "myImg"
divEl.onmousedown = onDown; // Assign funnction to event mousedown on "myDiv"
imgEl.onmousedown = onDown; // Assign funnction to event mousedown on "myImg"
function onDown(e) { // Mouse is down, long live the mouse :)
$el = this;
if (e.preventDefault) { e.preventDefault(e); } // This prevents default "drag image" behaivour
window.onmousemove = function(e) { // Assign function onmousemove to window
this.onmouseup = function(e) { // When we do a mouseup anywhere on window
window.onmousemove = undefined; // We clear onmouse from window
window.onmouseup = undefined; // We clear onmouseup from window
}
/* Part borowed from http://www.quirksmode.org/js/events_properties.html */
if (e.pageX) { posx = e.pageX; } // In case it is a normal browser
else if (e.clientX) { // In case it is IE
posx = e.clientX + document.body.scrollLeft+document.documentElement.scrollLeft;
}
/* End of borowed part */
$el.style.left = posx-parseInt($el.offsetWidth)/2+"px"; // Move this element to where mouse is
}
}
}
</script>
<style>
#myImg, #myDiv { display: block; position: relative; left: 0; width: 100px; height: 50px; }
#myImg { border: 1px solid red; }
#myDiv { border: 1px solid blue; background-color: lime;}
</style>
</head>
<body>
<img src="some_image.png" alt="my img" id="myImg">
<div id="myDiv">My div</div>
</body>
</html>
Ah, yes. CSS & JS in this example are in HTML only for testing purposes. In real code it is suggested you separate them to their own files.
Good luck :)
I'm still not sure what was causing the original behaviour in IE, but I've rewritten as Krule's suggestion and there is still a problem with Mozilla handling img tags differently to div tags. Here's an example, works fine in IE, doesn't work properly in Mozilla:
<script language="javascript" type="text/JavaScript">
var elem = null;
var downX = null;
var elemX = null;
function onDown(e) {
var ev = window.event ? window.event : e;
elem = window.event ? ev.srcElement : ev.target;
downX = ev.clientX;
elemX = parseInt(elem.style.left);
document.onmousemove = onMove;
document.onmouseup = onUp;
}
function onMove(e) {
var ev = window.event ? window.event : e;
elem.style.left = elemX + ev.clientX - downX + "px";
}
function onUp() {
document.onmousemove = null;
document.onmouseup = null;
}
</script>
<img src="" alt="my img" id="myImg" onmousedown="onDown(event);" style="position:relative; left:0px; width:100px; height:50px; border: solid 1px red;" />
<br />
<br />
<div onmousedown="onDown(event);" style="position:relative; left:0px; width:100px; height:50px; background-color:Lime; border: solid 1px blue;"><div>
Not sure if there is a way to override this behaviour, or if I'm going to have to rewrite everything yet again to use div instead of img.

Categories

Resources