programing

React.js: contentEditable의 onChange 이벤트

shortcode 2023. 4. 3. 23:24
반응형

React.js: contentEditable의 onChange 이벤트

의 변경 이벤트를 재생하려면 어떻게 해야 합니까?contentEditable- 기반 제어?

var Number = React.createClass({
    render: function() {
        return <div>
            <span contentEditable={true} onChange={this.onChange}>
                {this.state.value}
            </span>
            =
            {this.state.value}
        </div>;
    },
    onChange: function(v) {
        // Doesn't fire :(
        console.log('changed', v);
    },
    getInitialState: function() {
        return {value: '123'}
    }
});

React.renderComponent(<Number />, document.body);

JSFiddle 코드

이것은 나에게 효과가 있었던 가장 간단한 해결책이다.

<div
  contentEditable='true'
  onInput={e => console.log('Text inside div', e.currentTarget.textContent)}
>
Text inside div
</div>

구현의 오류를 수정하는 Sebastien Lorber의 답변을 참조하십시오.


onInput 이벤트 및 옵션으로 onBlur을 폴백으로 사용합니다.추가 이벤트 전송을 방지하기 위해 이전 내용을 저장할 수 있습니다.

저는 개인적으로 이것을 렌더링 함수로 사용하고 싶습니다.

var handleChange = function(event){
    this.setState({html: event.target.value});
}.bind(this);

return (<ContentEditable html={this.state.html} onChange={handleChange} />);

jsbin

contentEditable 주위에 이 간단한 래퍼를 사용합니다.

var ContentEditable = React.createClass({
    render: function(){
        return <div
            onInput={this.emitChange}
            onBlur={this.emitChange}
            contentEditable
            dangerouslySetInnerHTML={{__html: this.props.html}}></div>;
    },
    shouldComponentUpdate: function(nextProps){
        return nextProps.html !== this.getDOMNode().innerHTML;
    },
    emitChange: function(){
        var html = this.getDOMNode().innerHTML;
        if (this.props.onChange && html !== this.lastHtml) {

            this.props.onChange({
                target: {
                    value: html
                }
            });
        }
        this.lastHtml = html;
    }
});

누군가가 내 솔루션으로 NPM에 대한 프로젝트를 만들었다: 반응 콘텐츠

방금 제공한 HTML을 브라우저가 "재포맷"하려고 하면 컴포넌트가 항상 재렌더링되는 또 다른 문제가 발생했습니다.이것 좀 봐.

다음은 프로덕션 콘텐츠 편집 가능 구현입니다.다음 옵션 중 몇 가지 추가 옵션이 있습니다.react-contenteditable다음과 같은 기능을 제공합니다.

  • HTML fragment를 삽입할 수 있는 명령형 API
  • 콘텐츠 재포맷 기능

요약:.

FakeRainBrigand의 솔루션은 새로운 문제가 생기기 전까지는 꽤 잘 작동했습니다.Content Editables는 귀찮고 React에 대처하기가 쉽지 않습니다.

이 JSFiddle은 문제를 나타냅니다.

보시는 바와 같이 몇 개의 문자를 입력하고 을 클릭하면 내용이 지워지지 않습니다.이는 콘텐츠 가능을 마지막으로 알려진 가상 DOM 값으로 리셋하려고 하기 때문입니다.

그래서 다음과 같이 보입니다.

  • 당신은 필요하다shouldComponentUpdate캐럿 위치 점프를 방지하려면
  • React의 VDOM 확산 알고리즘에 의존할 수 없습니다.shouldComponentUpdate이쪽입니다.

그래서 당신은 여분의 대사가 필요합니다. 그래서 언제든지shouldComponentUpdate는 「yes」를 반환합니다.DOM 컨텐츠가 실제로 갱신되고 있는 것을 확인합니다.

이 버전에서는,componentDidUpdate다음과 같이 됩니다.

