8

I've a VF Page which uses a Apex StandardSetController() to display the list of a Standard Object Records(Case in my Page). The mini page layout which is a standard functionality from Salesforce has some CSS issues.

On Hover, the mini page layout appears as expected but the position is absolute. I've scroll-able view, so when I hover on records after scrolling(on those which were hidden previously), the mini page layout is shown at the absolute position and not at the current position.

I'm sure that it has nothing to do with the StandardSetController(). Is there a CSS patch for this situation. Will it be a proper way to alter the Standard CSS since the classes and the CSS might be changed from the Salesforce side in a newer releases?

  • Have you tried submitting a bug for this? Seems like a pretty valid use case that should be handled by Salesforce already – Ralph Callaway Feb 15 '13 at 16:36
  • You mean by raising a case? No. I didn't. – Vignesh Damodharan Feb 15 '13 at 17:26
  • Correct, definitely helpful to have a workaround the answer suggested, but this feels like something salesforce should be fixing – Ralph Callaway Feb 15 '13 at 19:18
3

I've run into this issue before as well. It seems to be a combination of Javascript and CSS. Looking at their Javascript, this is the function that appears to be called

function LookupHoverDetail(a, b) {
    this.id = a;
    this.width = LookupHoverDetail.STANDARD_BUBBLE_WIDTH;
    this.bubbleOffset = Sfdc.userAgent.isIE6 ? 5 : 14;
    this.height = LookupHoverDetail.STANDARD_BUBBLE_HEIGHT;
    this.hover = document.createElement("div");
    this.hover.id = a + "Hover";
    this.hover.className = "individualPalette lookupHoverDetail lookupHoverDetailLoading lookupHoverDetailOverridable";
    this.hover.innerHTML = '<div class="topLeft"><div class="bPageBlock"><div class="pbBody">' + LC.getLabel("Global", "loading") + '<div><div class="pbFooter"><div class="bg"><div></div></div><div>';
    document.body.appendChild(this.hover);
    var c = this;
    addEvent(this.hover, "mouseover", function () {
        c.show()
    }, true);
    addEvent(this.hover, "mouseout", function () {
        c.hide()
    }, true);
    this.hover = new iframeShim(this.hover);
    this.originalClass = "";
    this.fadingIn = this.fadingOut = null;
    this.loaderURL = b;
    this.loaded = false
}
LookupHoverDetail.STANDARD_BUBBLE_WIDTH = 302;
LookupHoverDetail.STANDARD_BUBBLE_HEIGHT = 262;
LookupHoverDetail.SHOW_DELAY = 800;
LookupHoverDetail.HIDE_DELAY = 250;
LookupHoverDetail.stopLoading = false;
LookupHoverDetail.hovers = {};
LookupHoverDetail.getHover = function (a, b) {
    var c = window.Shepherd;
    if (c && b) b = c.fixRetUrl(b);
    if (LookupHoverDetail.hovers[a]) return LookupHoverDetail.hovers[a];
    c = new LookupHoverDetail(a, b);
    return LookupHoverDetail.hovers[a] = c
};
LookupHoverDetail.hideAllHovers = function () {
    var a = LookupHoverDetail.hovers,
        b;
    for (b in a) a.hasOwnProperty(b) && a[b].hide()
};
LookupHoverDetail.prototype.show = function () {
    if (this.fadingOut) {
        clearTimeout(this.fadingOut);
        this.fadingOut = null
    } else {
        var a = this;
        if (!this.fadingIn) this.fadingIn = setTimeout(function () {
            a.showNow()
        }, LookupHoverDetail.SHOW_DELAY)
    }
};
LookupHoverDetail.prototype.showNow = function () {
    if (!this.loaded) if (this.loaderURL != null) {
        var a = this;
        Sfdc.Ajax.get(this.loaderURL, function (b) {
            a.load(b)
        }, {
            failure: function (b) {
                a.load(b)
            }
        })
    } else return;
    this.position();
    this.hover.setStyle("visibility", "visible");
    this.fadingIn = null
};
LookupHoverDetail.prototype.hide = function () {
    if (this.fadingIn) {
        clearTimeout(this.fadingIn);
        this.fadingIn = null
    } else {
        var a = this;
        this.fadingOut = setTimeout(function () {
            a.hideNow()
        }, LookupHoverDetail.HIDE_DELAY)
    }
};
LookupHoverDetail.prototype.hideNow = function () {
    this.hover.setStyle("visibility", "hidden");
    this.fadingOut = null
};
LookupHoverDetail.prototype.load = function (a) {
    this.hover.div.innerHTML = a;
    Util.evalScriptsUnderElement(this.hover.div);
    this.originalClass = this.hover.div.firstChild.className;
    this.height = this.hover.div.offsetHeight;
    delStyleClass(this.hover.div, "lookupHoverDetailLoading");
    this.position();
    this.loaded = true
};
LookupHoverDetail.prototype.position = function () {
    var a = getElementByIdCS(this.id),
        b = getObjX(a),
        c = getObjY(a),
        d = a.offsetWidth,
        e = a.offsetHeight,
        f = getScrollX(),
        g = getScrollY(),
        h = getWindowWidth(),
        i = getWindowHeight();
    a = this.originalClass + " ";
    if (c + e + this.height < g + i) {
        a += "top";
        c += e
    } else {
        a += "bottom";
        c -= this.height
    }
    if (b + d - this.bubbleOffset + this.width < f + h) {
        a += "Left";
        b = b + d / 2 - this.bubbleOffset
    } else {
        a += "Right";
        b = b + d / 2 - this.width
    }
    this.hover.setStyle("left", b + "px");
    this.hover.setStyle("top", c + "px");
    this.hover.div.firstChild.className = a;
    if (this.hover.div.firstChild) if (b = Util.hasStyleEndsWith(this.hover.div.firstChild, "Override")) {
        delStyleClass(this.hover.div, "lookupHoverDetailOverridable");
        delStyleClass(this.hover.div.firstChild, b);
        addStyleClass(this.hover.div, b)
    }
};

