最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Issue with Rebuilding Hidden Inputs After Drag‐and‐Drop Sorting Mixed “Links” and “Embeds” Using Sortable.js - Stac

programmeradmin4浏览0评论

I'm experiencing an issue on my dashboard where users can reorder a mixed list of items—some of which are “links” and others “embeds”—using Sortable.js. After reordering, I update the data-index attributes for each item and then rebuild the hidden input fields used for form submission. However, after saving the page, I sometimes find that one of the items (often a link) appears empty (its values are missing).

I suspect the problem lies in the way I re-index and rebuild the hidden inputs for the mixed content. In my implementation, I update the indexes for links and embeds separately but then rebuild the hidden fields using a unified loop over all .link-item elements. This may lead to an index mismatch between the rebuilt hidden fields and the actual order of items.

Below are the relevant parts of my code:

Javascript:

if (linksContainerEl) {
  new Sortable(linksContainerEl, {
    handle: '.drag-handle',
    animation: 400,
    onEnd: function () {
      // Update each item's data-index based on type
      let linkIndex = 0;
      let embedIndex = 0;
      $('#links-container .link-item').each(function () {
        const type = $(this).data('type');
        if (type === 'link') {
          $(this).attr('data-index', linkIndex);
          linkIndex++;
        } else if (type === 'embed') {
          $(this).attr('data-index', embedIndex);
          embedIndex++;
        }
      });

      // Rebuild hidden fields with the correct indexes
      $('#links-container .link-item').each(function (i) {
        const $item = $(this);
        const itemType = $item.data('type');

        if (itemType === 'link') {
          const iconClass    = $item.find('input[name^="links[icon_class]"]').val()     || '';
          const linkText     = $item.find('input[name^="links[link_text]"]').val()      || '';
          const linkUrl      = $item.find('input[name^="links[link_url]"]').val()       || '';
          const isPopup      = $item.find('input[name^="links[is_popup]"]').val()       || '0';
          const popupContent = $item.find('input[name^="links[popup_content]"]').val()  || '';
          const isEnabled    = $item.find('input[name^="links[enabledHidden]"]').val()  || '1';

          // Remove old link inputs
          $item.find('input[name^="links["]').remove();

          // Recreate them with the new index
          $item.append(`
            <input type="hidden" name="links[icon_class][${i}]" value="${iconClass}">
            <input type="hidden" name="links[link_text][${i}]" value="${linkText}">
            <input type="hidden" name="links[link_url][${i}]" value="${linkUrl}">
            <input type="hidden" name="links[is_popup][${i}]" value="${isPopup}">
            <input type="hidden" name="links[popup_content][${i}]" value="${popupContent.replace(/"/g, '&quot;')}">
            <input type="hidden" name="links[enabledHidden][${i}]" value="${isEnabled}">
          `);
        } 
        else if (itemType === 'embed') {
          const embedType    = $item.find('input[name^="embeds[embed_type]"]').val()    || '';
          const embedUrl     = $item.find('input[name^="embeds[embed_url]"]').val()     || '';
          const embedEnabled = $item.find('input[name^="embeds[enabledHidden]"]').val() || '1';

          // Remove old embed inputs
          $item.find('input[name^="embeds["]').remove();

          // Recreate them with the new index
          $item.append(`
            <input type="hidden" name="embeds[embed_type][${i}]" value="${embedType}">
            <input type="hidden" name="embeds[embed_url][${i}]" value="${embedUrl}">
            <input type="hidden" name="embeds[enabledHidden][${i}]" value="${embedEnabled}">
          `);
        }
      });

      // Rebuild items_order field
      const newOrder = [];
      $('#links-container .link-item').each(function () {
        const t = $(this).data('type');
        const i = $(this).data('index');
        newOrder.push(`${t}-${i}`);
      });
      $('#items_order').val(newOrder.join(','));
    },
  });
}

HTML Structure:

