如何保存对多个 contenteditable 元素的更改?

How to save changes on multiple contenteditable elements?

我想让我的照片标题可编辑,然后将更改保存在本地存储中。它适用于第一张照片,但当我想编辑另一张照片时,更改将保存到第一张照片而不是我编辑的照片。 这是我的代码的一部分,我在其中显示带标题的照片:

<div class="gallery">
   <a href="<?=$path1; ?>">
      <img src="<?= $path1; ?>">
   </a>
   <div id="contenteditable">
      <p id="caption-photo" contenteditable> <?=pathinfo($path1, PATHINFO_FILENAME)?></p>
   </div>
</div>

保存修改的是我的js代码:

const editables = document.querySelectorAll("[contenteditable]");
        editables.forEach(el => {
        el.addEventListener("blur", () => {
            localStorage.setItem("dataStorage-" + el.id, el.innerHTML);
        })
        });
        for (var key in localStorage) {
            if (key.includes("dataStorage-")) {
                const id = key.replace("dataStorage-","");
                document.querySelector("#" + id).innerHTML = localStorage.getItem(key);
            }
        }    

这里有一个建议:假设您的 foreach 循环是:

<?=
    #Other php stuff here
    #You can define a counter variable:
    $counter = 1;
?>
<div class="gallery">
<?= foreach($some_records as $your_value){ ?>
    <a href="<?=$path1; ?>">
    <img src="<?= $path1; ?>">
    </a>
<div id="contenteditable">
    #Which you can append to the caption-photo
    #So the id of the first will be: caption-photo1
    <p id="caption-photo<?= $counter ?>" contenteditable> <?=pathinfo($path1, PATHINFO_FILENAME)?></p>
<?=
        $counter++;
        #At the end of each loop, increase the value of counter by 1
    }
?>
</div>

也不要介意 foreach 循环,我确定它与你的不匹配,但只需在 id 上附加一个数字的方面,这样你就可以获得不同的值


好吧,因为那行不通,这里是上面的一个片段,但没有使用 short_open_tags(<?= ... =>),而是我只使用了 php(<?php ... ?>) 标签原样: 首先尝试 运行 代码并检查日志

<?php
    $some_records = ["record1.jpg", "record2.jpg", "record3.jpg", "record4.jpg", "record5.jpg"];
    $counter = 1;
?>

<div class="gallery">
    <?php foreach ($some_records as $path) {
        echo '<a href="' . $path . '">
            <img src="' . $path . '">
        </a>
        <div id="contenteditable">
            <p id="caption-photo' . $counter . '" contenteditable>' . pathinfo($path, PATHINFO_FILENAME) . '</p>
        </div>';
        $counter++;
    }
    ?>
</div>

<script>
    document.addEventListener("DOMContentLoaded", function(){
        const editables = document.querySelectorAll("[contenteditable]");
        editables.forEach(el => {
            el.addEventListener("blur", () => {
                localStorage.setItem("dataStorage-" + el.id, el.innerHTML);
                console.log(el.innerHTML);
                //When you change the value, it is logged
            })
        });
        /* The loop below does not work as expected */
        // for (var key in localStorage) {
        //     if (key.includes("dataStorage-")) {
        //         const id = key.replace("dataStorage-", "");
        //         document.querySelector("#" + id).innerHTML = localStorage.getItem(key);
        //  ---- The above direct way of selecting the element does not work
        //         console.log(localStorage.getItem(key));
        //     }
        // }
        /* 1 is added to editables.length because the counter starts
        from 1 and not 0 */
        for(var i = 1; i < editables.length+1; i++){
            console.log("dataStorage-caption-photo" + i + " => " + localStorage.getItem("dataStorage-caption-photo" + i));
            //This logs all the values associated with the elements
        }
    });
</script>

以上代码中的元素是这样设置的: 如您所见,计数器工作正常。

并且根据注释掉的循环,这部分 returns 是一个错误,因为 "direct" 访问元素的方法不起作用:

这里有一些代码可以帮助您: 将这个放在 php 文件

