Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IText not editable when canvas is in a fullscreen element on chrome #5126

Closed
Exlord opened this issue Jul 28, 2018 · 42 comments
Closed

IText not editable when canvas is in a fullscreen element on chrome #5126

Exlord opened this issue Jul 28, 2018 · 42 comments
Labels
needs_confirmation stale Issue marked as stale by the stale bot

Comments

@Exlord
Copy link

Exlord commented Jul 28, 2018

Version

2.3.3 (At this moment at the official test page)
I am still using 1.7.20 in my app.
chrome Version 67.0.3396.99 (Official Build) (64-bit)

Test Case

Fabric.js tests · IText tests
http://fabricjs.com/test/misc/itext.html

Steps to reproduce

open the official IText demo in chrome
open console and run this code :
document.querySelector('.example > .canvas-container').webkitRequestFullscreen()
double click in text and try to edit.

Expected Behavior

Text be edited and I can see the typed characters
works as expected in FireFox

Actual Behavior

Nothing happens! Keyboard does not work at all !!!

@asturur
Copy link
Member

asturur commented Jul 29, 2018

i think it is because the hiddenTextarea responsible for editing is actually in the body and not in .canvas-container

Can you link me some doc about this webkitRequestFullscreen so that i can read how is supposed to behave and if is detectable?

@samael205
Copy link

@asturur
Copy link
Member

asturur commented Aug 4, 2018

i m open to the idea of having a check if there is a fullscreen element and in that case append the textarea to the canvas-container instead of the body.
In case this creates other problems i will revert the patch.

@marshall76963
Copy link

I think I was having a similar issue when using the canvas within a bootstrap modal (using fabric 2.3.6). I was able to get the text selection but no keyboard input had any affect at changing the text.

The only change I made was in initHiddenTextarea: function(), change the following lines:

fabric.document.body.appendChild(this.hiddenTextarea)

becomes:

this.canvas.wrapperEl.appendChild(this.hiddenTextarea);

This made the IText and Texbox items editable again

@shamis
Copy link

shamis commented Sep 13, 2018

Facing the same problem with canvas on bootstrap modal

@Exlord
Copy link
Author

Exlord commented Oct 2, 2018

my patch for now based on @marshall76963 's answer

  const _original_initHiddenTextarea   = fabric.IText.prototype.initHiddenTextarea;
  fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.prototype */ {    
    //fix for : IText not editable when canvas is in a fullscreen element on chrome
    // https://github.com/fabricjs/fabric.js/issues/5126
    initHiddenTextarea: function() {
      _original_initHiddenTextarea.call(this);
      this.canvas.wrapperEl.appendChild(this.hiddenTextarea);
    }
  });

@acautin
Copy link

acautin commented Oct 11, 2018

After being fighting with this for a while I found this issue was already reported solved and the solution reverted because it gives selections issues on I.E.

This is the pr with a fix similar to the one proposed by @Exlord #3754

Here a picture of the selection issue on IE when the textarea is inside the dialog element. It will highlight the iText on the canvas but it will not be reflected on the textarea.
insidedialog

The issue caused by this change is documented here #3137

Here the same selection with the textarea placed on the body. Note how the focus now is not in the textarea but in the canvas container (red border) so typing doesn't work anymore.
insidebody

@asturur maybe you can help us understand how the selection is mapped between the iText and the hidden texteara and why doesn't work when adding the component inside the container on I.E.

Thanks!

@asturur
Copy link
Member

asturur commented Dec 8, 2018

The selection is mapped and has to stay mapped so that we can edit complex text situation where knowing selectionstart-selectionend is not enough.
Example:

the macOS long press popup makes a substitution without any kind of keydown/keyup event
Some text has ha visual representation that does not match the byte count ( 2 letter selected at screen, 8 positions in textarea)
Japanese/chinese text do have some kind of replacement logic in their input method that can't be tracked manually.

To solve all of this we need the hidden textarea to process those inputs rather than our JS, and then listen to the input event and have the fabric code represent the new textarea situation.

For this to happen while we modify the fabric selection we have to be sure that the textarea is selected in the sameway so that is ready to get the input events and react how the user would expect