var ContentEditable = React.createClass({
    render: function(){
        return <div id="contenteditable"
            onInput={this.emitChange}
            onBlur={this.emitChange}
            contentEditable
            dangerouslySetInnerHTML={{__html: this.props.html}}></div>;
    },

    shouldComponentUpdate: function(nextProps){
        return nextProps.html !== this.getDOMNode().innerHTML;
    },

    componentDidUpdate: function() {
        if (this.props.html !== this.getDOMNode().innerHTML) {
           this.getDOMNode().innerHTML = this.props.html;
        }
    },

    emitChange: function() {
        var html = this.getDOMNode().innerHTML;
        if (this.props.onChange && html !== this.lastHtml) {
            this.props.onChange({
                target: {
                    value: html
                }
            });
        }
        this.lastHtml = html;
    }
});

가상 DOM은 오래된 상태로 남아 있어 가장 효율적인 코드는 아닐 수 있지만 적어도 동작합니다:) 버그는 해결되었습니다.


세부사항:

  1. 캐럿 점프를 피하기 위해 show Component Update를 넣으면 (적어도 키 입력에서는) 컨텐츠가 재렌더되지 않습니다.

  2. 이 컴포넌트가 키 스트로크로 재렌더되지 않는 경우 React는 이 콘텐츠에 대해 오래된 가상 DOM을 유지합니다.

  3. React가 콘텐츠의 오래된 버전을 가상 DOM 트리에 보관하고 있는 경우 콘텐츠의 값을 가상 DOM 내의 오래된 값으로 리셋하려고 하면 가상 DOM diff 동안 React는 DOM에 적용할 변경 사항이 없음을 계산합니다.