中的所有内容之前
if(isset($_GET["old_name"]) && isset($_GET["new_name"])){
    $old_name = $_GET["old_name"];
    // The same variables in the `GET` request
    $new_name = $_GET["new_name"];
    // Also remember the extension of the files
    $ext = ".jpeg";
    // As for rename you have to give the full path
    // If your folder is uploads/, and the file is oldname.jpg
    // Then it should be rename("uploads/" . $old_name  $ext, "uploads/" . $new_name . $ext);
    // Just provide the full path to the file you are renaming
    // If the images are within the same directory, its ok as it is
    echo rename($old_name . $ext, $new_name . $ext);
    // returns true if successful
    // returns false if not successful
    // This is the "responseText" that Javascript will be logging
}

并在 <script> 标签的开头添加此功能:

function update_file(old_name, new_name) {
    var update = new XMLHttpRequest();
    // Initialize the request
    update.open("GET", "?old_name=" + old_name + "&new_name=" + new_name, true);
    // Put the variables in the request which will be captured by php
    update.onreadystatechange = function() {
        if (this.readyState == this.DONE && this.status == 200) {
            if (this.responseText != null) {
                if(this.responseText){
                    console.log("Renamed successfully!");
                }
                else {
                    console.log("Error renaming!");
                }
            }
            else {
                console.log("Not sent!!");
            }
        }
    };
    update.send();
}

然后把这段代码改成这样:

    const editables = document.querySelectorAll("[contenteditable]");
    editables.forEach(el => {
        var curr = el.innerHTML;
        // Get the current value before it changes
        el.addEventListener("blur", () => {
            localStorage.setItem("dataStorage-" + el.id, el.innerHTML);
            console.log(el.innerHTML);
            update_file(curr, el.innerHTML);
            // Call the XMLHttpRequest with the values
        })
    });

这应该让您抢先了解如何使用 XHR 请求(如果您不使用它们的话)...这只是因为您要从 javascript 到 [=13] 获取数据=] 它必须通过 GETPOST ...但是从 phpjavascript 的数据就像将 php 标签和里面的值一样简单

此外,到处都有字面上的评论,因此您可以阅读并理解它的工作原理和原因。不要犹豫,寻求帮助。

------------

好的,在你处理这个的时候,这里有一个提示——在 foreach 循环中,你可以像这样回显一个隐藏的输入:... <input type="hidden" name="directory-' . $counter . ' " value=" ' . pathinfo($path1, PATHINFO_DIRNAME) . ' "/>(假设这段代码在 foreach loop) ...以及它下面的相同隐藏输入用于扩展名,因为图像可能有不同的扩展名 --(PATHINFO_EXTENSION) 那么你有 "directory-(number)" 而不是 "directory-(number)" ] ...然后更新发送扩展名和目录的请求 - [update_info(old_name, new_name, extension, directory)] ...意味着您还更新了XHR 请求也发送那些...在 php 端,您只需将目录与文件名和收到的扩展名连接起来。

现在,当谈到从隐藏输入中实际获取值时,请记住有一个计数器,因此根据以特定数字结尾的 el.id 的值,您可以获取名称以相同数字结尾的隐藏输入(这在 forEach 循环中)例如对于扩展的值,取其 name 属性以 "extension-" 开头的值和以与 el.id 相同的值结尾 ... 对目录

也执行此操作

另一种选择是将 el.nextElementSibling.value 作为第一个隐藏输入,然后是下一个隐藏输入,这些将是目录和扩展名的值... 我实际上可以将它们全部写在代码中,但这对您来说不是一个学习过程,编码愉快:) ...希望您能理解上述解释中的概念

好吧,让我打开你的思路......也请原谅我的许多答案,我只是喜欢全面的答案: 创建一个名为 update_file.php 的新 php 文件并将其放在同一目录中。然后把处理请求的php代码放到这个文件中。 为什么这很重要? 因为我们依赖于从 php 文件收到的 responseText,它会告诉我们请求是否成功。并且,当处理请求的 php 与 html 在同一个文件中时,请求将 return 包含页面 html 内容的响应 ---你可以自己试验一下,看看如果你把下面的代码放在同一个文件中,responseText 将是页面中的全部代码,这不是我们想要的,我们想要 0 或 1 ...好的,够了 说: 把它放在 update_file.php 与当前文件相同的目录中:

