Disable text selection highlighting issue

Sorry for the inconvenient but my host provider blocked the page. Till Monday it will not be visible.

Hi,

I am working on the Build a Recipe Box project.
I try to resolve a UX-issue. When the user click on the edit icon (top left, next of the recipe name) will be open the edit mode.
Most of the edit event already works. At least I want to develop a grabbing event to move up/down the whole ingredients row if the user want. I tried first the html5 drag and drop event. It’s easy in first look and maybe works well with text or images but it was too hard to implement with table row. So I tried to develop my own drag and drop event for table row. I used mousedown-mouseover-mouseup events and as I see the table-row cangeable now but during the grabbing event the text in the table highlighted (selected).

My issue is:
I am not able to disable the select-event.

I tried

  • to set the css user-select property to “none” using javascript:
    dom.table.style.userSelect = "none";
  • to set the above property directly with css: user-select: none;

Nothing is works and I cannot to understand why… :(.
Here is a link to check the issue.

And this is the code of the function:

Summary
export const editRecipe = (recipes, recipeIndex) => {
//    console.log(event);
    const dom = {};
    const closeEditMode = () => {
        if (event.target === editContainer || 
            event.target === document.querySelector(".modalHack") ||
            event.target === document.querySelector(".delete")) {
            // simulate click event on edit icon:
            editIcon.click();
        }
    };
    
    // remove any html tags and spaces:
    const sanitizeInput = input => {
        return input.replace(/&nbsp;|<\/?[\w\s="/.':;#-\/]+>|&lt;\/?[\w\s="/.':;#-\/]+&gt;/gi, "").trim();
    };
    
    const changeName = () => {
        const updated = sanitizeInput(event.target.textContent);
        if (updated !== recipes[recipeIndex].name) {
            recipes[recipeIndex].name = updated;
            localStorage.setItem("myRecipes", JSON.stringify(recipes));
        } 
    };
    
    const ingredientsArray = recipes[recipeIndex].content.ingredients;
    const ingredientsTable = () => {
        let rows = "";
        ingredientsArray.forEach((indgredients, tableRow) => {
            let td = "";
            indgredients.forEach((data, tableColumn) => {
                td += `<td contenteditable="true" data-row="${tableRow}" data-column="${tableColumn}">${data}</td>`;
            });
            rows += `<tr>${td}<td class="delete-ingredient-row" data-row="${tableRow}">&times;</td></tr>`;
            
        });
        const table = `
            <table class="editable">
                <tr>
                    <th>Qty.</th>
                    <th>Unit</th>
                    <th>Name</th>
                </tr>
                ${rows}
                <tr>
                    <td contenteditable="true" data-row="${ingredientsArray.length}" data-column="${0}"></td>
                    <td contenteditable="true" data-row="${ingredientsArray.length}" data-column="${1}"></td>
                    <td contenteditable="true" data-row="${ingredientsArray.length}" data-column="${2}"></td>
                </tr>
            </table>
        `;
        ingredients.innerHTML = table;
        
        // add events to the table:
        dom.tdAll = document.querySelectorAll("[id^=content] ul table td");
        dom.trAll = document.querySelectorAll("[id^=content] ul table tr");
        dom.tdAll.forEach((cell, index) => {
            if ((index + 1) % 4 === 0) {
                // these cells are the delete buttons:
                cell.addEventListener("click", deleteIngredients);
                cell.addEventListener("mouseover", hoverDeleteIngredients);
                cell.addEventListener("mouseout", hoverDeleteIngredients);
            } else {
                cell.addEventListener("blur", changeIngredients);
            }
        });
        dom.trAll.forEach(row => {
            row.addEventListener("mousedown", grabIngredientsRow);
            row.addEventListener("mouseup", dropIngredientsRow);
        });
        
        
    };
    
    const changeIngredients = () => {
//        console.log(event)
        const row = + event.target.dataset.row;
        const column = + event.target.dataset.column;
        const updated = sanitizeInput(event.target.textContent);
        if (row === ingredientsArray.length) {
            if (column === 2) {
                // new ingredient name added to the list:
                const quantity = sanitizeInput(event.target.previousElementSibling.previousElementSibling.textContent);
                const unit = sanitizeInput(event.target.previousElementSibling.textContent);
                const ingredient = sanitizeInput(event.target.textContent);
                ingredientsArray.push([quantity, unit, ingredient]);
                console.log("new row added", ingredientsArray);
                // refresh table:
                return ingredientsTable();
            }
        } else if (updated !== ingredientsArray[row][column]) {
            ingredientsArray[row][column] = updated;
            localStorage.setItem("myRecipes", JSON.stringify(recipes));
        }
    };
    
    const hoverDeleteIngredients = () => {
        header.parentElement.classList.toggle("delete-mode-on");
        if (event.type === "mouseover") {
            header.textContent = "Are you sure You want to DELETE this row?";
            event.path[0].previousSibling.classList.add("delete-mode-on");
            event.path[0].previousSibling.previousSibling.classList.add("delete-mode-on");
            event.path[0].previousSibling.previousSibling.previousSibling.classList.add("delete-mode-on");
            
        } else {
            header.textContent = "Edite mode: ON";
            event.path[0].previousSibling.classList.remove("delete-mode-on");
            event.path[0].previousSibling.previousSibling.classList.remove("delete-mode-on");
            event.path[0].previousSibling.previousSibling.previousSibling.classList.remove("delete-mode-on");
        }
    };
    
    const deleteIngredients = () => {
        console.log(event);
        const row = event.target.dataset.row;
        ingredientsArray.splice(row, 1);
        console.log(ingredientsArray);
        localStorage.setItem("myRecipes", JSON.stringify(recipes));
        // refresh table:
        ingredientsTable();
    };
    
    let currentRow;
    const grabIngredientsRow = () => {
//        console.log(event);
        dom.table = document.querySelector("[id^=content] ul table");
        // disable text selection highlighting:
        dom.table.style.userSelect = "none";
        console.log("table.style user-select is:",dom.table.style.userSelect);
        // change cursor style to grabbing:
        dom.table.style.cursor = "grabbing";
        currentRow = event.target.dataset.row;
        // add hover event while grabbed the row:
        dom.trAll.forEach(row => {
            row.addEventListener("mouseover", moveIngeridientRow);
        });
    };
    
    const moveIngeridientRow = () => {
        // remove other events to avoid collision:
        dom.tdAll.forEach((cell, index) => {
            if ((index + 1) % 4 === 0) {
                // these cells are the delete buttons:
                cell.removeEventListener("click", deleteIngredients);
            } else {
                cell.removeEventListener("blur", changeIngredients);
            }
        });
        const hoverRow = event.target.dataset.row;
        
        if (hoverRow < currentRow) {
            // move up the row
            [ingredientsArray[currentRow], ingredientsArray[hoverRow]] = [ingredientsArray[hoverRow], ingredientsArray[currentRow]];
            currentRow = hoverRow;
            ingredientsTable();
            dom.trAll[currentRow].addEventListener("mousedown", grabIngredientsRow());
        } else {
            [ingredientsArray[currentRow], ingredientsArray[hoverRow]] = [ingredientsArray[hoverRow], ingredientsArray[currentRow]];
            currentRow = hoverRow;
            ingredientsTable();
            dom.trAll[currentRow].addEventListener("mousedown", grabIngredientsRow());
        }
    };
    
    const dropIngredientsRow = () => {
        // remove grabbing cursor style:
        dom.table.style.cursor = "pointer";
        dom.trAll.forEach(row => {
            row.removeEventListener("mouseover", moveIngeridientRow);
        });
    };
    console.log(event);
    const bgColor = event.path[4].style.backgroundColor,
          textColor = event.path[3].style.color || "black",
          editContainer = event.path[2],
          editIcon = event.target,
          modalHack = document.querySelector(".modalHack"),
          nextButton = event.path[4].children[1],
          backButton = event.path[4].parentElement.previousElementSibling.children[1].children[1],
          recipeName = event.path[3].children[1],
          ingredients = event.path[3].children[3],
          leftSideContent = event.path[4].parentElement.previousElementSibling.children[1].children[0]
    ;
    // toggle edit mode style on/off:
    document.getElementById("nav-container").classList.toggle("hide");
    recipeName.classList.toggle("editable");
    // create table to edit ingredients:
    ingredientsTable();
    event.path[3].children[5].classList.toggle("editable");
    backButton.classList.toggle("hide");
    editContainer.classList.toggle("edit-mode-on");
    header.parentElement.classList.toggle("edit-mode-on");
    modalHack.classList.toggle("hide");
    if (editIcon.dataset.editmode === "off") {
        nextButton.style.display = "none"; // class hide cannot to use because the flip-book class use display property
        // show the edit style input fields:
        // style the edit form:
        const hexBgColor = namedColorToHex(bgColor), 
              hexTextColor = namedColorToHex(textColor)
        ;
        const changeStyleForm = `
            <div id="editStyle" class="editShadow">
                <div>
                    <h1>Change image</h1>
                    <p class="editShadow">
                        <label for="image-url">Change url</label>
                        <input id="url" type="url" name="url" placeholder="http://example.com..." />
                    </p>
                    <p class="editShadow">image size:<br />
                        <input id="none" type="radio" name="position" value="none" />
                        <label for="none">None</label>
                        <input id="cover" type="radio" name="position" value="cover" />
                        <label for="cover">Cover</label>
                        <input id="hundred" type="radio" name="position" value="100% 100%" />
                        <label for="hundred">100%</label>
                    </p>
                    <h1>Change recipe style</h1>
                    <p class="editShadow">
                        <label for="bg-color">Change background color</label>
                        <input id="bg-color" type="color" name="bg-color" value="${hexBgColor}" />
                    </p>
                    <p class="editShadow">
                        <label for="text-color">Change text color</label>
                        <input id="text-color" type="color" name="text-color" value="${hexTextColor}" />
                    </p>
                </div>
            </div>
        `;
        leftSideContent.innerHTML = changeStyleForm;
        const editStyle = document.getElementById("editStyle");
        editStyle.style.backgroundColor = bgColor;
        editStyle.style.color = textColor;
        // switch on edit mode:
        editIcon.dataset.editmode = "on";
        editContainer.setAttribute("title", "Click to close Edit Mode")
        // turn editable the content page:
        recipeName.setAttribute("contenteditable", true); // Name of recipe        
        event.path[3].children[5].setAttribute("contenteditable", true); // directions
        // change header:
        header.textContent = "Edite mode: ON";
        
        // add listener to check if the user click outside of the book: --> editmode off:
        window.onclick = closeEditMode;
        
        // add blur event to all editable field:
        recipeName.addEventListener("blur", changeName);
        
    } else {
        // switch off edit mode:
        
        // have to remove all event listener
        recipeName.removeEventListener("blur", changeName);
        dom.tdAll.forEach((cell, index) => {
            if ((index + 1) % 4 === 0) {
                // these cells are the delete buttons:
                cell.removeEventListener("click", deleteIngredients);
                cell.removeEventListener("mouseover", hoverDeleteIngredients);
                cell.removeEventListener("mouseout", hoverDeleteIngredients);
            } else {
                cell.removeEventListener("blur", changeIngredients);
            }
        });
        // remove click event from modal hack:
        window.onclick = "";
        
        // delete the Change Style input fields:
        ingredients.innerHTML = createIngredientsList(ingredientsArray);
        leftSideContent.innerHTML = "";
        nextButton.style.display = "block";
        editIcon.dataset.editmode = "off";
        editContainer.setAttribute("title", "");
        recipeName.setAttribute("contenteditable", false); 
        ingredients.setAttribute("contenteditable", false); 
        event.path[3].children[5].setAttribute("contenteditable", false);
        header.innerHTML = "Build a Recipe Box";
    }
};

In the grabIngredientsRow() function I set the user-select property to “none” but not works…

Any idea?

P.s. I found one solution: I set the user-select property to body instead of the table. Now this issue is gone… but the question is till open: Why not works on the table tag?