Retrieve current state of contentEditable HTML block

I have a div element with contentEditable=‘true’ and a button with a callback attached which consists of storing the div content. The problem is that the retrieved content is the initial state of the div, not the current one.

This problem was already noted here but not satisfyingly resolved for my use case.

What I have attempted:

  • retrieve the whole page state when the button is clicked
  • retrieve the div state (children argument)
  • attempted other keys, like ‘value’, ‘content’, ‘innerHtml’
    None of the above worked for me.

Here is the snippet I use.

import dash
import dash_html_components as html
from dash.dependencies import Input, Output, State

app = dash.Dash(__name__)

_OUTPUT = '0'
_PREVIEW_BUTTON = '1'
_TEXT_AREA_COMPONENT = '2'

app.layout = html.Div(
    [
        html.Div('Write here', id=_TEXT_AREA_COMPONENT, contentEditable='true'),
        html.Button('Preview', id=_PREVIEW_BUTTON),
        html.Div('Test', id=_OUTPUT),
    ]
)


@app.callback(
    Output(_OUTPUT, 'children'),
    Input(_PREVIEW_BUTTON, 'n_clicks'),
    [State(_TEXT_AREA_COMPONENT, 'children')],
)
def print_text_area_content(n_clicks, state):
    print(state)  # always prints Write here
    return html.P(str(n_clicks))


app.run_server(debug=True)

Is there any way to retrieve the edited content ?

1 Like

I’ve also faced the same issue, with callback using only the initial value from div with contentEditable='true'. Would be curious to see a solution.

For my use case, I ended up using dcc.Textarea instead which works well for long texts.

1 Like

Hi @remidbs and @cylim did you have any luck with this? i’m currently facing the same issue. I need to retreive the edited content, not the initial one.

You would not be able to do this in a regular callback since when the user edits a cell it does not update the underlying react object state. What you could do however is use a clientside callback to grab the content of the element.

The clientside callback could look something like this:

assets/scripts.js

if (!window.dash_clientside) {
    window.dash_clientside = {};
}
window.dash_clientside.clientside {
  get_edited_content: function(trigger, element_id) {
    var el = document.getElementById(element_id)
    var content = el.innerText
    return content
  }
}

And you would register it like this:

app.clientside_callback(
    ClientsideFunction(namespace="clientside", function_name="get_edited_content"),
    Output(_OUTPUT, "children"),
    [Input(_PREVIEW_BUTTON, "n_clicks")],
    [State(_TEXT_AREA_COMPONENT, "id")],
)

Note: This has not been tested.

1 Like

Thank you RenaudLN for your answer, I was not aware of client side callbacks.

At the time I posted this message, my workaround was to create my own component (see on github) using the cookie cutter provided by plotly.

1 Like

Hi @remidbs your component looks great!
I found it on Pypi and Github and would like to use it…but how to use it remains unclear…could you share a quick example or explain how to use it please?