Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Suggestions for dealing with IE's terrible Javascript / Dom accessing engine

I'm working on a site that has a page that can potentially become very long. It can have in theory 1000+ rows of data in it. Then these rows could each have sub-rows.

I'm currently using a list / sublist structure to represent the data because it's difficult to visualize subsets in tables. the subset data is in a table though.

The Problem is this: In IE. my hovering over the row styles, tooltips and other javascripts are taking upwards of 5 seconds to fire! The page hangs like crazy and is pretty much unuseable. In FF, Chrome, and Safari it works just fine.

I don't need a whole bunch of stats saying IE is slow. i know it is. what i need is some suggestions / theories/ ideas on how to combat the slowness!

Some of the ideas i've had so far: -some kind of paging mechanism. But that gets tricky b/c 1. It's a list not a table, and 2. it's got sub sub lists and sub tables for those sub lists. so we'd need to somehow page based on the top level i think. because we don't want to split the data up at all. I guess that might be feasible...

-some kind of mechanism that holds the content in a javascript array or object or soomething and doesn't add it to the dom until it's been scrolled to. and takes it away when you scroll past it. in theory this would be cool. but i think it would be horrible to impliment.

-something else entirely?

Thanks in Advance for any ideas! I'm probably going to try and put a bounty on this as soon as i'm able to help inspire you ;)

EDIT:

OH MY! I think i may have found part of the problem using dyna trace! thanks Pointy for reminding me about this tool. i had forgotten about out. the click event of one of my buttons seems to be firing an insane amount. like 2000 times and count. something is clicking it automatically or something crazy like that. weird thing is, i don't have any javascripts that trigger that button click! EDIT: This doesn't seem to be an issue anymore.

EDIT:

Also: i don't think it is the complexity of my markup that is affecting the responsiveness of my scripts(i know if i could use just id selectors it would be best, but i've optimized my selectors and qtips as much as possible (with help from s.o. in the past). i have a button that is just on the page, not nested in anything, that the click handler takes 4 or 5 seconds to register. It's not that the click action takes a long time, it's just slow to notice that it's been clicked.

EDIT: i had 5 spans that were each nested in a span unnecessarily, i un-nested those and it seems to have cut the slowness in about 1/2!! (these 5 spans were repeated in every row)

EDIT: Here's some of the relevant scripts:

The Main Button Handler:

$('#FilterScheduledShifts').click(function () {
  var categoryId = $('#CategoryId').val();
  var activityId = $('#ActivityId').val();
  var shiftStatusFilters = GetShiftStatusFilterIds();
  var dayOfWeekFilters = GetDayOfWeekFilters();
  var startDateFilter = GetStartDateFilter();
  var endDateFilter = GetStartEndFilter();
  var dataToPost = {
    categoryId: categoryId,
    activityId: activityId,
    statuses: shiftStatusFilters,
    daysOfWeek: dayOfWeekFilters,
    startDate: startDateFilter,
    endDate: endDateFilter
  };
  var url = $('#UrlToFilter').val();
  $('#ListHolder').html("<%=web.loading %>");
  $.ajax({
    url: url,
    data: dataToPost,
    type: 'POST',
    success: function (data) {
      document.getElementById('ScheduledActivityShiftListHolder').innerHTML = data;
    },
    error: function () {
      v2ErrorNotice(v2Text.shared.genericAjaxErrorMessage);
    }
  });
});

The Get...FIlter functions are very simple functions that run in a few milliseconds... nothing interesting or noteworthy happening in there.

this bit attaches some of the behaviour that is very slow: in other pages they react nice and quick, on this page... 2-3 seconds.

$('span.infoCard').die();
$('span.infoCard').live("mouseover", function () {

  var $this = $(this);
  if (!$this.data("toolTipAttached")) {

    var title = '';

    if ($this.attr('infoCardTitle') != "") {
      title = $this.attr('infoCardTitle');
    } else {
      title = $this.html();
    }

    $this.qtip({
      content: {
        text: "loading",
        title: title,
        ajax: {
          url: $this.attr('urlToGet')
          // data: data,
        }
      },
      position: {
        at: 'right middle',
        my: 'left top',
        adjust: {
          screen: 'flip'
        }
      },
      hide: {
        fixed: true,
        when: 'mouseout'
      },
      show: {
        solo: true,
        delay: 500,
        ready: true
      },
      style: {
        classes: 'ui-tooltip-light InfoCardTip',
        widget: true,
        tip: {
          corner: true,
          width: 15,
          height: 15
        }
      }
    });
    $this.data("toolTipAttached", true);
  }
});