<?php

    if(isset($_GET["old_name"]) && isset($_GET["new_name"]) && isset($_GET["directory"]) && isset($_GET["extension"])){
        $old_name = $_GET["old_name"];
        $new_name = $_GET["new_name"];
        // The new values from the GET request
        $directory = $_GET["directory"];
        $extension = $_GET["extension"];
        // echo $directory;
        // echo $extension;
        // Concatenate the values with the directories

        // 1 thing to note, if the image is in the current directory,
        // the value you will receive for $directory will be "."
        // So the method below ensures flexibility with different directories
        $dot = ".";
        // $dot is added before the extension
        $slash = "/";
        // $slash is added after the directory name
        // if ".", it will be "./' -- for current directory
        // if "some/directory" , it will be "some/directory/"
        // Then add to the file name and the extension
        $full_oldname = $directory . $slash . $old_name . $dot . $extension;
        $full_newname = $directory . $slash . $new_name . $dot . $extension;
        echo rename($full_oldname, $full_newname);
    }

?>

那么这是您当前的文件:

<?php
    $some_records = ["record1.jpeg", "uploads/record2.jpeg", "images/subdirectory1/record3.jpeg", "images/record4.jpg", "images/subdirectory2/record5.jpg"];
    // Assuming your directory has subdirectories
    $counter = 1;

?>

<div class="gallery">
    <?php foreach ($some_records as $path) {
        echo '<a href="' . $path . '">
            <img src="' . $path . '">
        </a>
        <div id="contenteditable">
            <p id="caption-photo' . $counter . '" contenteditable>' . pathinfo($path, PATHINFO_FILENAME) . '</p>
            <input type="hidden" name="directory-"' . $counter . '" value="' . pathinfo($path, PATHINFO_DIRNAME) . '" />
            <input type="hidden" name="extension-"' . $counter . '" value="' . pathinfo($path, PATHINFO_EXTENSION) . '" />
        </div>';
        // Note the 2 hidden inputs above
        $counter++;
    }
    ?>
</div>

<script>
    function update_file(old_name, new_name, directory, extension) {
        // The function has been updated with the new parameters
        var update = new XMLHttpRequest();
        update.open("GET", "update_file.php?old_name=" + old_name + "&new_name=" + new_name + "&directory=" + directory + "&extension=" + extension, true);
        // The XHR request has also been updated with the new values
        update.onreadystatechange = function() {
            if (this.readyState == this.DONE && this.status == 200) {
                if (this.responseText != null) {
                    console.log(this.responseText);
                    // This will either be 0 or 1, if correct values have been passed to it
                    if(this.responseText == 1){
                        console.log("Renamed successfully!");
                    }
                    else {
                        console.log("Error renaming!");
                    }
                }
                else {
                    console.log("Not sent!!");
                }
            }
        };
        update.send();
    }

    document.addEventListener("DOMContentLoaded", function(){
        const editables = document.querySelectorAll("[contenteditable]");
        editables.forEach(el => {
            var curr = el.innerHTML;
            // The next element after "el" is the first hidden input with the directory
            var dir = el.nextElementSibling.value;
            // The next element after the above hidden input is the second hidden input with the extension
            // Note the 2 ".nextElementSibling" accessors
            var ext = el.nextElementSibling.nextElementSibling.value;
            // To get the value of any input(that can lie within a form control) use "[element].value'

            console.log(ext);
            console.log(dir);
            el.addEventListener("blur", () => {
                localStorage.setItem("dataStorage-" + el.id, el.innerHTML);
                console.log(el.innerHTML);
                update_file(curr, el.innerHTML, dir, ext);
            })
        });
        for(var i = 1; i < editables.length+1; i++){
            console.log("dataStorage-caption-photo" + i + " => " + localStorage.getItem("dataStorage-caption-photo" + i));
        }
    });
</script>

也看了评论,这里的重点是:

  1. 如何添加具有相应值的隐藏输入元素
  2. 如何使用 javascript
  3. 访问值
  4. 如何将它们添加到 XHR 请求中,然后使用 php
  5. 捕获它们
  6. 最后,如何连接它们并获得完整路径

希望对您有所帮助


这是您可以使用的一些开发人员工具,我不确定您使用的是哪种浏览器,但您可能会发现类似的东西,转到控制台选项卡,然后单击设置并启用 [=19 的日志记录=] 在您移除对任何 contenteditable 元素的关注并确认它们是否正在发送后,您将能够立即看到请求