<div class="card mt-4">
  <div class="card-header d-flex justify-content-between align-items-center">
    <h2>
      <i class="fas fa-window-minimize fa-xs"></i> Buttons and Media
      <span class="tooltip-icon">
        <i class="fas fa-info-circle fa-sm"></i>
        <div class="custom-tooltip">
          <h6>Add links to your page as buttons with an icon and link name.</h6>
        </div>
      </span>
    </h2>
  </div>

  <div class="card-body">
    <div id="links-container">
      <?php foreach ($combined_items as $i => $item): ?>
        <?php if ($item['item_type'] === 'link'): ?>
          <div class="link-item mb-3 d-flex align-items-center" data-index="<?= $i; ?>" data-type="link" style="opacity: <?= ($item['is_enabled'] == 1) ? '1' : '0.5'; ?>;">
            <i class="fas fa-grip-lines ms-2 drag-handle"></i>
            <div class="link-content flex-grow-1 me-2">
              <div class="link-text"><?= htmlspecialchars($item['link_text']); ?></div>
              <div class="link-url"><?= htmlspecialchars($item['link_url']); ?></div>
            </div>
            <div class="link-icon">
              <i class="<?= htmlspecialchars($item['icon_class']); ?> fa-2x"></i>
            </div>
            <!-- Hidden fields for POST -->
            <input type="hidden" name="links[icon_class][]" value="<?= htmlspecialchars($item['icon_class']); ?>">
            <input type="hidden" name="links[link_text][]" value="<?= htmlspecialchars($item['link_text']); ?>">
            <input type="hidden" name="links[link_url][]" value="<?= htmlspecialchars($item['link_url']); ?>">
            <input type="hidden" name="links[enabledHidden][]" value="<?= $item['is_enabled']; ?>">
            <input type="hidden" name="links[is_popup][]" value="<?= $item['is_popup']; ?>">
            <input type="hidden" name="links[popup_content][]" value="<?= htmlspecialchars($item['popup_content']); ?>">
          </div>
        <?php elseif ($item['item_type'] === 'embed'): ?>
          <div class="link-item mb-3 d-flex align-items-center" data-index="<?= $i; ?>" data-type="embed" style="opacity: <?= ($item['is_enabled'] == 1) ? '1' : '0.5'; ?>;">
            <i class="fas fa-grip-lines ms-2 drag-handle"></i>
            <div class="link-content flex-grow-1 me-2">
              <div class="link-text"><?= htmlspecialchars($item['embed_type']); ?></div>
              <div class="link-url"><?= htmlspecialchars($item['embed_url']); ?></div>
            </div>
            <div class="link-icon">
              <i class="fas fa-play-circle fa-2x"></i>
            </div>
            <!-- Hidden fields for POST -->
            <input type="hidden" name="embeds[embed_type][<?= $i ?>]" value="<?= htmlspecialchars($item['embed_type']); ?>">
            <input type="hidden" name="embeds[embed_url][<?= $i ?>]" value="<?= htmlspecialchars($item['embed_url']); ?>">
            <input type="hidden" name="embeds[enabledHidden][<?= $i ?>]" value="<?= $item['is_enabled']; ?>">
          </div>
        <?php endif; ?>
      <?php endforeach; ?>
    </div>

    <button type="button" class="btn btn-dark" id="add-link-button">
      Add Button <i class="fas fa-plus"></i>
    </button>
    <button type="button" class="btn btn-primary ms-2" id="add-embed-button">
      Add Media <i class="fas fa-play-circle"></i>
    </button>
  </div>
</div>

Relevant PHP Code (Form Processing):

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // Retrieve the items_order field
    $items_order = $_POST['items_order'] ?? '';
    $sequence = explode(',', $items_order);
    $position = 1;
    
    foreach ($sequence as $seg) {
        list($type, $idxStr) = explode('-', $seg);
        $idx = (int)$idxStr;
        
        if ($type === 'link') {
            // Example: retrieve posted link data using the rebuilt indexes
            $link_text = $_POST['links']['link_text'][$idx] ?? '';
            $link_url  = $_POST['links']['link_url'][$idx] ?? '';
            // Process link data (insert/update in database)...
        } elseif ($type === 'embed') {
            $embed_type = $_POST['embeds']['embed_type'][$idx] ?? '';
            $embed_url  = $_POST['embeds']['embed_url'][$idx] ?? '';
            // Process embed data...
        }
        $position++;
    }
}

After sorting, the items_order hidden field is rebuilt based on the data-index attributes assigned to each item. However, because links and embeds are re-indexed separately (using different counters) but then rebuilt in a single loop (using the loop’s index), there can be a mismatch. For example, the generated order might be "embed-0,link-2" or "embed-0,link-0,embed-1,link-1" even when the DOM order is mixed. This causes the PHP code to reference the wrong indexes in the arrays (or even missing indexes), leading to empty values being submitted for one or more items.

Logs:

==== DEBUG POST DATA ====
POST: Array
(
    [items_order] => embed-0,link-2
    [icons_order] => 
    [icons] => Array
        (
            [icon_class] => Array
                (
                    [0] => icon icon-youtube-square
                    [1] => fab fa-twitch
                    [2] => icon icon-x
                    [3] => fab fa-pinterest
                )
            [icon_url] => Array
                (
                    [0] => 
                    [1] => 
                    [2] => 
                    [3] => 
                )
            [enabledHidden] => Array
                (
                    [0] => 1
                    [1] => 1
                    [2] => 1
                    [3] => 1
                )
            [is_popup] => Array
                (
                    [0] => 1
                    [1] => 0
                    [2] => 0
                    [3] => 0
                )
            [popup_content] => Array
                (
                    [0] => <p>asdadw</p>
                    [1] => <p><br></p>
                    [2] => <p><img src=&quot;data:image/jpeg;base64,...&quot;></p>
                    [3] => <p><br></p>
                )
        )
    [embeds] => Array
        (
            [embed_type] => Array
                (
                    [0] => أنغامي
                )
            [embed_url] => Array
                (
                    [0] => 
                    [1] => 
                )
            [enabledHidden] => Array
                (
                    [0] => 1
                )
        )
    [links] => Array
        (
            [icon_class] => Array
                (
                    [0] => fab fa-viber
                )
            [link_text] => Array
                (
                    [0] => فايبر
                )
            [link_url] => Array
                (
                    [0] => viber://chat?number=viber://chat?number=...
                )
            [enabledHidden] => Array
                (
                    [0] => 1
                )
            [is_popup] => Array
                (
                    [0] => 0
                )
            [popup_content] => Array
                (
                    [0] => <p><br></p>
                )
        )
)
DEBUG: items_order sequence: Array
(
    [0] => embed-0
    [1] => link-2
)
DEBUG Embed => idx: 0 | embed_type: أنغامي | embed_url:  | is_enabled: 1
DEBUG Link => idx: 2 | link_text:  | link_url:  | icon_class:  | is_enabled: 1 | is_popup: 0 | popup_cont: 

What might be causing the hidden input values to be emptied after sorting?

How can I modify my JavaScript code so that the hidden fields and the items_order field accurately reflect the current order of items, preserving all their values?

Any insights, suggestions, or alternative approaches would be greatly appreciated. Thanks in advance for your help!

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论