Resize table columns with mouse - javascript

I am writing a script to allow a client to mouse drag table cell borders and resize columns in a table. So far I have a working model in Firefox but there is a flaw in width measurement that leaves the mouse out of sync when the change gets large. Worse, the script fails in other browsers (opera,safari) or even if I change the browser zoom in Firefox.
function doDrag() {document.body.style.cursor='crosshair';}
function noDrag() {document.body.style.cursor='auto';}
var xpos=0;
var sz=0;
var dragObj = {};
function resizeOn(el)
{
dragObj = document.getElementById(el);
document.addEventListener("mousemove",resize, true);
document.addEventListener("mouseup",resizeOff, true);
}
function resize(ev)
{
if(xpos == 0) {xpos=ev.clientX;}
if(xpos != ev.clientX)
{
sz = dragObj.offsetWidth + (ev.clientX - xpos);
dragObj.style.width = sz - 10 + "px";
alert("size="+sz+" offsetwidth="+dragObj.offsetWidth);
if(dragObj.offsetWidth != sz)
{
resizeOff();
return false;
}
xpos=ev.clientX;
}
}
function resizeOff()
{
xpos = 0;
document.removeEventListener("mousemove",resize, true);
document.removeEventListener("mouseup",resizeOff, true);
}
The HTML looks like:
<th id="col0" class="edit">client</th>
<th class="drag" onmouseover="doDrag()" onmouseout="noDrag()" onmousedown="resizeOn('col0')"></th>
The second cell is made to appear as the right edge of the first.
I assume the problem is dragObj.style.width = sz - 10. The -10 was derived purely by trial and error. I suspect this is the difference between the actual width of the cell including borders, padding etc and offsetwidth. It should really be, per my css, 10 for padding + 1 for the left border = 11px. Either my fixed padding/borders aren't staying fixed or there is some other css property between the offsetWidth and the actual with of the element. Is there some way to get the actual width of the element regardless of the browsers scaling?

Have a look at the documentation in www.quirksmode.org and/or http://help.dottoro.com. Confirm whether the properties are supported by your target browser. They also have comments on how zoom affects offsetX and similar.
Also, you should note that ev.clientX has been broken in IE by a recent patch (KB2846071). If the patch is installed, clientX returns a meaningless result.
Hopefully MS will release a patch for their patch!

Related

Issue calculating table scrollbar location

So I have a script that adds a slight shadow to table edge where you can scroll, depending on the location of the scrollbar, but it sometimes doesn't work.
This is one part of it:
$('table').on('scrollstart scrollstop', function(){
if($(this).parent().hasClass('table-wrap')){
var elem = $(this),
elemBody = elem.find('tbody'),
elemParent = elem.parent('.table-wrap');
var scrolled = (elemBody.outerWidth() - elemParent.outerWidth() - elem.scrollLeft());
if(scrolled === 0){
elemParent.addClass('left_active');
elemParent.removeClass('right_active');
} else if(elem.scrollLeft() === 0) {
elemParent.removeClass('left_active');
elemParent.addClass('right_active');
} else {
elemParent.addClass('left_active');
elemParent.addClass('right_active');
}
}
});
This part sometimes I have to add "+1" to "elem.scrollLeft() --here---); to make it work.
var scrolled = (elemBody.outerWidth() - elemParent.outerWidth() -
elem.scrollLeft());
But then I noticed, some tables it helps and on others, it stops working. Meaning when I scroll to right the 'right_active' class will not disappear.
Any suggestions?
Have you tried including scrollbar width in your calculation for var scrolled?
var scrolled = (elemBody.outerWidth() - elemParent.outerWidth() - elem.scrollLeft());
I think you are on the right track but the width is probably not precise since .outerWidth() doesn't include scrollbar width. Hence, the maximum scroll width is always greater than the actual element width.

img.on("load") executes after the img is loaded but before it has its size set (IE)