I would guess that Salesforce most likely uses the same ID for the same object referenced twice on a page (so if you have a lookup field to the same object twice on the page, they probably share the same ID), so when this LookupHoverDetail method is used, it looks for the first instance in the DOM which is at the top of the page.

To be completely honest with you, I am not sure how you can fix this. You will need to override Salesforce's included Javascript.

  • Since salesforce provides the hover functionality by default when a reference is used (obj_ref__c), is it possible to report a case, an idea or something to address this issue? Because, the same applies in every other visualforce page. – Vignesh Damodharan Sep 27 '12 at 17:44
  • 1
    I did some quick searching and really couldn't find an idea that was already started. I would suggest starting something up if that is the course you would like to take success.salesforce.com/ideaHome?c=09a30000000D9xtAAC – Jesse Altman Sep 27 '12 at 20:28
  • Can you say What I need to do for making this happen.Because I copied this in script tabg in Vf page.But didnt see any change. How to override Salesforce's included Javascript. – Arun SFDC Jun 13 '13 at 13:22
5

I've found the solution for this issue. The point is to give the popup an unique id. So we can't use the object reference id because it is a shared value on the page (like @JesseAltman said in his answer). Now we need to generate this ID manually and use an imitation of the popup. For that i will use an apex:variable tag to generate ID if this form: hover123:

Visualforce:

<apex:outputPanel style="width:300px;" layout="block">

<!-- Here defining a new variable for the fake id -->
<apex:variable value="{!0}" var="rowNumber" />

<apex:pageBlock mode="maindetail">
    <apex:pageBlockTable value="{!accs}" var="a">
        <apex:column value="{!a.name}"/>

        <apex:column headerValue="Some header">
            <a href="/{!a.test2__r.id}" id="hover{!rowNumber}" 
                                        position="relative"
                                        onblur="LookupHoverDetail.getHover('hover{!rowNumber}').hide();" 
                                        onfocus="LookupHoverDetail.getHover('hover{!rowNumber}', '/{!a.test2__r.id}/m?retURL=%2F{!a.test2__r.id}&isAjaxRequest=1').show();" 
                                        onmouseout="LookupHoverDetail.getHover('hover{!rowNumber}').hide();" 
                                        onmouseover="LookupHoverDetail.getHover('hover{!rowNumber}', '/{!a.test2__r.id}/m?retURL=%2F{!a.test2__r.id}&isAjaxRequest=1').show();">
                {!a.test2__r.name}
            </a>
        </apex:column>

        <apex:column headerValue="1">
            <!-- Increasing the value of the variable -->
            <apex:variable var="rowNumber" value="{!rowNumber + 1}" />
        </apex:column>

    </apex:pageBlockTable>
