javascript - Flickeringbeing removed when using sortablejs with dcraw - Stack Overflow

I try to list files selected using multiple file input using the code below (also available at jsbin).

I try to list files selected using multiple file input using the code below (also available at jsbin). When trying to move li items to sort and nest them, they start flickering and sometimes are dropped from the list and completely removed from DOM. Can it be solved while keep using dcraw and SortableJS? It seems to work

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Bootstrap-Flask Demo Application</title>

    <style>
        .handle {
            cursor: -webkit-grabbing;
            cursor: move;
        }
    </style>
</head>

<body>
    <input class="form-control" id="files" multiple="" name="files" required="" type="file">
    <div id="files-selected" class="mb-3">
        <ul class="list-group very-first-parent nested-sortable h-200">

        </ul>
    </div>

    <script src=";></script>
    <script src=".js"></script>
    <script>
        var filesField;
        var filesSelected;
        document.addEventListener('DOMContentLoaded', function () {
            filesField = document.getElementById("files");
            filesSelected = document.getElementById("files-selected");

            filesField.addEventListener('change', function (e) {
                var files = e.target.files;

                var reader = [];
                var filenames = Array.from(files).map(file => file.name);

                filesSelected.querySelector('.very-first-parent').innerHTML = '';
                filenames.forEach((filename, index) => {
                    // alert('file selected');
                    var file = files[index];
                    var li = document.createElement('li');
                    li.className = 'list-group-item';
                    li.innerHTML = `
                    <div class="d-flex align-items-center">
                        <i class="handle bi-arrows-move"></i>
                        <button type="button" class="btn btn-danger me-3 ms-3" onclick="dismiss(this)">
                            <span aria-hidden="true">&times;</span>
                        </button>
                        <span class="file-name">${filename}</span>
                        <!--spinner--><div class="spinner spinner-border ms-auto" role="status"><span class="visually-hidden">Loading...</span></div><!--/spinner-->
                    </div>
                    <ul class="list-group nested-sortable"></ul>
                    `;
                    showPreview(file, li);
                    filesSelected.querySelector('.very-first-parent').appendChild(li);
                });

                makeNestedSortable();
            });
        });
        function isImage(file) {
            const imageTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/webp'];
            return imageTypes.includes(file.type);
        }

        function isRawImage(file) {
            const rawImageExtensions = ['.nef', '.cr2', '.tiff'];
            return rawImageExtensions.some(ext => file.name.toLowerCase().endsWith(ext));
        }

        function isVideo(file) {
            const videoTypes = ['video/mp4', 'video/quicktime'];
            return videoTypes.includes(file.type);
        }

        function replaceSpinner(string_old, string_new) {
            return string_old.replace(/<!--spinner-->.*<!--\/spinner-->/gi, string_new)
        }

        function showPreview(file, li) {
            if (isImage(file)) {
                const reader = new FileReader();
                reader.onload = function (e) {
                    li.innerHTML = replaceSpinner(li.innerHTML, `<img class="preview ms-auto" src="${e.target.result}" height="70" style="max-height: 70px;">`);
                };
                reader.readAsDataURL(file);
            } else if (isRawImage(file)) {
                // You can use a library like raw.js to decode raw images and show a preview
                const reader = new FileReader();
                reader.onload = (function (o) {
                    return function (e) {
                        // Get the image file as a buffer
                        var buf = new Uint8Array(e.currentTarget.result);

                        // Get the RAW metadata
                        var metadata = dcraw(buf, { verbose: true, identify: true }).split('\n').filter(String);

                        // Extract the thumbnail
                        var thumbnail = dcraw(buf, { extractThumbnail: true });

                        // Create thumbnail
                        var blob = new Blob([thumbnail], { type: "image/jpeg" });
                        var urlCreator = window.URL || window.webkitURL;
                        var imageUrl = urlCreator.createObjectURL(blob);
                        li.innerHTML = replaceSpinner(li.innerHTML, `<img class="preview ms-auto" src="${imageUrl}" height="70" style="max-height: 70px;">`);
                    };
                })(file);
                reader.readAsArrayBuffer(file);
            } else if (isVideo(file)) {
                li.innerHTML = replaceSpinner(li.innerHTML, `<video src="${URL.createObjectURL(file)}" class="ms-auto" controls height="70" style="max-height: 70px;"></video>`);
            } else {
                li.innerHTML = replaceSpinner(li.innerHTML, `<span class="preview ms-auto">Preview not available</span>`);
            }
        }

        function dismiss(target) {
            var li = target.closest('li');
            var fileToDelete = li.querySelector('span.file-name').textContent;

            var dt = new DataTransfer();
            Array.from(filesField.files).forEach((file, i) => {
                if (file.name !== fileToDelete)
                    dt.items.add(file)

                filesField.files = dt.files // this will trigger a change event
            });

            li.remove();
        }

        function makeNestedSortable() {
            nestedSortables = [].slice.call(document.querySelectorAll('.nested-sortable'));
            console.log(nestedSortables.length);

            for (var i = 0; i < nestedSortables.length; i++) {
                new Sortable(nestedSortables[i], {
                    group: 'nested',
                    animation: 150,
                    fallbackOnBody: true,
                    // invertSwap: true,
                    swapThreshold: 0.5
                });
            }

        }
    </script>
