Highlight selected text in HTML.div or Dcc.Markdown in Dash

Viewed 1020

I am trying to implement a keyword search approach in Dash where if the user types a certain keyword, it gets highlighted in the HTML.div or dcc.Markdown.

I have tried to enclose the keyword in dcc.Markdown using keyword, but it is not what I am looking for. Also for some reason the `` doesnt really highlight the text but rather makes it look tiny.

1 Answers

You could use html.Mark if you put the text in a html.Div component.

You could do something like this:

import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output


app = dash.Dash(__name__)

text = """
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
"""

app.layout = html.Div(
    [
        html.Div(
            id="text-container",
            children=[html.Div(text)],
        ),
        dcc.Input(id="search-input"),
    ]
)


@app.callback(
    Output("text-container", "children"),
    Input("search-input", "value"),
    prevent_initial_call=True,
)
def search(search_value):
    output = html.Div([text])

    if search_value:
        sequences = text.split(search_value)
        i = 1
        while i < len(sequences):
            sequences.insert(i, html.Mark(search_value))
            i += 2

        output.children = sequences

    return output


if __name__ == "__main__":
    app.run_server()

So the idea above is to create a callback which is called when your search input value changes. When the callback is called and the search value is not equal to an empty string I split the text with the search value as the delimiter.

sequences = text.split(search_value)
# equals: ['\n', ' ipsum dolor sit amet, consectetur adipiscing elit.\n']
# if search_value equals: Lorem

So now we have sequences of text where the search value doesn't match. In between these sequences we can put the sequences that did match the search value surrounded by the html.Mark function, that is what the while loop is doing in the example above.

Now as far as I know you can't do this for the Markdown component, because this only takes strings and not other Dash components. You could do something like this:

@app.callback(
    Output("text-container", "children"),
    Input("search-input", "value"),
    prevent_initial_call=True,
)
def search(search_value):
    new_text = text
    if search_value:
        new_text = text.replace(search_value, f"<mark>{search_value}</mark>")

    return dcc.Markdown(
        new_text,
        dangerously_allow_html=True,
    )

But the documentation says the following about dangerously_allow_html:

A boolean to control raw HTML escaping. Setting HTML from code is risky because it's easy to inadvertently expose your users to a cross-site scripting (XSS) (https://en.wikipedia.org/wiki/Cross-site_scripting) attack.

Related