views:

158

answers:

3

Hello, i have a strange problem only in Chrome using an iframe but working in all others common browser.

the problem: If i type in the IFRAME and then press the button to send, it work fine, the focus back to the IFRAME and the cursor BLINK.

But if i type and then press ENTER to invoke the event handler function, the focus back but the cursor disappear. And then if you go in another window and then back the cursor appear. This happen only in Chrome. I did the example page to show the problem in action. Click the link below to see.

UPDATE: I added the code also here below

    var editorFrame = 'myEditor'

function addFrame() {

    var newFrame = new Element('iframe', {
        width: '520', 
        height: '100',
        id: editorFrame,
        name: editorFrame,
        src: 'blank.asp',
        class: 'myClass'
    });

    $('myArea').appendChild(newFrame);

    window.iframeLoaded = function() {
        // this is call-back from the iframe to be sure that is loaded, so can safety attach the event handler

        var iframeDoc, UNDEF = "undefined";
        if (typeof newFrame.contentDocument != UNDEF) {
            iframeDoc = newFrame.contentDocument;
        } else if (typeof newFrame.contentWindow != UNDEF) {
            iframeDoc = newFrame.contentWindow.document;
        }
        if (typeof iframeDoc.addEventListener != UNDEF) {
            iframeDoc.addEventListener('keydown', keyHandler, false);
        } else if (typeof iframeDoc.attachEvent != UNDEF) {
            iframeDoc.attachEvent('onkeydown', keyHandler);  
        }
    };
}

function resetContent() 
{   
    var myIFrame = $(editorFrame);

    if (myIFrame) myIFrame.contentWindow.document.body.innerHTML='';
}

function setEditFocus() 
{

    var iFrame = document.frames ? document.frames[editorFrame] : $(editorFrame);
    var ifWin = iFrame .contentWindow || iFrame;

    ifWin.focus();

}

function send()
{
    resetContent();
    setEditFocus(); 
}

function keyHandler (evt) {

    var myKey=(evt.which || evt.charCode || evt.keyCode)

    if (myKey==13)  {

        if (!evt) var evt = window.event;

        evt.returnValue = false;

        if (Prototype.Browser.IE) evt.keyCode = 0;

        evt.cancelBubble = true;

        if (evt.stopPropagation) evt.stopPropagation();

        if (evt.preventDefault) evt.preventDefault();

        send();

    }
}

In the HTML page

<body onload="addFrame()">

<div id="myArea"></div>
<input id="myButton" type="button" value="click me to send [case 1]" onclick="send()">

To make more easy to understand the problem i've create a specific page to reproduce the problem with full example and source included.

You can view here by using Google Chrome: example of the problem

I really need your help because i tried to solve this problem for many days with no luck. And all the suggestions, tips and workaround are well accepted.

Thanks in advance.

A: 

Just quickly, can you try adding semicolons to the end of the lines inside your send() function? And see if that works.

function send() {
  resetContent();
  setEditFocus();
}
Albert
@Albert, thanks for your suggestion, but this dont let it work as well, the semicolons dont have any effect. I invite you to click the link above to see the problem in action using Chrome
Luka
+1  A: 

You know how I solved this one? In resetContent(), replace '' with ' ':

if (myIFrame) myIFrame.contentWindow.document.body.innerHTML=' ';

If it works, good. Don't ask why though, it might be one of those Webkit glitches with Range object, file a bug if you will.

syockit
@syockit thanks for your tip, look like the "Colombo egg" :) but still have some problem with this suggestion in other browser
Luka
+2  A: 

I'm not really sure what the cause of the issue is, as there are times where Chrome will give focus to the element correctly, though most of the time it does not. You shouldn't need to request focus at all, since the focus is not lost when you press the key. If you omit the setEditFocus() call, you should notice that it still works correctly in everything but Chrome, which apparently gets offended that you've removed all of the content in the body.

When you set contenteditable, every browser sets the innerHTML of the iframe document's body element to be something different:

Browser           | innerHTML
-----------------------------
Internet Explorer | ''
Opera             | '<br>\n'
Firefox           | '<br>'
Chrome/Safari     | '\n'