</body>

</html>

I try to list files selected using multiple file input using the code below (also available at jsbin). When trying to move li items to sort and nest them, they start flickering and sometimes are dropped from the list and completely removed from DOM. Can it be solved while keep using dcraw and SortableJS? It seems to work

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Bootstrap-Flask Demo Application</title>

    <style>
        .handle {
            cursor: -webkit-grabbing;
            cursor: move;
        }
    </style>
</head>

<body>
    <input class="form-control" id="files" multiple="" name="files" required="" type="file">
    <div id="files-selected" class="mb-3">
        <ul class="list-group very-first-parent nested-sortable h-200">

        </ul>
    </div>

    <script src="https://cdn.jsdelivr/npm/dcraw"></script>
    <script src="https://sortablejs.github.io/Sortable/Sortable.js"></script>
    <script>
        var filesField;
        var filesSelected;
        document.addEventListener('DOMContentLoaded', function () {
            filesField = document.getElementById("files");
            filesSelected = document.getElementById("files-selected");

            filesField.addEventListener('change', function (e) {
                var files = e.target.files;

                var reader = [];
                var filenames = Array.from(files).map(file => file.name);

                filesSelected.querySelector('.very-first-parent').innerHTML = '';
                filenames.forEach((filename, index) => {
                    // alert('file selected');
                    var file = files[index];
                    var li = document.createElement('li');
                    li.className = 'list-group-item';
                    li.innerHTML = `
                    <div class="d-flex align-items-center">
                        <i class="handle bi-arrows-move"></i>
                        <button type="button" class="btn btn-danger me-3 ms-3" onclick="dismiss(this)">
                            <span aria-hidden="true">&times;</span>
                        </button>
                        <span class="file-name">${filename}</span>
                        <!--spinner--><div class="spinner spinner-border ms-auto" role="status"><span class="visually-hidden">Loading...</span></div><!--/spinner-->
                    </div>
                    <ul class="list-group nested-sortable"></ul>
                    `;
                    showPreview(file, li);
                    filesSelected.querySelector('.very-first-parent').appendChild(li);
                });

                makeNestedSortable();
            });
        });
        function isImage(file) {
            const imageTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/webp'];
            return imageTypes.includes(file.type);
        }

        function isRawImage(file) {
            const rawImageExtensions = ['.nef', '.cr2', '.tiff'];
            return rawImageExtensions.some(ext => file.name.toLowerCase().endsWith(ext));
        }

        function isVideo(file) {
            const videoTypes = ['video/mp4', 'video/quicktime'];
            return videoTypes.includes(file.type);
        }

        function replaceSpinner(string_old, string_new) {
            return string_old.replace(/<!--spinner-->.*<!--\/spinner-->/gi, string_new)
        }

        function showPreview(file, li) {
            if (isImage(file)) {
                const reader = new FileReader();
                reader.onload = function (e) {
                    li.innerHTML = replaceSpinner(li.innerHTML, `<img class="preview ms-auto" src="${e.target.result}" height="70" style="max-height: 70px;">`);
                };
                reader.readAsDataURL(file);
            } else if (isRawImage(file)) {
                // You can use a library like raw.js to decode raw images and show a preview
                const reader = new FileReader();
                reader.onload = (function (o) {
                    return function (e) {
                        // Get the image file as a buffer
                        var buf = new Uint8Array(e.currentTarget.result);

                        // Get the RAW metadata
                        var metadata = dcraw(buf, { verbose: true, identify: true }).split('\n').filter(String);

                        // Extract the thumbnail
                        var thumbnail = dcraw(buf, { extractThumbnail: true });

                        // Create thumbnail
                        var blob = new Blob([thumbnail], { type: "image/jpeg" });
                        var urlCreator = window.URL || window.webkitURL;
                        var imageUrl = urlCreator.createObjectURL(blob);
                        li.innerHTML = replaceSpinner(li.innerHTML, `<img class="preview ms-auto" src="${imageUrl}" height="70" style="max-height: 70px;">`);
                    };
                })(file);
                reader.readAsArrayBuffer(file);
            } else if (isVideo(file)) {
                li.innerHTML = replaceSpinner(li.innerHTML, `<video src="${URL.createObjectURL(file)}" class="ms-auto" controls height="70" style="max-height: 70px;"></video>`);
            } else {
                li.innerHTML = replaceSpinner(li.innerHTML, `<span class="preview ms-auto">Preview not available</span>`);
            }
        }

        function dismiss(target) {
            var li = target.closest('li');
            var fileToDelete = li.querySelector('span.file-name').textContent;

            var dt = new DataTransfer();
            Array.from(filesField.files).forEach((file, i) => {
                if (file.name !== fileToDelete)
                    dt.items.add(file)

                filesField.files = dt.files // this will trigger a change event
            });

            li.remove();
        }

        function makeNestedSortable() {
            nestedSortables = [].slice.call(document.querySelectorAll('.nested-sortable'));
            console.log(nestedSortables.length);

            for (var i = 0; i < nestedSortables.length; i++) {
                new Sortable(nestedSortables[i], {
                    group: 'nested',
                    animation: 150,
                    fallbackOnBody: true,
                    // invertSwap: true,
                    swapThreshold: 0.5
                });
            }

        }
    </script>
