Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

Editor.js is not ready because of Error: element with ID «editorjs» is missing. Pass correct holder's ID.

オープン
#2,914 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
3/5
見積もり時間
1〜2日
初心者へのやさしさ
35/100
issue の種類
バグ
明瞭さ
説明が足りない
活発さ
停滞
技術スタック
next.js, typescript
領域
frontend

調査の方向性

MyTextEditor コンポーネントと、EditorJS の holder editorjs-container 周辺における useEffect のライフサイクルから始め、次に示されている Next.js の構成でページ間のナビゲーションを再現します。holder がレンダリングされるタイミングに対して、初期化と破棄がいつ発生するかを確認します。ページを変更しても missing-element エラーが発生せず、エディターが引き続き初期化されてコンテンツを保存できれば完了です。

索引モデルが issue の本文から書いたものです。

説明

bug

Can anyone please share the workaround for this issue when changing page it is coming everytime.I tried creating a client Editor component,and did a dynamic nossr import in the page to be used but still facing the issue. I am using NextJS.


'use client'
import { EDITOR_JS_TOOLS } from '@/lib/editorConfig';
import { createClient } from '@/lib/supabase/client';
import { useEffect, useRef, useState } from 'react';
import { toast } from 'react-toastify';
import EditorJS, { OutputData } from '@editorjs/editorjs';

const INITIAL_DATA = {
    time: 1701368244004,
    blocks: [
        {
            "type": "header",
            "data": {
                "text": "5 🔑s to a longer life ",
                "level": 1
            }
        },
        {
            "id": "zbGZFPM-iI",
            "type": "paragraph",
            "data": {
                "text": "Hey. Meet the new Editor. On this page you can see it in action — try to edit this text. Source code of the page contains the example of connection and configuration."
            }
        },
        {
            "id": "qYIGsjS5rt",
            "type": "header",
            "data": {
                "text": "Key features",
                "level": 3
            }
        },
        {
            "id": "XV87kJS_H1",
            "type": "list",
            "data": {
                "style": "unordered",
                "items": [
                    "It is a block-styled editor",
                    "It returns clean data output in JSON",
                    "Designed to be extendable and pluggable with a simple API"
                ]
            }
        },
        {
            "id": "AOulAjL8XM",
            "type": "header",
            "data": {
                "text": "What does it mean «block-styled editor»",
                "level": 3
            }
        },
        {
            "id": "cyZjplMOZ0",
            "type": "paragraph",
            "data": {
                "text": "Workspace in classic editors is made of a single contenteditable element, used to create different HTML markups. Editor.js <mark class=\"cdx-marker\">workspace consists of separate Blocks: paragraphs, headings, images, lists, quotes, etc</mark>. Each of them is an independent contenteditable element (or more complex structure) provided by Plugin and united by Editor's Core."
            }
        },
        {
            "type": "delimiter",
            "data": {}
        },
    ],
    version: "2.30.8",
};

interface EditorProps {
    initialData?: OutputData;
    id: number;
}

interface EditorState {
    isLoading: boolean;
    isDirty: boolean;
}