EDIT : Added jsfiddle
(for) https://jsfiddle.net/x60bwgw7/4/
(each) https://jsfiddle.net/v3y2v8cf/3/
open in IE, output values of 30 are wrong (IE placeholder height)
I tried to return DOM naturalHeight and clientHeight, and while naturalHeight works properly in IE, clientHeight (the one I need) does not.
Here's an obvious workaround, but it kinda sucks https://jsfiddle.net/0rhjt0wn/1/
Seems like the problem is that IE renders image after it is loaded, while other browsers render it while it is being loaded, but Im just guessing here.
I've some images, that I want to load by assigning their "data-src" attribute value to theirs "src" attribute and vertically center them after.
It works without a problem in all browsers except IE (tested from 8 to edge).
In IE some images get centered without a problem, but some wont and it is because code in the .on("load") event gets executed (probably) after the image is loaded, but before it gets its size set.
Is there any way to always execute the code after the image is loaded and its size is set (in IE)?
for (i = 0; i < 100; i++)
{
targetSrc = $divElement.eq(i).children(imgClass).attr(dataSrc) + "?v=" + datetime;
$divElement.eq(i).find(imgClass).attr("src", targetSrc).on("load", function()
{
$holder.append($(this).attr("src") + " || height = " + $(this).height() + "<br>");
});
}
Initially thought requestAnimationFrame and a timeout fallback could easily solve this but it turned out to be a lot more complicated. Apparently the exact frame at which an image is rendered after onload is not very predictable in IE. But I eventually came up with this solution that checks the naturalHeight against the current height to see if an image has been rendered, looping through to the next display frame when it is not the case yet :
https://jsfiddle.net/r8m81ajk/
$(function() {
if (window.requestAnimationFrame) var modern = true;
var element = $('.divElement'),
reference = '.imgElement',
holder = $('.holder'),
datetime = Date.now(),
path, image;
for (var i = 0; i < 100; i++) {
var target = element.eq(i).children(reference),
path = target.data('src') + '?v=' + datetime;
target.one('load', function() {
image = this;
if (modern) requestAnimationFrame(function() {
nextFrame(image);
});
else setTimeout(renderImage, 50);
}).attr('src', path);
}
function nextFrame(picture) {
var dimension = $(picture).height();
if (dimension == picture.naturalHeight) {
holder.append('height = ' + dimension + '<br>');
}
else nextFrame(picture);
}
function renderImage() {
holder.append('height = ' + $(image).height() + '<br>');
}
});
The fallback will always check on the third frame (when there's no bottleneck). Don't mind I adapted the code a bit to my usual conventions, the external references should have remained the same.
From the question I realise the loop might not even be needed and getting naturalHeight when the image loads would be enough. Interesting exercise to detect the exact point of rendering in any case.

How can I determine how many squares are needed to fill a browser window, dynamically?

I'm making colored squares to fill a browser window (say, 20px by 20px repeated horizontally and vertically).
There are 100 different colors, each link to a different link (blog post relevant to that color).
I want to fill the browser window with at least 1 of each colored square, and then repeat as necessary to fill the window, so that there are colored squares on the whole background, as the user drags the window smaller and larger.
If these were just images, setting a repeatable background would work. But, I would like them to be links. I'm not sure where to start on this. Any ideas, tips?
Here's the link to the site I'm working on: http://spakonacompany.com/
I think the most specific piece I need here is this: how can I determine the number of squares needed to repeat to fill the background, using jQuery that dynamically calculates that using the size of the browser window, including when dragged, resized, etc?
Many thanks. :)
To get browser's window width and height I use this function ->
//checking if the browser is Internet Explorer
var isIEX = navigator.userAgent.match(/Trident/);
var doc = isIEX ? document.documentElement : document.body;
function getwWH() {
var wD_ = window;
innerW = wD_.innerWidth || doc.clientWidth;
innerH = wD_.innerHeight || doc.clientHeight;
return {iW:innerW, iH:innerH}
}
There is also a native method of detecting when the browser's window is being resized which works in all major browsers (including IE 8, if you're planning on supporting it) ->
window.onresize = function(){
//here goes the code whenever the window is getting resized
}
So, in order to define how many squares are required to fill the window, you can get the window's width and divide it by the width of the square you are going to fill the window with ->
//getting total number of squares for filling the width and the height
width_ = getwWH().iW; //the width of the window
height_ = getwWH().iH; //the height of the window
If your square's width and height are static 20 by 20, than we can calculate total number of squares per window by dividing our width_ variable by 20 (the same for the height_) ->
squaresPerWidth = width_/20;
squaresPerHeight = height_/20;
So every time our browser window is getting resized we do this ->
window.onresize = function(){
width_ = getwWH().iW;
height_ = getwWH().iH;
squaresPerWidth = width_/20;
squaresPerHeight = height_/20;
//and the rest of the code goes here
}
Haven't tested it but this should work.
Here's something I whipped up. It uses a fixed number of resizable squares, but if you need squares of a fixed size, you just set the window to overflow: hidden and generate an unreasonably large number of squares.
var fillGrid = function(getColor, onClick) {
var tenTimes = function(f){
return $.map(new Array(10),
function(n, i) {
return f(i);
});
};
var DIV = function() {
return $('<div></div>');
};
var appendAll = function(d, all) {
$.map(all, function(e) {
d.append(e);
});
return d;
};
appendAll($('body'),
tenTimes(function(col) {
return appendAll(DIV().css({ height : "10%" }),
tenTimes(function(row) {
return DIV().css({
height : "100%",
width : "10%",
backgroundColor: getColor(row, col),
'float' : "left"
}).click(function() { onClick(row, col); });
}));
}));
};
You have to supply two functions, one to specify the color, the other to be invoked when the user clicks.

