PDA

View Full Version : Weird rendering issue when rendering more than 11 columns



devol
11 Oct 2006, 12:47 PM
Rendering more than 11 columns causes weird rendering issues in my app. I don't know why that seems to be the magic number, but it is.

If I make the model contain 13 columns, the 12th and 13th column headers render, but inside the model info is blank. Then the data from those two columns renders on top of the first column.

I'm trying to figure out how I can sanitise this client data so that I can put up an example to show you.

devol
11 Oct 2006, 1:11 PM
Ok here is an example of my problem.
http://www.brokencrew.com/~sjacobs/grid/portal-yui.html

devol
11 Oct 2006, 1:39 PM
I'm using the Prototype library still. But If you'd like to see the problem:
In my code:


var myColumns = $R(1, 11*2, false).collect ( function (col, idx) {
var even = {header: 'Week '+ Math.ceil(col/2) + '
Actual', width: 50, renderer: hours};
var odd = {header: 'Week ' + Math.ceil(col/2) + '
Budget', width: 50, renderer: hours};
return (idx % 2 ? even : odd);
});
myColumns = [{header: 'Labor Category', width: 300, renderer: italic}].concat(myColumns);

If you change that to this:


var myColumns = $R(1, 10*2, false).collect ( function (col, idx) { /*** CHANGED 11 to 10 ***/
var even = {header: 'Week '+ Math.ceil(col/2) + '
Actual', width: 50, renderer: hours};
var odd = {header: 'Week ' + Math.ceil(col/2) + '
Budget', width: 50, renderer: hours};
return (idx % 2 ? even : odd);
});
myColumns = [{header: 'Labor Category', width: 300, renderer: italic}].concat(myColumns);

It works, but of course you are now missing the last two columns worth of data on the page.[/code]

jack.slocum
11 Oct 2006, 2:12 PM
Grab this file and include it:

http://www.jackslocum.com/blog/css/reset-min.css and tell me if it fixes it.

It's a very slightly modified version of yahoo standard css reset.

devol
11 Oct 2006, 2:41 PM
No difference. I also tried using reset.css and fonts.css from yahoo and that did nothing as well.

devol
11 Oct 2006, 2:42 PM
One thing I noticed, is if I remove the height of the element, it of course only renders the top portion. But I am able to scroll "further to the right" than if I do specify the height parameter. I don't know if that has anything to do with it.

jack.slocum
11 Oct 2006, 3:47 PM
This was an interesting one.

The problem is you are actually rendering 22 columns. Here's what I get for a row:

<span tabindex="0" class="ygrid-col ygrid-col-0"><span class="ygrid-cell-text">Text For Row 2</span></span><span tabindex="0" class="ygrid-col ygrid-col-1"><span class="ygrid-cell-text"><div class="gridhours">40.00</div></span></span><span tabindex="0" class="ygrid-col ygrid-col-2"><span class="ygrid-cell-text"><div class="gridhours">32.00</div></span></span><span tabindex="0" class="ygrid-col ygrid-col-3"><span class="ygrid-cell-text"><div class="gridhours">40.00</div></span></span><span tabindex="0" class="ygrid-col ygrid-col-4"><span class="ygrid-cell-text"><div class="gridhours">40.00</div></span></span><span tabindex="0" class="ygrid-col ygrid-col-5"><span class="ygrid-cell-text"><div class="gridhours">40.00</div></span></span><span tabindex="0" class="ygrid-col ygrid-col-6"><span class="ygrid-cell-text"><div class="gridhours">120.00</div></span></span><span tabindex="0" class="ygrid-col ygrid-col-7"><span class="ygrid-cell-text"><div class="gridhours">40.00</div></span></span><span tabindex="0" class="ygrid-col ygrid-col-8"><span class="ygrid-cell-text"><div class="gridhours">40.00</div></span></span><span tabindex="0" class="ygrid-col ygrid-col-9"><span class="ygrid-cell-text"><div class="gridhours">40.00</div></span></span><span tabindex="0" class="ygrid-col ygrid-col-10"><span class="ygrid-cell-text"><div class="gridhours">40.00</div></span></span><span tabindex="0" class="ygrid-col ygrid-col-11"><span class="ygrid-cell-text"><div class="gridhours">40.00</div></span></span><span tabindex="0" class="ygrid-col ygrid-col-12"><span class="ygrid-cell-text"><div class="gridhours">40.00</div></span></span><span tabindex="0" class="ygrid-col ygrid-col-13"><span class="ygrid-cell-text"><div class="gridhours">40.00</div></span></span><span tabindex="0" class="ygrid-col ygrid-col-14"><span class="ygrid-cell-text"><div class="gridhours">40.00</div></span></span><span tabindex="0" class="ygrid-col ygrid-col-15"><span class="ygrid-cell-text"><div class="gridhours">40.00</div></span></span><span tabindex="0" class="ygrid-col ygrid-col-16"><span class="ygrid-cell-text"><div class="gridhours">40.00</div></span></span><span tabindex="0" class="ygrid-col ygrid-col-17"><span class="ygrid-cell-text"><div class="gridhours">40.00</div></span></span><span tabindex="0" class="ygrid-col ygrid-col-18"><span class="ygrid-cell-text"><div class="gridhours">40.00</div></span></span><span tabindex="0" class="ygrid-col ygrid-col-19"><span class="ygrid-cell-text"><div class="gridhours">40.00</div></span></span><span tabindex="0" class="ygrid-col ygrid-col-20"><span class="ygrid-cell-text"><div class="gridhours">40.00</div></span></span><span tabindex="0" class="ygrid-col ygrid-col-21"><span class="ygrid-cell-text"><div class="gridhours">40.00</div></span></span><span tabindex="0" class="ygrid-col ygrid-col-22 ygrid-col-last"><span class="ygrid-cell-text"><div class="gridhours">40.00</div></span></span></span>

Notice it goes all the way to ygrid-col-21 and 22. In the default CSS file, I've only defined placeholder rules up to ygrid-col-20. So the extra ones are not being positioned.

On a side note I notice you intend to have 2 grid in the same page. That's fine, but if you do you will have to define column placeholders for each column, for example:

#mygrid .ygrid-col-0{
}

for as many columns as you have. The must be individual selectors and cant be combined. This is a limitation for multi grids in 1 page because they can't share the default rule and I am not aware of a way to dynamically create rules that works in Safari.

devol
11 Oct 2006, 4:21 PM
I realized the 22 column thing too after my initial post, however I was unaware that there were placeholder rules... I guess this is a by product of the positioning code. I was planning on having about 104 columns... (And was noticing that horizontal scrolling wasn't as optimized as vertical scrolling).

I need to take a look and see how this impacts what I'm doing, and if I can think of a better way to optimize this.

devol
11 Oct 2006, 4:22 PM
Worst case I can dynamically generate the css for the page. Kind of ugly though.

devol
11 Oct 2006, 4:48 PM
ok so I see that you are using YAHOO.ext.util.CSS.updateRule to update the css rule dynamically. Could this be changed to us YAHOO.util.Dom.setStyle and giving every column's span an id? And then just setting the positioning attributes directly with the id? You could still skin the id's directly from CSS, and they would be easy to name. You pass the element to render to when you call the grid construct. So if that is gridID you could name things something like:

<span id="gridID-ygrid-col-1">...</span>

Your css would certainly be a lot smaller since you would only need to define rules for columns that you didn't want to be the default.

Thoughts?

devol
11 Oct 2006, 5:41 PM
Ok after a bunch of reading I now realize that in order to have the css files be able to customize the columns, you must have a class for each row. And you can't read the style from the css, you can just set the name, so you have no way to tell if someone overrode your "default" in the code.

The only way I can think of around it, is to use the colModel to set the class names for each column, or use a "default" name otherwise.

jack.slocum
11 Oct 2006, 5:48 PM
Only 1 element can have a particular ID according to the spec. If there is more than one I'm not sure what happens in each browser when trying to access it (them) via DOM.

With that said it still wouldn't work because even if document.getElementById('grid1-col-0') returned an array of all the cells (which it may) you would still have to loop through every cell and change it's position + width values on updates. Supposed for example the user resizes column 0. Every cell in the grid would have to be reset with a new position. That's just too slow, even if it did work.

Changing the rules is the correct approach. It is fast because it lets the browser do all the processing. If I could create the rules (Nige "Animal" sent me code he wrote that can do it, but it needs to be tested on Safari) then we'd have the best of both worlds.

devol
11 Oct 2006, 6:47 PM
Understood. I didn't see all the issues until I poked around a bit. Specifying in the column definition wouldn't either for the resize reasons you just described.

Comforting, isn't it, that this javascript stuff is so new that it should all be fixed shortly. I mean, its not like it has been around for years and has almost no possibility of changing...

jack.slocum
11 Oct 2006, 7:01 PM
If I can find a way to test that CSS creation code in Safari, it could fix this problem instantly.

Animal
11 Oct 2006, 11:13 PM
Worst case I can dynamically generate the css for the page. Kind of ugly though.

I have this code in my grid creation:



// Create stylesheet rules for this specific grid.
this.stylesheet = new CSSStyleSheet();
for (var i = 0; i < this.columnModel.getColumnCount(); i++)
{
this.stylesheet.addRule("#" + this.container.id + " .ygrid-col-" + i, "");
}


My CSSStyleSheet object is listed below. I've only tested the dynamic style creation in IE, FF and Opera.



/**
An object which encapsulates a dynamically created, modifiable stylesheet.
*/
function CSSStyleSheet()
{
/**
* The array of rules for this stylesheet.
* @private
*/
this.rules = [];

/**
* An associative array, keyed by the selector text containing the rule index number for
* the rule for that selector text.
* @private
*/
this.ruleIndex = [];

if (document.createStyleSheet)
{
this.sheet = document.createStyleSheet();
}
else
{
this.styleElement = document.createElement("style");
document.getElementsByTagName("head")[0].appendChild(this.styleElement);
this.sheet = this.styleElement.styleSheet ? this.styleElement.styleSheet : this.styleElement.sheet;
}
}

/**
Create a style rule in the stylesheet.
@param selectorText The CSS selector text.
@param ruleText The style specification with or without braces.
*/
CSSStyleSheet.prototype.addRule = function(selectorText, ruleText)
{
var result;

// Opera, and other browsers with no DOM stylesheet support
if (!this.sheet)
{
// Remove braces.
ruleText = ruleText.replace(/^\{?([^\}])/, "$1");

// If it exists, modify it.
if (!this.ruleIndex[selectorText])
this.ruleIndex[selectorText] = this.rules.length;
this.rules[this.ruleIndex[selectorText]] = ruleText;

// Build the innerHTML of the &lt;style> element from our rules.
var cssText = "";
for (var sel in this.ruleIndex)
cssText = sel + " {" + this.rules[this.ruleIndex[sel]] + "}";
this.styleElement.innerHTML = cssText;
}

// DOM standard. Result object contains looks like {cssText:selectorText + " " + ruleText}
// cssText property is readonly. deleteRule(ruleIndex} must be used to remove.
else if (this.sheet.insertRule)
{
// insertRule() requires braces
if (!/^\{[^\}]*\}$/.test(ruleText))
ruleText = "{" + ruleText + "}";

var r = this.sheet.cssRules.length;
this.sheet.insertRule(selectorText + " " + ruleText, r);
result = this.sheet.cssRules[r];
this.ruleIndex[selectorText] = r;

if (this.rules.length == 0)
this.rules = this.sheet.cssRules;
}

// IE.
// Each rule object has a style property which contains the style attributes.
else if (this.sheet.addRule)
{
// addRule() requires no braces
ruleText = ruleText.replace(/^\{?([^\}])/, "$1");
var r = this.sheet.rules.length;
if (ruleText.length == 0)
ruleText = " "
this.sheet.addRule(selectorText, ruleText);
result = this.sheet.rules[r];
this.ruleIndex[selectorText] = r;

if (this.rules.length == 0)
this.rules = this.sheet.rules;
}
else
{
alert("Cannot create rule");
}
return result;
};

/**
* Change a style property in a rule.
* @param selectorText The identifier of the rule to change
* @param property The name of the style property to change
* @param value The new value of the style property.
*/
CSSStyleSheet.prototype.changeRule = function(selectorText, property, value)
{
var index = this.ruleIndex[selectorText];

// If the rule is not present, create it.
if (typeof index == "undefined")
{
this.addRule(selectorText, property + ":" + value);
}

// Opera, and other browsers with no DOM stylesheet support
if (!this.sheet)
{
var cssText = this.rules[index];
if (cssText)
{
var propSearch = new RegExp("^(.*" + property + "\\s*\:\\s*)([^;]*)(.*)$");
var ruleText = propSearch.exec(cssText);
// If the value was in the old rule...
if (ruleText)
{
// And it was different...
if (ruleText[4] != value)
{
this.rules[index] = ruleText[1] + value + ruleText[3];
}
}
else
{
this.rules[index] = cssText + "; " + property + ": " + value + ";";
}

// Rebuild the innerHTML of the <style> element from our rules.
cssText = "";
for (var sel in this.ruleIndex)
cssText = sel + " {" + this.rules[this.ruleIndex[sel]] + "}";
this.styleElement.innerHTML = cssText;
}

var cssText = "";
var cssText = "";
for (var sel in this.ruleIndex)
cssText = sel + " {" + this.rules[this.ruleIndex[sel]] + "}";
}

// IE. Easy
else if (this.sheet.addRule)
{
var style = this.rules[index].style;
property = property.replace(/-([a-z])/gi, function(m0, m1) {return m1.toUpperCase();});
style[property] = value;
}

// DOM standard. We must parse the rule, delete, and create a new one.
else if (this.sheet.insertRule)
{
var oldRule = this.rules[index];
if (oldRule)
{
var cssText = oldRule.cssText;
var propSearch = new RegExp("^[^\\{]*(\\{.*" + property + "\\s*\\:\\s*)([^};]*)([^}]*})");
var ruleText = propSearch.exec(cssText);

// If the value was in the old rule...
if (ruleText)
{
// And it was different...
if (ruleText[4] != value)
{
cssText = ruleText[1] + value + ruleText[3];
this.sheet.deleteRule(index);
this.sheet.insertRule(selectorText + " " + cssText, index);
}
}
else
{
var propSearch = new RegExp("\\{([^}]*)}");
ruleText = propSearch.exec(cssText);
cssText = "{ " + ruleText[1] + "; " + property + ": " + value + " }";
this.sheet.deleteRule(index);
this.sheet.insertRule(selectorText + " " + cssText, index);
}
}
}
};

CSSStyleSheet.prototype.getRuleProperty = function(selectorText, property)
{
var index = this.ruleIndex[selectorText];

// If the rule is not present, create it.
if (typeof index == "undefined")
{
return;
}

// Opera, and other browsers with no DOM stylesheet support
if (!this.sheet)
{
var cssText = this.rules[index];
if (cssText)
{
var propSearch = new RegExp("^.*" + property + "\s*\:\s*([^;]*)");
var ruleText = propSearch.exec(cssText);

// If the value was in the old rule...
if (ruleText)
{
return ruleText[1];
}
}
}

// IE. Easy
else if (this.sheet.addRule)
{
var style = this.rules[index].style;
property = property.replace(/-([a-z])/gi, function(m0, m1) {return m1.toUpperCase();});
return style[property];
}

// DOM: We must parse the rule cssText.
else if (this.sheet.insertRule)
{
var oldRule = this.rules[index];
if (oldRule)
{
cssText = oldRule.cssText;
var propSearch = new RegExp("^.*" + property + "\\s*\\:\\s*([^};]*)");
var ruleText = propSearch.exec(cssText);

// If the value was in the old rule...
if (ruleText)
{
return ruleText[1];
}
}
}
};

devol
12 Oct 2006, 4:28 AM
If you pop up a demo page I can check it in safari and webkit (safari beta) real quick. I'm on a mac here.

Animal
12 Oct 2006, 6:03 AM
Can't do that, sorry. I only have the app running inside our network - nobody from outside can get in.

devol
12 Oct 2006, 7:18 AM
FYI. The grid is REALLY slow to render with 100 columns. Once its rendered its quick, unless you want to resize a column.

devol
13 Oct 2006, 9:37 PM
Animal I tried making that code work and I can't even get it to add a class in Firefox, much less Safari. Maybe I'm setting things up wrong, I'll post up a sample page tomorrow.