views:

511

answers:

3

I don't understand why the vertical scroll bar moves automatically to the most top position when "Line 9" clicked, for example. Further clicks does not move the scroll bar. Could anyone explain why, and how to fix this ? I work with Firefox 3.6.3.

HTML:

<html>
    <head>
        <link rel="stylesheet" href="test.css" type="text/css" />
        <script src="http://code.jquery.com/jquery-latest.js"&gt;&lt;/script&gt;
        <script language="JavaScript" src="test.js"></script>
    </head>

    <body>
        <div>
            <table>
                <tr row='0'><td class='column1'>Line 0</td></tr>
                <tr row='1'><td class='column1'>Line 1</td></tr>
                <tr row='2'><td class='column1'>Line 2</td></tr>
                <tr row='3'><td class='column1'>Line 3</td></tr>
                <tr row='4'><td class='column1'>Line 4</td></tr>
                <tr row='5'><td class='column1'>Line 5</td></tr>
                <tr row='6'><td class='column1'>Line 6</td></tr>
                <tr row='7'><td class='column1'>Line 7</td></tr>
                <tr row='8'><td class='column1'>Line 8</td></tr>
                <tr row='9'><td class='column1'>Line 9</td></tr>
            </table>
        </div>
    </body>
</html>

JS:

$(document).ready(function() {
    $(".column1").each(function(index) {
        $(this).after("<td class='column2'>Details " + index + "</td>");
        $(this).toggle(function() { $("[row='" + index + "'] .column2").fadeIn("fast") },
                       function() { $("[row='" + index + "'] .column2").fadeOut("fast") });
    });
});

CSS:

div {
    overflow: auto;
    height: 100px;
    width: 300px;
    border: 1px solid blue;
}

.column1 {
    cursor: pointer;
    width: 100px;
    background-color: green;
    color: white;
}

.column2 {
    display: none;
    width: 200px;
    background-color: blue;
    color: white;
}
+1  A: 

I copied and tried your code, on Firefox 3.6.3 and Chrome 5.0.375.29. And saw nothing what you described so I am at loss.

Scrollbar moved only when scrolling normally, not when clicking on the text.

Mattijle
Very strange... Are you sure that you clicked on "Line 9" ? Here are the steps to see the problem: (1) (Re)Load the page (2) Move the scroll bar to see "Line 9" (3) Click on "Line 9". After this click the scroll bar is moved up automatically such that I see only "Line 0", "Line 1", "Line 2", and "Line 3". Am I the only one that experiencing this problem ?
Misha Moroshko
@Misha Ah, now that I followed your instructions I see it.However to fix it, I changed the .fadeIn() and .fadeOut to.css("display", "block") and .css("display", "none") and the scrolling started to work as intented.
Mattijle
Mattijle: Setting table cells to 'display:block' will side-step the problem, but cause other issues, as the cells are no longer effectively part of the table structure. I think this simple example doesn't show up these problems, but it isn't a good idea in a real life scenario. When you fadeIn() or fadeOut() a table cell with jQuery, jQuery toggles the display between 'none' and 'table-cell' which is correct.
Beejamin
A: 

You should update your browser or try with another browser.

SkipSoft
-1: this answer is not helpful. He's looking for why it happens, and I believe 3.6 is the last version of firefox. Why should he move to another browser without being sure that it's a problem of his browser and not the code?
ANeves
Folks, please don't mass-downvote new people. A total of -1 and an explanation in a comment would be enough, any more will just make him feel harassed and unwelcome and probably cut down on his willingness to be a part of SO and improve.
ANeves
+5  A: 

After doing some trial and error tests, it looks like this is related to the moment that the browser recalculates and redraws the table when you fade in/fade out one of the cells. There's nothing wrong with your code, and jQuery is correctly toggling the 'display' property of the cell - it looks like it's a minor bug in FF.