</body>

</html>
Share Improve this question asked Nov 21, 2024 at 5:51 Mohammad QodratiMohammad Qodrati 652 silver badges11 bronze badges 3
  • does the answer provided resolve your issue because then it wil be greatly apreciated if you accept the answer – oelimoe Commented Dec 4, 2024 at 10:09
  • i dont know if you get notified when an answer is edited but there is a new solution and please accept when this is the solutiojn you needed and upvote if the solution was clear – oelimoe Commented Dec 12, 2024 at 9:17
  • No notification when edited. Thank you! It seems simply replacing the string does not work, but creating a DOM element does. – Mohammad Qodrati Commented Dec 14, 2024 at 3:17
Add a comment  | 

1 Answer 1

Reset to default 1

Solution

It took a while but after removing alot i found what was causing the problem.

The actual problem wasnt the ul inside of the sortable. what the actual problem was is the replace of the spinner.

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Bootstrap-Flask Demo Application</title>

<style>
    .handle {
        cursor: -webkit-grabbing;
        cursor: move;
    }
</style>
</head>
  <body>
    <input class="form-control" id="files" multiple="" name="files" required="" type="file">
<div id="files-selected" class="mb-3">
    <ul class="list-group very-first-parent nested-sortable h-200">

    </ul>
</div>

    <script src="https://cdn.jsdelivr/npm/dcraw"></script>