이 문제는 주로 다음과 같은 경우에 발생합니다.

  • 처음에는 컨텐츠에 저장 가능한 빈 공간이 있습니다(shouldComponentUpdate=true,prop=previous 이전 vdom=N/A).
  • 사용자가 일부 텍스트를 입력하면 렌더링이 금지됩니다(shouldComponentUpdate=false,prop=text, 이전 vdom=").
  • 사용자가 유효성 검사 버튼을 클릭한 후 해당 필드(shouldComponentUpdate=false,prop=previous previous vdom=")를 비우선 vdom="
  • 새로 생성된 가상 DOM과 오래된 가상 DOM이 모두 " "이기 때문에 리액트는 DOM에 접촉하지 않습니다.

되면 항상 때문에 '포커스'를 .onBlur이벤트 핸들러

<div
  onBlur={e => {
    console.log(e.currentTarget.textContent);
  }}
  contentEditable
  suppressContentEditableWarning={true}
>
  <p>Lorem ipsum dolor.</p>
</div>

이것은 아마 당신이 찾고 있는 답은 아닐 것입니다만, 스스로 이 문제와 씨름하고 있고, 제안된 답변에 문제가 있어, 저는 그 대신 그것을 통제하지 않기로 결정했습니다.

editable은 「」입니다.false , , , 을합니다.texttruetext 동안.onChange제어에 의해 발사됩니다.으로 내가 때editable으로 false된 내용을 로 채웁니다.text:

/** @jsx React.DOM */
'use strict';

var React = require('react'),
    escapeTextForBrowser = require('react/lib/escapeTextForBrowser'),
    { PropTypes } = React;

var UncontrolledContentEditable = React.createClass({
  propTypes: {
    component: PropTypes.func,
    onChange: PropTypes.func.isRequired,
    text: PropTypes.string,
    placeholder: PropTypes.string,
    editable: PropTypes.bool
  },

  getDefaultProps() {
    return {
      component: React.DOM.div,
      editable: false
    };
  },

  getInitialState() {
    return {
      initialText: this.props.text
    };
  },

  componentWillReceiveProps(nextProps) {
    if (nextProps.editable && !this.props.editable) {
      this.setState({
        initialText: nextProps.text
      });
    }
  },

  componentWillUpdate(nextProps) {
    if (!nextProps.editable && this.props.editable) {
      this.getDOMNode().innerHTML = escapeTextForBrowser(this.state.initialText);
    }
  },

  render() {
    var html = escapeTextForBrowser(this.props.editable ?
      this.state.initialText :
      this.props.text
    );

    return (
      <this.props.component onInput={this.handleChange}
                            onBlur={this.handleChange}
                            contentEditable={this.props.editable}
                            dangerouslySetInnerHTML={{__html: html}} />
    );
  },

  handleChange(e) {
    if (!e.target.textContent.trim().length) {
      e.target.innerHTML = '';
    }

    this.props.onChange(e);
  }
});

module.exports = UncontrolledContentEditable;

이를 위해서는 MutationObserver를 사용하는 것이 좋습니다.그것은 당신에게 무슨 일이 일어나고 있는지 훨씬 더 잘 통제할 수 있게 해줍니다.또, 브라우즈로 모든 키 스트로크를 해석하는 방법에 대해서도 자세하게 설명합니다.

여기 TypeScript:

import * as React from 'react';

export default class Editor extends React.Component {
    private _root: HTMLDivElement; // Ref to the editable div
    private _mutationObserver: MutationObserver; // Modifications observer
    private _innerTextBuffer: string; // Stores the last printed value

    public componentDidMount() {
        this._root.contentEditable = "true";
        this._mutationObserver = new MutationObserver(this.onContentChange);
        this._mutationObserver.observe(this._root, {
            childList: true, // To check for new lines
            subtree: true, // To check for nested elements
            characterData: true // To check for text modifications
        });
    }

    public render() {
        return (
            <div ref={this.onRootRef}>
                Modify the text here ...
            </div>
        );
    }

    private onContentChange: MutationCallback = (mutations: MutationRecord[]) => {
        mutations.forEach(() => {
            // Get the text from the editable div
            // (Use innerHTML to get the HTML)
            const {innerText} = this._root;

            // Content changed will be triggered several times for one key stroke
            if (!this._innerTextBuffer || this._innerTextBuffer !== innerText) {
                console.log(innerText); // Call this.setState or this.props.onChange here
                this._innerTextBuffer = innerText;
            }
        });
    }

    private onRootRef = (elt: HTMLDivElement) => {
        this._root = elt;
    }
}

다음은 lovasoa에 의해 이 대부분을 통합한 컴포넌트입니다.https://github.com/lovasoa/react-contenteditable/blob/master/index.js

emiss Change에서 이벤트를 시뮬레이트합니다.

emitChange: function(evt){
    var html = this.getDOMNode().innerHTML;
    if (this.props.onChange && html !== this.lastHtml) {
        evt.target = { value: html };
        this.props.onChange(evt);
    }
    this.lastHtml = html;
}

비슷한 방법을 성공적으로 사용하고 있습니다.

<div
    spellCheck="false"
    onInput={e => console.log("e: ", e.currentTarget.textContent}
    contentEditable="true"
    suppressContentEditableWarning={true}
    placeholder="Title"
    className="new-post-title"
/>

다음은 Sebastien Lorber의 답변을 바탕으로 한 후크 기반 버전입니다.

const noop = () => {};
const ContentEditable = ({
  html,
  onChange = noop,
}: {
  html: string;
  onChange?: (s: string) => any;
}) => {
  const ref = useRef<HTMLDivElement>(null);
  const lastHtml = useRef<string>('');

  const emitChange = () => {
    const curHtml = ref.current?.innerHTML || '';
    if (curHtml !== lastHtml.current) {
      onChange(curHtml);
    }
    lastHtml.current = html;
  };

  useEffect(() => {
    if (!ref.current) return;
    if (ref.current.innerHTML === html) return;
    ref.current.innerHTML = html;
  }, [html]);

  return (
    <div
      onInput={emitChange}
      contentEditable
      dangerouslySetInnerHTML={{ __html: html }}
      ref={ref}
    ></div>
  );
};

Saint Laurent의 를 사용하려고 했습니다.

<div
  onBlur={e => {
    console.log(e.currentTarget.textContent);
  }}
  contentEditable
  suppressContentEditableWarning={true}
>
    <p>Lorem ipsum dolor.</p>
</div>

이 값을 설정하기 전까지는 완벽하게 동작합니다.를 호출하는 setState(e.currentTarget.textContent) , , , , , 을 .currentTarget무효로 합니다. setState으로 동작합니다.currentTarget를 사용할 수 없습니다.

17가 있었던 은 액 1717.0을 사용하는 이었습니다.2개e.target.innerText:

<div
  onBlur={e => setState(e.target.innerText)}
  contentEditable
  suppressContentEditableWarning={true}
>
    <p>Lorem ipsum dolor.</p>
</div>

언급URL : https://stackoverflow.com/questions/22677931/react-js-onchange-event-for-contenteditable

반응형