M
M
MaximPatrushev2019-04-30 16:52:10
React
MaximPatrushev, 2019-04-30 16:52:10

How to get the coordinates of an element in an iframe?

In general, the task is to render a reactive table of contents for the contents of the WYSIWYG editor.

I am working with React and I am integrating ckeditor as an editor. The task is to make it so that when filling the editor with content, all headings are parsed and reactively displayed as a table of contents next to the editor window. At the same time, the items in this table of contents should be clickable - on click, the editor window should scroll to the corresponding anchor (title). Available plugins only allow you to display a table with a table of contents, but do not make its elements clickable.

I decided to "manually" extract the titles from the content of the editor, display them, and link them to anchors inside the editor. But that didn't work, navigating through anchors inside an iframe is either not possible or I couldn't find a way to do it. Then I decided to hang handlers on these displayed headers and use the scrollTo method to simulate the transition through anchors.

There was a problem here with getting the coordinates of the headings in the editor text (inside the iframe). headerObj.offsetTop always returns equal to 0.

I would be glad for help in solving the problem in any way)

import React, { useState } from 'react';
import CKEditor from 'ckeditor4-react';

const App = () => {

  const [headers, setHeaders] = useState([]);

  const handleChange = e => {
    const content = e.editor.getData();
    const contentObject = document.createElement('div');
    contentObject.innerHTML = content;
    const headersArray = [...contentObject.getElementsByTagName('h2')];
    setHeaders(headersArray);
  };

  const goToAnchor = headerObj => {
    const frame = document.getElementsByTagName('iframe')[0];
    const top = headerObj.offsetTop + frame.contentWindow.scrollY;

    frame.contentWindow.scrollTo({
      top: top,
      behavior: "smooth"
    })
  };


  return (
    <div className="App">
      <h2>Using CKEditor 4 in React</h2>
      <CKEditor
        data="<h2>1. Заголовок 1</h2><h2>2. Заголовок 2</h2><h2>3. Заголовок 3</h2>"
        onChange={e => handleChange(e)}
      />
      <div>
        <h2>Содержание</h2>
        {headers.map((header, index) => {
          return (
            <h2
              key={index}
              dangerouslySetInnerHTML={{__html: header.innerHTML}}
              onClick={() => goToAnchor(header)}
            />
          );
        })}
      </div>
    </div>
  );
};

export default App;

Answer the question

In order to leave comments, you need to log in

1 answer(s)
0
0xD34F, 2019-04-30
@MaximPatrushev

headerObj.offsetTop

Senseless operation. This headerObj is not at all the one in the iframe. Better when clicking in the list, get the index of the clicked element, and take the heading with the same index in the iframe.
This is not necessary. Use useRef - you get a link to an instance of your editor, it has a document property there, the iframe you need.
UPD. https://codesandbox.io/s/j3oy0q1219

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question