how do i get the x and y position directly under the left bottom side of the input rectangle?

I'm thinking of implementing a custom auto-complete feature so basically my idea now is that i will make an abs positioned div and give it the position here:
(image) http://i.stack.imgur.com/3c5BH.gif
So my question is with a variable referencing the textbox, how do i get the x and y position directly under the left bottom side of the input rectangle?
My script must work in latest versions of IE / FF / Safari / Opera / Chrome
I know i can use a library to do it, but no i'm interested in learning how do they do it (or maybe better ways)?
This question is a lot more complicated than it seems and involves getting the position of the element relative to the document. The code to do so can be pulled from the jquery source (http://code.jquery.com/jquery-1.6.1.js -- search for "jQuery.fn.offset")
in jQuery:
var node = $('#textbox'),
pos = box.offset(); // the complicated piece I'm using jQuery for
node.top += node.height(); // node.offsetHeight without jQuery
node.left += node.width(); // node.offsetWidth without jQuery
The answer can be extremely simplified if you don't care about FF2 or Safari3:
var box = document.getElementById('yourTextBox').getBoundingClientRect(),
left = box.left,
bottom = box.bottom;
x = x offset
y = y offset - ( textbox height +
padding-top + padding-bottom )
Good comments! For my scenario, there is always an offset parent (which is why I use position - http://api.jquery.com/position/). In hopes that it might help someone else wanting a quick fix, here's the code:
// I have a parent item (item) and a div (detail)
// that pops up at the bottom left corner of the parent:
var jItem = $(item);
var pos = jItem.position();
var marginTop = parseInt(jItem.css('margin-top'));
if (isNaN(marginTop)) {
marginTop = 0;
}
$(detail).css("top", pos.top + jItem.outerHeight() + marginTop)
.css("left", pos.left);
$(detail).show();
Just give the box a defined width and height. Then, get its top and left property and add it with the width and height. Simple. I am gonna give you Pseodocode.
<STYLE>
object{width: 100px; height: 20px;}
</STYLE>
<SCRIPT>
x = object.left;
y = object.top;
x = x + object.width;
y = y + object.height;
</SCRIPT>

What is different with window and div widths between firefox and IE

I have a web page that uses a scrolling div to display table information. When the window is resized (and also on page load), the display is centered and the div's scrollbar positioned to the right of the page by setting its width. For some reason, the behaviour is different under firefox than IE. IE positions/sizes the div as expected, but firefox seems to make it too wide, such that the scrollbar begins to disappear when the window client width reaches about 800px. I'm using the following methods to set the position and size:
function getWindowWidth() {
var windowWidth = 0;
if (typeof(window.innerWidth) == 'number') {
windowWidth=window.innerWidth;
}
else {
if (document.documentElement && document.documentElement.clientWidth) {
windowWidth=document.documentElement.clientWidth ;
}
else {
if (document.body && document.body.clientWidth) {
windowWidth=document.body.clientWidth;
}
}
}
return windowWidth;
}
function findLPos(obj) {
var curleft = 0;
if (obj.offsetParent) {
curleft = obj.offsetLeft
while (obj = obj.offsetParent) {
curleft += obj.offsetLeft
}
}
return curleft;
}
var bdydiv;
var coldiv;
document.body.style.overflow="hidden";
window.onload=resizeDivs;
window.onresize=resizeDivs;
function resizeDivs(){
bdydiv=document.getElementById('bdydiv');
coldiv=document.getElementById('coldiv');
var winWdth=getWindowWidth();
var rghtMarg = 0;
var colHdrTbl=document.getElementById('colHdrTbl');
rghtMarg = parseInt((winWdth - 766) / 2) - 8;
rghtMarg = (rghtMarg > 0 ? rghtMarg : 0);
coldiv.style.paddingLeft = rghtMarg + "px";
bdydiv.style.paddingLeft = rghtMarg + "px";
var bdydivLft=findLPos(bdydiv);
if ((winWdth - bdydivLft) >= 1){
bdydiv.style.width = winWdth - bdydivLft;
coldiv.style.width = bdydiv.style.width;
}
syncScroll();
}
function syncScroll(){
if(coldiv.scrollLeft>=0){
coldiv.scrollLeft=bdydiv.scrollLeft;
}
}
Note that I've cut out other code which sets height, and other non-relevant parts. The full page can be seen here. If you go to the link in both IE and firefox, resize width until "800" is displayed in the green box top-right, and resize height until the scrollbar at the right is enabled, you can see the problem. If you then resize the IE width, the scrollbar stays, but if you resize the firefox width wider, the scrollbar begins to disappear. I'm at a loss as to why this is happening....
Note that AFAIK, getWindowWidth() should be cross-browser-compatible, but I'm not so sure about findLPos().... perhaps there's an extra object in Firefox's DOM or something, which is changing the result??
You are dealing with "one of the best-known software bugs in a popular implementation of Cascading Style Sheets (CSS)" according to Wikipedia. I recommend the Element dimensions and CSS Object Model View pages on Quirksmode.org.
Also: I think you'll find that Safari and Opera behave like Firefox in most circumstances. A more compatible approach to working around these problems is testing for, and making exceptions for, MSIE instead of the other way around.
Ok, I found the problem. Seems to be that firefox does not include the style.paddingLeft value in its style.width setting, whereas IE does, thus the div was ending up exactly style.paddingLeft too wide. That is, if for example style.paddingLeft is 8, IE's style.width value would be 8 more than FireFox's - and thus the inverse when setting the value, for FireFox I needed to subtract the style.paddingLeft value
Modified code with:
if (__isFireFox){
bdydiv.style.width = winWdth - bdydivLft - rghtMarg;
} else {
bdydiv.style.width = winWdth - bdydivLft;
}
As long as you don't include a valid doctype, you can't expect consistent results, due to Quirks Mode. Go add one (HTML 4.01 Transitional is fine), then let us know if it still occurs.
Also see http://en.wikipedia.org/wiki/Quirks_mode.
In your getWindowWidth() function, whenever you grab the width of something, instead of this:
windowWidth = document.documentElement.clientWidth;
try this
windowWidth = Math.max(document.documentElement.scrollWidth, document.documentElement.clientWidth);
A detail to help optimize some of your code:
function getPos(elm) {//jumper
for(var zx=zy=0;elm!=null;zx+=elm.offsetLeft,zy+=elm.offsetTop,elm=elm.offsetParent);
return {x:zx,y:zy}
}
(jumper is a user who posted this code in Eksperten.dk)

Categories

Resources