I.E. is I.E. it makes strange things and we cannot do much about it.

Since I.E. does not support fullscreen mode presumably, you can still append the textarea somewhere else when you enter fullscreen mode. There may be other things that will make the textarea position (top/left) go wrong but if you do not use chines/japanese that may be not an issue

@lcswillems
Copy link

I am using Material-UI (https://material-ui.com/) and especially, the Dialog component (https://material-ui.com/components/dialogs/).

I can't make IText or Textbox object work when the canvas is inside a Dialog. I don't understand why I am not able to do it

@lcswillems
Copy link

I was able to fix it by changing the initHiddenTextarea method defined here:

initHiddenTextarea: function() {

I used this code:

  fabric.util.object.extend(fabric.IText.prototype, {    
    initHiddenTextarea: function() {
      // The original function code with some modifications
    }
  });

where the modifications are:

  • replacing this fabric.document.body.appendChild(this.hiddenTextarea); by this.canvas.wrapperEl.appendChild(this.hiddenTextarea);
  • replacing this this.hiddenTextarea.style.cssText = 'position: absolute; top: ' + style.top + '; left: ' + style.left + '; z-index: -999; opacity: 0; width: 1px; height: 1px; font-size: 1px;' + ' paddingーtop: ' + style.fontSize + ';'; by 'z-index: -999; opacity: 0; width: 1px; height: 1px; font-size: 1px; padding: 0;'

@asturur Why do you set left, top, padding-top properties on the hiddenTextarea? I don't understand their purpose

@asturur
Copy link
Member

asturur commented Jan 6, 2020

Left and top needs to be updated because the hidden text area needs to follow the blinking cursor in order to properly support chinese/japanese and other languages that need the textarea to display suggestions.

padding-top had to be some workaround to position the textarea enough under the text we are painting. Probably there was some difficulties if i would set the height of the textarea directly.

Appending the the textarea to canvas container rather than document.body was creating some difficulties to ie11.

@lcswillems
Copy link

Thank you for this detailed answer!

If you don't support IE11 anymore, I think it would be better to add textarea to canvas container again, because otherwise, it makes embedding the textarea in popup impossible

@asturur
Copy link
Member

asturur commented Jan 6, 2020

in theory we support ie10 up.
I tested this nearly 3 years ago. If you have a ie11 machine i would reverify it. The main problem was the document jumping up and down, but it could also be that sequent changes solved the problem elsewhere.

@philipstratford
Copy link

philipstratford commented Jan 7, 2020

I'd never heard of fabric.js until yesterday so please excuse my ignorance. I'm using TUI Image Editor which uses fabric.js, and I'm having a similar issue to this.

My image editor (using fabric.js) is inside a Bootstrap modal. When I add a text area to the canvas and try to type, as others have described here, nothing happens. I can select the text but presses of keyboard keys have no effect. This is in the latest version of Chrome and I'm using fabric.js v3.6.0.

I've noticed that if I hit the ESC key whilst trying to edit the text, I can see the Bootstrap modal close behind the fabric.js canvas (so that key press obviously isn't being handled by fabric.js as expected), but after that the text becomes editable, so the problem is definitely somehow related to the canvas element being inside the modal.

I tried to make the changes outlined by @lcswillems but unfortunately they haven't solved the problem for me.

EDIT: I've also tried the patch mentioned above by @Exlord but I'm still unable to edit the text and now, when I double-click on the text area to try and edit, the image jumps around the page.

@asturur
Copy link
Member

asturur commented Jan 7, 2020

before moving forward we should agree at least that modals do not exists. Bootstrap creates a div structure that looks like a modal. If they catch the key events or if they focus something else, the textarea stop to works. is it possible to recreate your setup in a fiddle or codepen?

@philipstratford
Copy link

before moving forward we should agree at least that modals do not exists. Bootstrap creates a div structure that looks like a modal. If they catch the key events or if they focus something else, the textarea stop to works.

Agreed!

is it possible to recreate your setup in a fiddle or codepen?

This fiddle was created by @kghandi a couple of years ago but it seems to be exactly the same issue and I experience the same problem of being unable to edit the text. This is from #3754.

@asturur
Copy link
Member

asturur commented Jan 7, 2020

we have currently 2 issues with the inputs. one is the fullscreen option and other is the event blocking container.

both could be resolved putting back the textarea in the canvas container, if this won t make the page jumps start again. The problem is that i have no time to dedicate to ie11 and i have no a reproducible test case for the page jumps. If we want this to move forward i need help.

first step would be digup the pr in which we initially moved again the textarea outside and look at comments if there are tracks of reproducible issues.

@marshall76963
Copy link

My solution (with all the caveats mentioned above by many previous commentors) was to add this this to a seperate JS file and include it after fabric.js:

fabric.IText.prototype.initHiddenTextarea = (function (initHiddenTextarea) {
    return function () {
        var result = initHiddenTextarea.apply(this);
        fabric.document.body.removeChild(this.hiddenTextarea);
        this.canvas.wrapperEl.appendChild(this.hiddenTextarea);
        return result;
    };
})(fabric.IText.prototype.initHiddenTextarea);

I can confirm that this works on IE11 (and other browsers) whilst using a fabric canvas in a bootstrap (v3) popup.

@philipstratford
Copy link

philipstratford commented Jan 8, 2020

@marshall76963 For some reason, when I try this solution, as with one or two of the others suggested, clicking on the text to edit it causes the image to jump around the screen, and the text never becomes editable. I don't know what I'm doing differently that prevents this from working for me.

@asturur
Copy link
Member

asturur commented Jan 8, 2020

it may depend from the current hiddenTextArea style.

@philipstratford
Copy link

Well, I've spent about 2 days on this now and haven't got the time to get right to the bottom of the issue but this is where I'm at:

  • I'm using Froala's WYSIWYG text editor, which uses TUI Image Editor (which uses Fabric.js) for advanced image editing. The text editor is inside a Bootstrap (v3) modal.
  • When Froala instantiates the TUI Image Editor it adds the element as a direct descendent of the DOM body.
  • When I add text to an image and try to edit it, the hiddentTextarea is therefore also added as a direct descendent of the body. The text is not editable.
  • If I use the DevTools console to manually remove the open modal element from the DOM (which is not visible on screen because it's behind the TUI Image Editor element), the text becomes editable. So it's definitely the open modal that's interfering.

As a result, I've come up with this hacky solution which works for me, given my current project time constraints. It uses the patch from @marshall76963, but slightly modified.

fabric.IText.prototype.initHiddenTextarea = (function (initHiddenTextarea) {
    return function () {
        var result = initHiddenTextarea.apply(this);        
        if (document.querySelectorAll(".modal.fade.in").length > 0) {
            fabric.document.body.removeChild(this.hiddenTextarea);
            document.getElementsByClassName("modal fade in")[0].appendChild(this.hiddenTextarea);
        }        
        return result;
    };
})(fabric.IText.prototype.initHiddenTextarea);

By adding the hiddenTextarea to the modal element in this way if a modal is visible, it allows the text to always be editable.

@asturur
Copy link
Member

asturur commented Jan 8, 2020

because the modal div/box is probably eating and killing keyboard events, somehow is also stealing the focus from it? i m not sure how it can steal events if the textarea is outside of it and has focus....

Does it work out of the box if you try to do textbox.hiddenTextarea.focus() with a setTimeout at some point? just as a try

@philipstratford
Copy link

@asturur Doesn't seem to. I modified the patch as follows to test:

fabric.IText.prototype.initHiddenTextarea = (function (initHiddenTextarea) {
    return function () {
        var result = initHiddenTextarea.apply(this);
        window.setTimeout(function () {
            document.querySelectorAll('[data-fabric-hiddentextarea]')[0].focus();
            console.log("focussed");
        }, 5000);
        return result;
    };
})(fabric.IText.prototype.initHiddenTextarea);

The text is still not editable after the timeout.

@asturur
Copy link
Member

asturur commented Jan 8, 2020

I would really appreciate if someone could throw in a simple demo page that is broken on fabricjs.com ( copy pasting another itext demo page ) to replicate this bug.
A canvas with an IText in a modal that can't be edited.

Having a broken example makes easier to fix it and the mantain it fixed.
Automated testing for this is hard

@philipstratford
Copy link

philipstratford commented Jan 8, 2020

I think my post from yesterday contains a link to exactly such a fiddle.

@PieterVanDiggele
Copy link

I guess this issue relates to : #3695

My solution was to change :

fabric.document.body.appendChild(this.hiddenTextarea); ( line : 28350, Fabricjs 3.6.1 )
to
this.canvas.upperCanvasEl.parentElement.appendChild(this.hiddenTextarea);

This way the Textarea is created within the scope of the canvas elemt itself wherever it is in the DOM. It will get the same events fired within a modal, the same way any other will get it. Modals WILL prevent events from bubling upto body elements, thats why they are modal.

Might be worth checking for a patch ?

@stale
Copy link

stale bot commented Feb 5, 2020

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the stale Issue marked as stale by the stale bot label Feb 5, 2020
@stale stale bot closed this as completed Feb 12, 2020
@daiplusplus
Copy link

daiplusplus commented Feb 24, 2020

I'm having the same problem, except in my page I'm not using Bootstrap modal, but a pure HTML5 <dialog>.

The tui-image-editor isn't located inside the <dialog>, it's actually loaded into an <iframe> elsewhere on the page while the <dialog> element is in the root DOM, but the <dialog> does not have the open attribute set - yet despite that, something about the <dialog> is blocking keyboard events to the hidden <textarea>.

None of my own scripts set-up keyboard event-handlers or call anything like ev.preventDefault().

My workaround for now is to destroy the <dialog> instead of hiding it and to recreate it when the tui-image-editor is closed.

I still don't know what is special about <dialog> that causes it to block keyboard input to other iframes when the <dialog> is closed - perhaps it's a bug in Chrome?

FWIW, things are fine in Firefox - but I think Firefox is using the dialog-polyfill I'm using.


I believe this code below should repro the problem in Chrome (I'm not actually able to verify that this actually does repro it right now - if someone could test it and see if it doesn't repro then I'll put more effort into making a reliable repro).

<html>
<body>
<dialog>Foobar</dialog> <!-- Note this dialog is not `<dialog open>`. -->
<div>
    <iframe src="tuiImageEditorDemo.html"></iframe>
</div>
</body>
</html>

(where tuiImageEditorDemo.html is this page: https://github.com/nhn/tui.image-editor/blob/master/examples/example01-includeUi.html

@asturur
Copy link
Member

asturur commented Feb 24, 2020

i did not even know we had a dialog html element. Definetely there is that textarea bug to solve and maybe put a switch for ie11 to keepmit working. The reason why i did not keep working in this bug is because i do not have a dev friendly ie11 setup

@lcswillems
Copy link

lcswillems commented Feb 24, 2020

i do not have a dev friendly ie11 setup

So why do you still support ie11? You can't even test the code under ie11. Stoping supporting ie11 (that is not used anymore) would help to solve this issue

@asturur
Copy link
Member

asturur commented Feb 24, 2020

because is still out there and has a canvas. ie10 and ie11 in theory works. Ie10
could go out of support easily since if you are stuck in that you probably have a terrible internet experience with or without fabricjs.

Fabric 4 is still in beta so we could remove it. I l not 100% sure i want to do it.

Probably who wants really make something new and still support ie11 can eventually leave with 3.6.2 and backport stuff.

Another reason is that in my everyday work i do support ie11 and i would create a problem to myself unsupporting it.

@daiplusplus
Copy link

@asturur

The reason why i did not keep working in this bug is because i do not have a dev friendly ie11 setup

But this issue happens in (at least) Chrome 80, which was released a week ago - the polar opposite of IE11.

FWIW, you can download an IE11 virtual-machine for dev/testing directly from Microsoft: https://developer.microsoft.com/en-us/microsoft-edge/tools/vms/

@lcswillems
Copy link

@Jehoel is right. It seems you prefer support IE11 rather than Chrome 80...

@daiplusplus
Copy link

daiplusplus commented Feb 24, 2020

@lcswillems As a bonus, I used to work on Internet Explorer 11 when I was at Microsoft 😁

...not that this should influence this discussion at all.

@asturur
Copy link
Member

asturur commented Feb 24, 2020 via email

@daiplusplus
Copy link

daiplusplus commented Feb 24, 2020

I also proposed to make a configuration option, in order to attach the
hiddenTextarea to the canvas container to fix the problem and support the
necessary different calculations.

@asturur FWIW, I did try this and unfortunately that didn't fix the problem for me. It seems that whenever a <dialog> is present (and not necessarily open) in the parent page then any other hidden <textarea> in an <iframe> in the same page won't receive keyboard events.

@asturur
Copy link
Member

asturur commented Feb 24, 2020 via email

@xShteff
Copy link

xShteff commented Jul 29, 2020

For those using this in Angular, you can do this as a workaround for IText not being editable inside modals (we're using NG Zorro).

It's pretty much what's been already mentioned by several people, but "adapted" to Angular :).

import { fabric } from 'fabric';
import { ITextOptions } from 'fabric/fabric-impl';

export class CustomIText extends fabric.IText {
  public constructor(text: string, options?: ITextOptions) {
    super(text, options);
  }

  public initHiddenTextarea = () => {
    super.initHiddenTextarea();
    document.body.removeChild(this.hiddenTextarea as Node);
    this.canvas?.getElement().parentElement?.appendChild(this.hiddenTextarea as Node);
  };
}

@DustinX
Copy link

DustinX commented Sep 7, 2020

I think I found the cause, and pretty good solution for this.

Others have provided a solution that, for me, only partly worked.
It let me edit the text area in a modal on chrome, but still left me with the issue where I couldn't copy/paste or select and delete text in textboxes on IE.

The cause is that on IE the hidden textarea is unselectable by any means.
This is partly because fabric sets the canvas element and it's parent to be unselectable. The textarea itself is not set to be unselectable.
The code that does that is this:
(specifically the element.onselectstart = fabric.util.falseFunction;)

    function makeElementUnselectable(element) {
      if (typeof element.onselectstart !== 'undefined') {
        element.onselectstart = fabric.util.falseFunction;
      }
      if (selectProp) {
        element.style[selectProp] = 'none';
      }
      else if (typeof element.unselectable === 'string') {
        element.unselectable = 'on';
      }
      return element;
    }

On chrome this doesn't cause problems.
But on IE, it seems children of nodes that have onselectstart set to () => false also are unselectable.

A solution that is working for me is to put the textarea inside the modal element, but outside of the canvas unselectable elements. I did this by replacing this line:
fabric.document.body.appendChild(this.hiddenTextarea);
with this:
this.canvas.lowerCanvasEl.parentNode.parentNode.appendChild(this.hiddenTextarea);

This would cause issues for you if the parent of the parent of the canvas element isn't safe to append the textarea to, but this could be solved by adding an extra div wrapper to the canvas element.

@asturur
Copy link
Member

asturur commented Sep 19, 2020

i have no memories why we make the canvas unselectable. Probably to avoid selecting it when we do the drag selection on the upper canvas.
Probably next time, removing ie11 support will allow us for a series of improvements all over the place.
I wonder if having a different onselectstart function would help.
like if i selected a textbox that is editing, then, i let the function return not false.

@isaakdc
Copy link

isaakdc commented Aug 26, 2024

@asturur

This issue still seems to persist in v6.3 using FabricText. Is there are a suitable workaround for this?

I'm using the Dialog component from react-aria-components

@asturur
Copy link
Member

asturur commented Aug 26, 2024

i think for this we setup a textarea container to which you can append the hidden textarea element when going in fullscreen mode
You have to be sure in fullscreen mode the textarea in html still exists.
Can you verify that?

https://github.com/fabricjs/fabric.js/pull/7314/files

here the relevant PR.

If it find it helpful for your use case, can you please update the JSDOC and explain that solves the issue with fullscreen elements too?
If it doesn't solve can you open an issue with a reproducible use case?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs_confirmation stale Issue marked as stale by the stale bot
Projects
None yet
Development

No branches or pull requests