Contents tagged with JavaScript

  • 4 pixels, apparently.

    I have some JavaScript code that uses jQuery to get the height of a web page to determine whether we should do some auto-scrolling as we add new content to the page. I do this by comparing the height of the document and the window like this:

    var $win = $(window);
    var currentPos = $win.height() + $win.scrollTop();
    
    if (currentPos == $(document).height()) {
        // The user is at the bottom of the page.
        shouldScroll = true;
    } else {
        shouldScroll = false;
    }

    Unfortunately, Internet Explorer 8 acts weird when it comes to comparing the height of window and document. When there's no scroll bar on the page, the height of the document is always 4 pixels more than the height of the window. You can see this happening even on the jQuery documentation for height. Chrome, Firefox and IE9 will report 125px for both document and window height. IE8 will report the window height as 121px.

    So, $win.height() + $win.scrollTop() is always 4 pixels less than $(document).height() which causes my scrolling feature to kick in, even when there's no scrolling to do. As a result, I had to turn that elegant piece of code into this monstrosity just because of IE 8:

    // This first check is an ugly hack to deal with the fact that
    // IE 8 reports document height to be window height + 4 pixels
    // in the absence of a scroll bar.
    if ($win.scrollTop() == 0) {
        if (currentPos + 4 >= $(document).height()) {
            // User is at the bottom of the page
            scrollResult = true;
        } else {
            scrollResult = false;
        }
    } else {
        // Just the following code would be sufficient if it weren't
        // for IE 8.
        if (currentPos == $(document).height()) {
            // The user is at the bottom of the page.
            scrollResult = true;
        } else {
            scrollResult = false;
        }
    }
  • Recently I came across a requirement to create an ATL ActiveX control that could be used from JavaScript in the following manner:

    o = new ActiveXObject("Test.MyControl");
    o.OnStateChanged = MyEventHandler;
    

    I figured this would be pretty simple -- just implement an ActiveX control with connection points. Unfortunately, it doesn't work. Internet Explorer complains thus:

    ie7_object_error 

    This is because JavaScript can't sink COM events. This seemed odd to me, because the same approach works when you try to use the XMLHttpRequest object. As it turns out, XMLHttpRequest doesn't implement "events" as COM events at all. They're actually properties that accept IDispatch pointers. JavaScript functions are marshalled to ActiveX as objects that implement IDispatch and having a method named "call".

    Simply put, this is what we need to do to enable ActiveX events to be handled by JavaScript using the syntax shown above:

    1) Implement a property in the ActiveX control that accepts an IDispatch pointer.

    STDMETHODIMP CTestCtrl::putref_OnStateChanged(IDispatch* newVal)
    STDMETHODIMP CTestCtrl::get_OnStateChanged(IDispatch** pVal)
    

    2) Get the DISPID of the "call" method by calling GetIDsOfNames like so:

    newVal->GetIDsOfNames(IID_NULL, &szMember, 1, LOCALE_SYSTEM_DEFAULT, pDispId))

    3) When you want to invoke the event, construct a VARIANTARG containing the parameters you want to pass to the event in reverse order. Add a pointer to your own IDispatch implementation as the last element in the array.

    DISPPARAMS dispParams;
    VARIANTARG args[2];
    
    VariantInit(&args[0]);
    args[0].vt = VT_BSTR;
    args[0].bstrVal = ::SysAllocString(L"The event says hello!");
    
    IDispatch* pDisp;
    HRESULT hRes = this->QueryInterface(IID_IDispatch, (void**)&pDisp);
    
    VariantInit(&args[1]);
    args[1].vt = VT_DISPATCH;
    args[1].pdispVal= pDisp;
    
    memset(&dispParams, 0, sizeof(dispParams));
    dispParams.rgvarg = args;
    dispParams.cArgs = sizeof(args)/sizeof(args[0]);
    
    EXCEPINFO excepinfo; 
    UINT uArgErr;
    
    pEventHandler->Invoke(*pDispId, IID_NULL, LOCALE_SYSTEM_DEFAULT, 
        DISPATCH_METHOD, &dispParams, NULL, &excepinfo, &uArgErr);
    

    4) Clean up when you're done by calling VariantClear on both elements of the args array.