const MyTextEditor: React.FC<EditorProps> = ({ initialData, id }) => {
    const editorInstance = useRef<EditorJS | null>(null);
    const [editorData, setEditorData] = useState(initialData);
    const [isReadOnly, setIsReadOnly] = useState(true);
    const [state, setState] = useState<EditorState>({
        isLoading: false,
        isDirty: false,
    });
    const saveTimeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);

    const debouncedSave = (data: OutputData) => {
        if (saveTimeoutRef.current) {
            clearTimeout(saveTimeoutRef.current);
        }

        saveTimeoutRef.current = setTimeout(async () => {
            await handleSave(data, false);
        }, 5000);
    };

    useEffect(() => {
        initEditor();
        return () => {
            if (saveTimeoutRef.current) {
                clearTimeout(saveTimeoutRef.current);
            }
            if (editorInstance.current?.destroy) {
                editorInstance.current?.destroy();
                editorInstance.current = null;
            }
        };
    }, [editorData, isReadOnly]);

    const initEditor = () => {
        if (!editorInstance.current) {
            const editor = new EditorJS({
                holder: "editorjs-container",
                tools: EDITOR_JS_TOOLS,
                data: editorData || INITIAL_DATA,
                onReady: () => {
                    console.log("Editor.js is ready to work!");
                    editorInstance.current = editor;
                },
                onChange: async (api) => {
                    setState(prev => ({ ...prev, isDirty: true }));
                    const data = await api.saver.save();
                    debouncedSave(data);
                },
                placeholder: "Type '/' for commands",
                readOnly: isReadOnly,
            });
        }
    }

    const extractHeader = (data: OutputData) => {
        const firstBlock = data['blocks']?.[0];
        return firstBlock?.type === "header" ? firstBlock.data.text : null;
    };

    const handleSave = async (data?: OutputData, reinitialize: boolean = true) => {
        try {
            setState(prev => ({ ...prev, isLoading: true }));
            if (!data && editorInstance.current) {
                data = await editorInstance.current.save();
            }
            if (!data) return;
            // Only update editor data if we want to reinitialize
            if (reinitialize) {
                setEditorData(data);
            }

            const title = extractHeader(data) || `Note_${id}`;
            const supabase = createClient();
            const { error } = await supabase
                .from('notes')
                .update({ content: data, title: title })
                .eq('id', id);

            if (error) throw error;

            setState(prev => ({ ...prev, isDirty: false }));
            toast.success("Content saved successfully!");
        } catch (error) {
            console.error("Error updating content:", error);
            toast.error("Failed to save data!");
        } finally {
            setState(prev => ({ ...prev, isLoading: false }));
        }
    };

    const toggleReadOnly = async () => {
        if (editorInstance.current) {
            await editorInstance.current.readOnly.toggle();
            setIsReadOnly((prev) => !prev);
            console.log("Read-Only Mode:", !isReadOnly);
        }
    };

    return <div className="p-2">
        <div className="flex gap-2 mb-4">
            <button
                className={`px-4 py-2 rounded-md transition-colors ${isReadOnly ? 'bg-blue-500 hover:bg-blue-600' : 'bg-gray-500 hover:bg-gray-600'
                    } text-white disabled:opacity-50`}
                onClick={toggleReadOnly}
            >
                {isReadOnly ? "Edit" : "Cancel"}
            </button>
            <button
                className="px-4 py-2 rounded-md bg-green-500 hover:bg-green-600 text-white disabled:opacity-50"
                onClick={() => handleSave()}
                disabled={isReadOnly || !state.isDirty || state.isLoading}
            >
                {state.isLoading ? "Saving..." : "Save"}
            </button>
            {state.isDirty && !isReadOnly && (
                <p className="text-sm text-gray-500 mt-2">Unsaved changes</p>
            )}
        </div>
        <div className="rounded-md" id='editorjs-container' />
        {/* <Image src='https://cdn.pixabay.com/photo/2023/07/31/16/37/sugar-apple-8161386_1280.jpg' alt={''} width={500} height={50} className='overflow-clip rounded-md object-fit m-6' /> */}
    </div>
}

export default MyTextEditor



// This should prevent the page from jumping during autosave while still maintaining the saving functionality. The editor will maintain its cursor position and scroll position during autosave operations.
// 1. Added a `reinitialize` parameter to `handleSave` that controls whether we update the editor state
// 2. Modified `debouncedSave` to call `handleSave` with `reinitialize: false` to prevent editor reinitialization during autosave
// 3. Only update `editorData` state when `reinitialize` is true (which will be the case for manual saves)
主要言語
TypeScript
スター
32k
フォーク
2.2k
平均マージ
2日 1時間
マージ済み PR(30日)
1

環境構築

このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

codex-team/editor.js のほかの issue

codex-team/editor.js の issue をすべて見る

似ている issue

TypeScript の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。