If you're not expecting to see that extra stuff when you parse the content later, you might want to remove it upfront in addFrame().

I was able to "fix" the problem by doing the following:

First, update the event handler so we can return false in it and prevent Opera from generating HTML for fun when we call getSelection() later...

function addFrame() {
    ...
    window.iframeloaded = function() {
       ...
       if (typeof iframeDoc.addEventListener != UNDEF) {
           iframeDoc.addEventListener('keypress', keyHandler, false);
       } else if (typeof iframeDoc.attachEvent != UNDEF) {
           iframeDoc.attachEvent('onkeypress', keyHandler);
       }
    }
}

Edit: Removed original function in favour of the new one included below

Finally, return false from the key press handler to fix the Opera issue mentioned above.

function keyHandler (evt) {
    var myKey=(evt.which || evt.charCode || evt.keyCode)

    if (myKey==13) {
        ...
        return false;
    }
}

I had originally done what syockit suggested, but I found it was doing weird things with the caret size in Chrome, which this method seems to avoid (although Firefox is still a bit off...). If you don't care about that, setting the innerHTML to be non-blank is likely an easier solution.

Also note that you should be using className instead of class in the object you pass to new Element(), since IE seems to consider it a reserved word and says that it's a syntax error.

Edit: After playing around with it, the following function seems to work reliably in IE8/Firefox/Chrome/Safari/Opera for your more advanced test case. Unfortunately, I did have to include Prototype's browser detection to account for Opera, since while everything looks the same as far as the JavaScript is concerned, the actual behaviour requires different code that conflicts with the other browsers, and I wasn't able to find a better way to differentiate between them.

Here's the new function, which focuses on the editable content of the iframe, and makes sure that if there is already content in there, that the caret is moved to the end of that content:

function focusEditableFrame(frame) {
    if (!frame)
        return;

    if (frame.contentWindow)
        frame = frame.contentWindow;

    if (!Prototype.Browser.Opera) {
        frame.focus();

        if (frame.getSelection) {
            if (frame.document.body.innerHTML == '')
                frame.getSelection().extend(frame.document.body, 0);
            else
                frame.getSelection().collapseToEnd();
        } else if (frame.document.body.createTextRange) {
            var range = frame.document.body.createTextRange();

            range.moveEnd('character', frame.document.body.innerHTML.length);
            range.collapse(false);
            range.select();
        }
    } else {
        frame.document.body.blur();
        frame.document.body.focus();
    }
}

Updated setEditFocus() (Not really necessary now, but since you already have it):

function setEditFocus()
{
    focusEditableFrame($(editorFrame));
}
Tim Stone
@Tim, oh you are a real GURU and the explanation is so clear! As the last time u help me a lot and ur suggestions fix the problem in CHROME! IN real the code that i post is extrapolated from my source that is bigger because the chat can be closed, re-open and more so ur fix work well but i need to give the focus in same cases and in FF, IE so i need to add a condition in the `function setEditFocus`. So i added a condition as `if(Prototype.Browser.Gecko || Prototype.Browser.IE || Prototype.Browser.Opera) {ifWin.focus();} else {... your code}` Opera dont work as well btw :( Suggestions?
Luka
@Luka - I tested the code that I posted in Firefox, IE, Opera, and Chrome (and Safari, just 'cause), so you shouldn't need to add the condition, because it works as I've written it in all of them. Is it not working when you add it to the rest of your code, or?
Tim Stone
@Tim, ops i let you work a lot :) but you are so good!! I created a new example where you can see your code in action, go here `http://under.bestqm.com/test/iframe2.asp` and try to press the buttons in sequence, you will see that the focus dont work in FF (button 2). Btw when press enter (as first problem) it work fine (both in Chrome and FF) as i desired and thanks to your code. The problem in this case is that i need give the focus in many cases to the IFRAME as the example using the button 2.
Luka
@Luka - Ah, I see. I'll work on a more generalized solution later that should hopefully take care of that.
Tim Stone
@Tim Great work! work fine with all browsers! Excellent solution.
Luka