</apex:pageBlock>

</apex:outputPanel>

Apex (here i have the same test2__c reference in each account):

public with sharing class MyClass {

    // Just a list with some accounts
    public List<Account> accs { get; set; }

    public MyClass() { 
        accs = [Select id, test2__c, test2__r.id, test2__r.name, Name 
                From Account 
                Limit 100];
    }
}

This will looks like this:

enter image description here

5

I came across this post trying to find a solution to the same issue, In my case i found that the popup was selecting the correct ID along the x axis but moving the position on the popup outside of the panel i wanted. So when i highlighted the linked item if it was say 800px on a scroller. It would move the popup to 800px outside the scroller.

This was the first issue I found. There were also ID issues along the X and Y for different commandLinks & when I reordered my page the popups would again be scrambled.

I worked out that the broken / buggy SalesForce code was within LookupHoverDetail.prototype.position. In here we were returning getObjX and getObjY wrongly.

So within my visualforce page I placed some overwriting code to replace the wrong possitions.

function getObjX(a) {
    var left = 0;       
    while(a){
        left += (a.offsetLeft - a.scrollLeft + a.clientLeft);
        a = a.offsetParent;
    }
    return left;
}
function getObjY(a) {
    var top = 0;        
    while(a){
        top += (a.offsetTop - a.scrollTop + a.clientTop);
        a = a.offsetParent;
    }
    return top;
}

Next I had to deal with the ID's being non-unique, I call this function after every Ajax event so that if I order the page, the Id's are to the correct links still. This function will execute at the same time as the others on load.

function fixSalesforce() {
    $FFDC('.lineItem A').each(function(index){
        var me = $FFDC(this);
        var aId = me.attr('id');
        if (aId != null){
        var trId = me.closest('tr').attr('id');
            if (aId.indexOf(trId) != 0){
            var newId = trId + aId;
            me.attr('id', newId);
            me.attr('onmouseover', me.attr('onmouseover').replace( new RegExp(aId, 'g' ), newId));
            me.attr('onmouseout', me.attr('onmouseout').replace( new RegExp(aId, 'g' ), newId));
            me.attr('onfocus', me.attr('onfocus').replace( new RegExp(aId, 'g' ), newId));
            me.attr('onblur', me.attr('onblur').replace( new RegExp(aId, 'g' ), newId));
        }
        }
    });
}

The aim of the above function is to make the ID's unique by prefixing them with the row ID which I put on the TR.

{
      <apex:variable value="{!0}" var="tRow" /> 
      <apex:repeat var="tl"value="{!viewState.BankStatement.TransactionLineItems}"id="tRow1"> 
      <tr id="tRow{!tRow}" class="lineItem{!IF(tl.isChecked,'selected','')}">
      <apex:variable var="tRow"value="{!tRow+1}"/>
}

Within all CommandLinks I then placed onComplete="Refresh();"

This is a function that will call the fixSalesforce() function from earlier.

0

For those who are still struck, once your page loads, have below function executed:check my blog

function modifyIDS(){
             console.log('modifyIDS');
            j$("a[id *='lookup']").each(function(index){
                var newID = "referencedField"+index;
                var elem = j$(this);
                var fullHTML = elem[0].outerHTML;
                var oldID = elem.attr('id');
                 console.log('modifyIDS2' +oldID);
                var regex = new RegExp( oldID, 'g');
               // console.log(fullHTML);

                try{

                    elem[0].outerHTML = fullHTML.replace(regex,
                    newID);
                }
                  catch(e){
                    console.log('error occured: '+e);
                }
            });
        }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.