There are 2 different versions of this for 2 different classes, one gets a single "shift" one gets the user s for a bunch of shifts. as with other things. once the function itself runs quick, the ajax is fast, the v2ReLoadScheduledActivitySection runs in about 50 ms. nothing interesting in there. :

$('div.GetShiftUserArrow').die('click');
$('div.GetShiftUserArrow').live('click', function () {
  var listIsVisible = $(this).attr('listIsVisible');
  var holder = $('#' + $(this).attr('listHolder'));
  var activityHolder = $('#TopLevel{0}'.format($(this).attr('activityShiftId')));

  if (listIsVisible == -1) {
    v2ReLoadScheduledActivitySection($(this), 1);
  } else {
    holder.empty().toggle('hide');
    $(this).removeClass('GetShiftUserArrowOpen');
    $(this).attr('listIsVisible', listIsVisible * -1);
  }
});

Here's a bit of the HTML. this is the

  • for a day... the main list has one of these for every day of the year (or whatever range they pick). There could be any number of sub li's in that list, but it tends to be between 1-10.
    <li class="EntityRow" style="font-weight:normal;">
     <div class="TopLevelRow" sectiondate="1/21/2011 12:00:00 AM">
      <div class="ScheduleDayHeader OnlyFloatLeftInIE7" style="width:100%;margin-bottom:0px;font-weight:normal;">
       <div class="Buttons OnlyFloatLeftInIE7" style="padding-top:0px; padding-bottom:0px;margin-top:0px;margin-bottom:0px;">
        <div class="GetDayUserArrow OnlyFloatLeftInIE7" listisvisible="-1" sectiondate="1/21/2011 12:00:00 AM" url="/This/IsNot/TheReal/Url"></div>
       </div><span class="CategorizedNameHolder OnlyFloatLeftInIE7" style="display:inline-block; width:380px;padding-bottom:2px;">Friday, January 21, 2011</span> <span class="TimeHolderSpan">Start</span> <span class="TimeHolderSpan">End</span> <span class="StatusIcons">Status</span> <span class="SkinnyColumn GlossaryMe" tooltip="A Number">Mn</span> <span class="SkinnyColumn GlossaryMe" tooltip="A Number">Mx</span> <span class="SkinnyColumn GlossaryMe" tooltip="A Number">BU</span> <span class=
       "SkinnyColumn GlossaryMe" tooltip="A Number">Av</span> <span class="SkinnyColumn GlossaryMe" tooltip="Assigned">As</span> <span class="SkinnyColumn GlossaryMe" tooltip="A Number">Co</span>
      </div>
    
      <div class="ShiftListHolder" sectiondate="1/21/2011 12:00:00 AM">
       <ul class="ScheduledActivitiesForDayList EntityList FancyList SubList">
        <li class="EntityRow" activityshiftid="4645" id="ListItemFor4645">
         <div class="BlueOnHover ScheduleShiftHeader" style="width:100%;">
          <div class="Buttons OnlyFloatLeftInIE7" style="padding-top:0px; padding-bottom:0px;margin-top:0px;margin-bottom:0px;">
           <div class="GetShiftUserArrow OnlyFloatLeftInIE7" listisvisible="-1" activityshiftid="4645" url="/Some/Url=4645" sectiondate="1/21/2011 12:00:00 AM"></div>
          </div><span class="CategorizedNameHolder OnlyFloatLeftInIE7" style="display:inline-block; width:350px;padding-bottom:2px;"><span class="AssignLink" activityshiftid="4645">SomeText</span></span> <span class="TimeHolderSpan">9:00 AM</span> <span class="TimeHolderSpan">12:30 PM</span>
    
          <div class="StatusIcons OnlyFloatLeftInIE7">
           <div class='TipMe LegendMe ClassToPickTheIcon' tooltip='Min # scheduled' legendgroupid='5'>
            <span class='NoDisplay'>3</span>
           </div>
    
           <div class='TipMe LegendMe OtherClassToPickTheIcon' tooltip='Unlocked' legendgroupid='5'>
            <span class='NoDisplay'>2</span>
           </div>
    
           <div class='TipMe LegendMe AnotherClassToPickTheIcon' tooltip='Do not auto assign' legendgroupid='5'>
            <span class='NoDisplay'>1</span>
           </div>
    
           <div class='TipMe LegendMe ClassToPickTheIconAgain' tooltip='Do not autolock' legendgroupid='5'>
            <span class='NoDisplay'>1</span>
           </div>
          </div><span class="OnlyFloatLeftInIE7"><span class="SkinnyColumn TipMe ContentToolTip" tooltip="A Number">1</span> <span class="SkinnyColumn TipMe ContentToolTip" tooltip="A Number">--</span> <span class="SkinnyColumn TipMe ContentToolTip" tooltip="A Number">0</span> <span class="SkinnyColumn TipMe ContentToolTip" tooltip="A Number">0</span> <span class="SkinnyColumn TipMe ContentToolTip" tooltip="Assigned">2</span> <span class="SkinnyColumn TipMe ContentToolTip" tooltip=
          "A Number">0</span></span>
         </div>
    
         <div class="UserListHoder" id="UserListHolder4645" activityshiftid="4645"></div>
        </li>
    
        <li class="EntityRow" activityshiftid="4129" id="ListItemFor4129">
         <div class="BlueOnHover ScheduleShiftHeader" style="width:100%;">
          <div class="Buttons OnlyFloatLeftInIE7" style="padding-top:0px; padding-bottom:0px;margin-top:0px;margin-bottom:0px;">
           <div class="GetShiftUserArrow OnlyFloatLeftInIE7" listisvisible="-1" activityshiftid="4129" url="/Some/Url=4129" sectiondate="1/21/2011 12:00:00 AM"></div>
          </div><span class="CategorizedNameHolder OnlyFloatLeftInIE7" style="display:inline-block; width:350px;padding-bottom:2px;"><span class="AssignLink" activityshiftid="4129">Blah Bla blah</span></span> <span class="TimeHolderSpan">9:00 AM</span> <span class="TimeHolderSpan">5:00 PM</span>
    
          <div class="StatusIcons OnlyFloatLeftInIE7">
           <div class='TipMe LegendMe ClassToPickTheIcon' tooltip='Min # scheduled' legendgroupid='5'>
            <span class='NoDisplay'>3</span>
           </div>
    
           <div class='TipMe LegendMe OtherClassToPickTheIcon' tooltip='Unlocked' legendgroupid='5'>
            <span class='NoDisplay'>2</span>
           </div>
    
           <div class='TipMe LegendMe AnotherClassToPickTheIcon' tooltip='Do not auto assign' legendgroupid='5'>
            <span class='NoDisplay'>1</span>
           </div>
    
           <div class='TipMe LegendMe OtherClassForIcon' tooltip='on availabilities' legendgroupid='5'>
            <span class='NoDisplay'>2</span>
           </div>
          </div><span class="OnlyFloatLeftInIE7"><span class="SkinnyColumn TipMe ContentToolTip" tooltip="A Number">1</span> <span class="SkinnyColumn TipMe ContentToolTip" tooltip="A Number">5</span> <span class="SkinnyColumn TipMe ContentToolTip" tooltip="A Number">0</span> <span class="SkinnyColumn TipMe ContentToolTip" tooltip="A Number">0</span> <span class="SkinnyColumn TipMe ContentToolTip" tooltip="Assigned">5</span> <span class="SkinnyColumn TipMe ContentToolTip" tooltip=
          "A Number">0</span></span>
         </div>
    
         <div class="UserListHoder" id="UserListHolder4129" activityshiftid="4129"></div>
        </li>
    
        <li class="EntityRow" activityshiftid="3534" id="ListItemFor3534">
         <div class="BlueOnHover ScheduleShiftHeader" style="width:100%;">
          <div class="Buttons OnlyFloatLeftInIE7" style="padding-top:0px; padding-bottom:0px;margin-top:0px;margin-bottom:0px;">
           <div class="GetShiftUserArrow OnlyFloatLeftInIE7" listisvisible="-1" activityshiftid="3534" url="/Some/Url=3534" sectiondate="1/21/2011 12:00:00 AM"></div>
          </div><span class="CategorizedNameHolder OnlyFloatLeftInIE7" style="display:inline-block; width:350px;padding-bottom:2px;"><span class="AssignLink" activityshiftid="3534">even more</span></span> <span class="TimeHolderSpan">1:00 PM</span> <span class="TimeHolderSpan">4:30 PM</span>
    
          <div class="StatusIcons OnlyFloatLeftInIE7">
           <div class='TipMe LegendMe SchedulingFullyA NumberIcon' tooltip='Min # A Number' legendgroupid='5'>
            <span class='NoDisplay'>4</span>
           </div>
    
           <div class='TipMe LegendMe LockedIcon' tooltip='Locked' legendgroupid='5'>
            <span class='NoDisplay'>1</span>
           </div>
    
           <div class='TipMe LegendMe ClassToPickTheIconAgain' tooltip='Auto assign' legendgroupid='5'>
            <span class='NoDisplay'>2</span>
           </div>
    
           <div class='TipMe LegendMe OtherClassForIcon' tooltip='on assignments' legendgroupid='5'>
            <span class='NoDisplay'>3</span>
           </div>
          </div><span class="OnlyFloatLeftInIE7"><span class="SkinnyColumn TipMe ContentToolTip" tooltip="A Number">1</span> <span class="SkinnyColumn TipMe ContentToolTip" tooltip="A Number">5</span> <span class="SkinnyColumn TipMe ContentToolTip" tooltip="A Number">0</span> <span class="SkinnyColumn TipMe ContentToolTip" tooltip="A Number">0</span> <span class="SkinnyColumn TipMe ContentToolTip" tooltip="Assigned">7</span> <span class="SkinnyColumn TipMe ContentToolTip" tooltip=
          "A Number">3</span></span>
         </div>
    
         <div class="UserListHoder" id="UserListHolder3534" activityshiftid="3534"></div>
        </li>
       </ul>
      </div>
     </div>
    </li>
     ​
    

    anything with TipMe, or GlossaryMe brings up a qtip tooltip.

    like this:

     $(".TipMe").live("mouseover", function () {
      var $this = $(this);
      $this.removeAttr('title');
      $this.removeAttr('alt');
    
      if (!$this.data("toolTipAttached")) {
    
        $this.qtip({
          show: {
            ready: true,
            solo: true,
            delay: 500
          },
          style: {
            classes: 'ui-tooltip-light InfoCardTip',
            widget: true
          },
          content: $this.attr('tooltip'),
          position: {
            at: 'bottomRight',
            my: 'topleft',
            adjust: {
              screen: 'flip',
              x: 0,
              y: 0
            }
          },
          hide: {
            fixed: true,
            inactive: 1000
          }
        });
        $this.data("toolTipAttached", true);
        //    $this.trigger("mouseover");
      }
    });
    

    Something tells me it might be all my tooltip handlers that are causing the problem...

    Let me know if you guys want anything else!

  • like image 556
    Patricia Avatar asked Jan 17 '11 20:01

    Patricia


    People also ask

    What are the issues with JavaScript?

    These days, most cross-browser JavaScript problems are seen: When poor-quality browser-sniffing code, feature-detection code, and vendor prefix usage block browsers from running code they could otherwise use just fine. When developers make use of new/nascent JavaScript features, modern Web APIs, etc.)

    How does JavaScript access the DOM?

    Accessing a DOM element By ID: JavaScript can find HTML elements in the DOM based on the "id" of the element. The document object provides a method "getElementById()" to accomplish this task.


    2 Answers

    "I don't think it is the complexity of my markup that is affecting the responsiveness of my scripts"

    False assumptions are the worst leaders of the mind. It's interesting because you also said:

    "this bit attaches some of the behaviour that is very slow, in other pages they react nice and quick, on this page... 2-3 seconds."

    "i had 5 spans that were each nested in a span unnecessarily, i un-nested those and it seems to have cut the slowness in about 1/2!! (these 5 spans were repeated in every row)"

    There you go. :)

    What you can try is to change live handlers to delegate. The main difference between them is that live attaches The Handler to the document, while in case of delegate you can specify where to attach it. I think this can be the tipping point. Also try mouseenter which fires less frequently.

    $('#ROOT').delegate(".infoCard", "mouseenter", function () { ... })
    

    root can be any element depending on your site. You can try the parent <ul> for example.

    Another thing to try is to use your own handler (but you can't use mouseenter then):

    $("#ROOT").mouseover(function (e) {
        var $this = $(e.target);
        if ($this.hasClass('TipMe')) {
            // TipMe code...
        } else if ($this.hasClass('infoCard')) {
            // infoCard code...
        }
    });
    

    Let me know if any of them helped! :)

    like image 159
    25 revs, 4 users 83% Avatar answered Sep 23 '22 19:09

    25 revs, 4 users 83%


    Hard to give a concrete answer without knowing what your code looks like.

    If you're just doing a little style change on hover, then I'd suggest you use CSS instead of javascript to accomplish it.

    The :hover pseudo selector won't work on a <tr> in IE6, but it will work in the other browsers.

    #myTable tr:hover {
        background: yellow;
    }
    

    For those items that require javascript, we would really need to see the code to know why it is slow. It sounds like you may be doing unnecessary DOM selection.

    Even if performance can be improved via pagination or something similar to reduce the size, it could be that you still have some inefficient code that should be tightened up a bit.

    like image 24
    user113716 Avatar answered Sep 22 '22 19:09

    user113716