Probably the easiest way around it is to avoid toggling table cells themselves, and instead toggle the contents of the column2 cell, like so:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;
<html>
    <head>
        <link rel="stylesheet" href="test.css" type="text/css" />
        <script src="http://code.jquery.com/jquery-latest.js"&gt;&lt;/script&gt;
        <script language="JavaScript">
        $(document).ready(function() {
            $("td.column1").each(function(index) {
                $(this).after('<td class="column2"><span class="details">Details ' + index + '</span></td>');
                $(this).toggle(
                  function(){$(this).siblings('.column2').children('span.details').fadeIn("fast")},
                  function(){$(this).siblings('.column2').children('span.details').fadeOut("fast")}
                ) 
            });
        });
        </script>
        <style type="text/css" media="screen">
          div {
              overflow: auto;
              height: 100px;
              width: 300px;
              border: 1px solid blue;
          }

          .column1 {
              cursor: pointer;
          }

          .column2 .details{
              display:none;
          }

        </style>
    </head>

    <body>
        <div>
            <table>
                <tr row='0'><td class='column1'>Line 0</td></tr>
                <tr row='1'><td class='column1'>Line 1</td></tr>
                <tr row='2'><td class='column1'>Line 2</td></tr>
                <tr row='3'><td class='column1'>Line 3</td></tr>
                <tr row='4'><td class='column1'>Line 4</td></tr>
                <tr row='5'><td class='column1'>Line 5</td></tr>
                <tr row='6'><td class='column1'>Line 6</td></tr>
                <tr row='7'><td class='column1'>Line 7</td></tr>
                <tr row='8'><td class='column1'>Line 8</td></tr>
                <tr row='9'><td class='column1'>Line 9</td></tr>
            </table>
        </div>
    </body>
</html>

So, the script adds the column2 cell, and that stays visible the whole time - instead we show/hide the <span class="details"> within it. I've tested this version in FF 3.6.3 and it behaves as it should!

Oh - and I cleaned up your jQuery selectors for better performance. If you want more info on why, let me know!

Beejamin
Nice workaround, but it does not solve my problem completely. In my case `.column2` has a fixed `width` and a `background-color`. If you add these attributes to the CSS code you'll see the `background-color` even when the details span is not displayed. I will edit the question accordingly. Also, please let me know why your jQuery selectors have better performance than mine.
Misha Moroshko
It appears in Opera as well.
Gert G
OK - I think, in order to remove the source of the problem, column2 is going to have to be visible at all times. My belief is that it's the showing and hiding of the cell that is the direct cause - here's why:Unlike most elements, TD's have inter-dependencies with elements around them. The size of one cell can depend on the widths of both other cells in the same row, and others in the same column. If you add a new cell (via .fadeIn()), the browser needs to recalculate the dimensions of some or all of the other cells in the table. (continued)
Beejamin
...when this happens, it appears FF loses its 'place' in the scrolling container, and returns you to the top. This is only my theory, of course, but it does seem to match all the facts.So - what to do? Can you apply the styles currently on .column2 to .column2 .details? Probably the only thing required on .column2 itself is the width (to avoid redraw of the table). Failing that, perhaps you could markup the content without using a table. What sort of data is it? It is tabular, right?
Beejamin
As far as jQuery selector performance goes, mine are more specific, and work as a series of fast operations on small collections of nodes.In particular, your `$("[row='" + index + "'])` selector requires inspecting *every element in the whole page* looking for the right row attribute. It's always more efficient to search based on a known node: in this case, we know the TD that was clicked on, so we start our search from there, first getting the siblings (which is a browser-native DOM operation), then searching their children (also DOM native).
Beejamin
Setting the width of column2 does not help. Finally, I used `scrollTo()` function to get the scroll bar value before the fading operation, and then set it back after the fading. It's not an ideal solution because there is some flickering effect, but it is also not too bad.
Misha Moroshko
Setting the width of column2 will not help if you still show/hide column2 via toggling the display to 'none' and back (via fadeIn() and fadeOut() in this case).
Beejamin