<script src="https://sortablejs.github.io/Sortable/Sortable.js"></script>
<script>
    var filesField;
    var filesSelected;
    document.addEventListener('DOMContentLoaded', function () {
        filesField = document.getElementById("files");
        filesSelected = document.getElementById("files-selected");

        filesField.addEventListener('change', function (e) {
            var files = e.target.files;

            var reader = [];
            var filenames = Array.from(files).map(file => file.name);

            filesSelected.querySelector('.very-first-parent').innerHTML = '';
            filenames.forEach((filename, index) => {
                // alert('file selected');
                var file = files[index];
                var li = document.createElement('li');
                li.className = 'list-group-item';
                li.innerHTML = `
                    <div class="d-flex align-items-center">
                        <i class="handle bi-arrows-move"></i>
                        <button type="button" class="btn btn-danger me-3 ms-3" onclick="dismiss(this)">
                            <span aria-hidden="true">&times;</span>
                        </button>
                        <span class="file-name">${filename}</span>
                        <div class="spinner spinner-border ms-auto" role="status"><span class="visually-hidden">Loading...</span></div>
                    </div>
                    <ul style="height: 100px;" class="list-group nested-sortable nested-2"></ul>
                    `;
                showPreview(file, li);
                filesSelected.querySelector('.very-first-parent').appendChild(li);
            });

            makeNestedSortable();
        });
    });
    function isImage(file) {
        const imageTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/webp'];
        return imageTypes.includes(file.type);
    }

    function isRawImage(file) {
        const rawImageExtensions = ['.nef', '.cr2', '.tiff'];
        return rawImageExtensions.some(ext => file.name.toLowerCase().endsWith(ext));
    }

    function isVideo(file) {
        const videoTypes = ['video/mp4', 'video/quicktime'];
        return videoTypes.includes(file.type);
    }


    function replaceSpinner(element_old, string_new) {
      const parser = new DOMParser();
      string_new = parser.parseFromString(string_new, 'text/html').body.firstChild;
      let elementChanged = element_old.getElementsByClassName('spinner')[0];
      return elementChanged.replaceWith(string_new).innerHTML;
    }

    function showPreview(file, li) {
        if (isImage(file)) {
            const reader = new FileReader();
            reader.onload = function (e) {
                li.innerHTML = replaceSpinner(li, `<img class="preview ms-auto" src="${e.target.result}" height="70" style="max-height: 70px;">`);
            };
            reader.readAsDataURL(file);
        } else if (isRawImage(file)) {
            // You can use a library like raw.js to decode raw images and show a preview
            const reader = new FileReader();
            reader.onload = (function (o) {
                return function (e) {
                    // Get the image file as a buffer
                    var buf = new Uint8Array(e.currentTarget.result);

                    // Get the RAW metadata
                    var metadata = dcraw(buf, { verbose: true, identify: true }).split('\n').filter(String);

                    // Extract the thumbnail
                    var thumbnail = dcraw(buf, { extractThumbnail: true });

                    // Create thumbnail
                    var blob = new Blob([thumbnail], { type: "image/jpeg" });
                    var urlCreator = window.URL || window.webkitURL;
                    var imageUrl = urlCreator.createObjectURL(blob);
                    li.innerHTML = replaceSpinner(li, `<img class="preview ms-auto" src="${imageUrl}" height="70" style="max-height: 70px;">`);
                };
            })(file);
            reader.readAsArrayBuffer(file);
        } else if (isVideo(file)) {
            li.innerHTML = replaceSpinner(li, `<video src="${URL.createObjectURL(file)}" class="ms-auto" controls height="70" style="max-height: 70px;"></video>`);
        } else {
            li.innerHTML = replaceSpinner(li, `<span class="preview ms-auto">Preview not available</span>`);
        }
    }

    function dismiss(target) {
        var li = target.closest('li');
        var fileToDelete = li.querySelector('span.file-name').textContent;

        var dt = new DataTransfer();
        Array.from(filesField.files).forEach((file, i) => {
            if (file.name !== fileToDelete)
                dt.items.add(file)

            filesField.files = dt.files // this will trigger a change event
        });

        li.remove(); 
    } 

    function makeNestedSortable() {
      nestedSortables = [].slice.call(document.querySelectorAll('.nested-sortable'));
      console.log(nestedSortables.length);
    
      for (var i = 0; i < nestedSortables.length; i++) {
        new Sortable(nestedSortables[i], {
          group:'nested' ,
          animation: 150,
          fallbackOnBody: true,
          invertSwap: true,
          swapThreshold: 0.5
        });
      }
    
    }
</script>
  </body>
</html>

I know the snipper is realy long this is because it took a while editing and at first i was editing your test example. so i dont know what i did change and what i didnt. what was important is the reload spinner function:

    function replaceSpinner(element_old, string_new) {
      const parser = new DOMParser();
      string_new = parser.parseFromString(string_new, 'text/html').body.firstChild;
      let elementChanged = element_old.getElementsByClassName('spinner')[0];
      return elementChanged.replaceWith(string_new).innerHTML;
    }

and how this is called/defined: li.innerHTML = replaceSpinner(li, '<img class="preview ms-auto" src="${imageUrl}" height="70" style="max-height: 70px;">');/<div class="spinner spinner-border ms-auto" role="status"><span class="visually-hidden">Loading...</span></div>. without the spinnner comments arround it. LOOKOUT: tjis example has quotes arround the img tag it should have been back-ticks but that ruins SO formatting.

Simple solution

You seem to have made a little mistake in your code that creates this problem your current code is:

li.innerHTML = `
   <div class="d-flex align-items-center">
      <i class="handle bi-arrows-move"></i>
      <button type="button" class="btn btn-danger me-3 ms-3" onclick="dismiss(this)">
         <span aria-hidden="true">&times;</span>
      </button>
      <span class="file-name">${filename}</span>
      <!--spinner--><div class="spinner spinner-border ms-auto" role="status">
         <span class="visually-hidden">Loading...</span>
      </div><!--/spinner-->
   </div>
   <ul class="list-group nested-sortable"></ul> <-----small mistake i believe
`;

what the mistake is that the added image or somethinge else also has an list insifr himself where it can be added on. So what happens is that the li tries to change place within the ul but also tries to place the li in the ul that is inside the li. this might be confusing but if you remove this line the flickering wil be helped or have the ul that is inside the li be added to another group that isnt 'nested' so it they are also lists but the parents cant be added to it.

if you want them to be able to be inside each other

You should consider removing and saving the ul inside the li while dragging and add it again when when its dropped.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1742311439a4419974.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信