【问题标题】:Placeholder text is editable占位符文本是可编辑的
【发布时间】:2016-08-29 07:48:57
【问题描述】:

我的表单表现得很奇怪。我们系统中的其他表单表现得很好,但我的在 IE11 中做了一些微妙的奇怪的事情。这似乎是因为某些组件不能很好地共存。

我的问题是:如何阻止它做这些奇怪的事情?为什么会发生这些奇怪的事情?

我的表单在<table> 中显示房地产销售列表。每个销售都有自己的<tbody>,其中包含一些<tr>s。其中一个单元格包含一个文件输入,其中包含一个工具提示(来自 Bootstrap)。当表单首次显示时,我必须隐藏一些销售,我使用 display: none 或 JQuery .hide()。该表单还根据here 中的代码使用占位符和一些“强制输入修饰”(自动在强制输入旁边放置一个星号)。

通过消除过程,我发现了两件事可以阻止奇怪的事情发生:不隐藏任何包含文件输入的<tr>s,以及不使用任何强制输入修饰。不幸的是,我需要做这两件事。

奇怪的事情

  1. 初始插入符号位置:在最初聚焦的文本框(地址字段,预先填充)中,插入符号有时出现在文本的开头,有时出现在结尾
  2. 可编辑占位符:见下文
  3. 标签标题:浏览器标签文本卡在“Waiting for ...”
  4. 怪异行为的怪异模式:见下文

可编辑占位符

当我单击带有占位符的文本框时,正常行为是插入符号出现在文本框的左侧,并且(在 Chrome 中)占位符文本在插入符号的右侧仍然可见,或者(在 IE11 中)占位符文本不见了。但是当我的页面在 IE11 中表现异常时,插入符号出现在占位符之后:

...并且是可编辑的...

提交表单后,任何更新的占位符内容都不会随它一起提交,因此我的用户可能会输入他们认为会被保存的文本,但事实并非如此。

占位符的怪异也很微妙:它只发生在第一次鼠标点击页面时;在随后的单击或字段之间的键盘跳动中,事情会正常运行。

怪异行为的怪异模式

根据页面显示之前发生的情况,事情的运作方式会有所不同。

在第一次显示时,占位符是可编辑的。如果我点击占位符(即让插入符号出现在占位符文本中或之后的某处),刷新后页面的行为与我没有点击占位符时不同。

如果我首先单击一个带有占位符的文本框并且该占位符是可编辑的,那么在 F5 之后,最初聚焦的文本框将在开头具有插入符号。或者,如果最初聚焦的文本框 以插入符号开头,并且我不对表单做任何事情,则插入符号会在 F5 之后再次回到开头。如果最初聚焦的文本框末尾有插入符号,那么我可以编辑我的评论字段的占位符文本......这有点令人困惑。

我的尝试

我们通常使用this 使占位符在旧版浏览器中工作。我试过删除它,但没有任何区别。

我试过this script,但它只是让问题略有不同:插入符号移动到占位符的开头,但它仍然是可编辑的。

我试过删除nicefileinput,但这没有任何区别。

这是我的整个表格。 “评论”字段出现在表单的一半左右

@model OurCompany.Web.ViewModels.OurProductCompleteDesktopSalesComparisonViewModel
@{
    ViewBag.Title = "CompleteDesktopSalesComparison";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<link rel="stylesheet" href="~/content/css/typeahead.js-bootstrap.css">
<script type="text/javascript" src="~/Content/Scripts/DICurrencyInputFormatter.js"></script>

<section class="container-wrap">
<div class="container">
    @Html.Partial("~/Views/OurProduct/_CompleteHeaderPartial.cshtml")
    @using (Html.BeginForm("CompleteDesktopSalesComparison", "OurProduct", FormMethod.Post, new { enctype = "multipart/form-data" }))
    {
        @Html.ValidationSummary(false)
        @Html.AntiForgeryToken()
        @Html.HiddenFor(m => m.Valuation.OrderNumber)
        @Html.HiddenFor(m => m.Property.DIPID)
        @Html.HiddenFor(m => m.Property.FullAddress)
        @Html.HiddenFor(m => m.Valuation.CategoryCode)
        @Html.HiddenFor(m => m.Valuation.ValuationTypeCode)

        @Html.Partial("~/Views/OurProduct/_CompleteNavigationTabsPartial.cshtml")

        <div class="col-md-10 col-md-offset-1">
            <div class="row">&nbsp;</div>
            <div>
                @*Sales comparisons==============================================================================*@
                @Html.Hidden("saleCount", Model.ComparableSales.Count)
                <table class="table table-default">
                    @foreach (var sale in Model.ComparableSales)
                    {
                        var saleIndex = Model.ComparableSales.IndexOf(sale);
                        var isVisible = sale.Visible;

                        var shadedClass = saleIndex % 2 == 0 ? string.Empty : "shaded-rows";
                        var visibleStyle = isVisible ? string.Empty : "display: none;";
                        var saleTbodyId = string.Format("saleTbody{0}", saleIndex);
                        var selectedId = string.Format("selected{0}", saleIndex);
                        var visibleId = string.Format("visible{0}", saleIndex);

                        <tbody id="@saleTbodyId" class="@shadedClass" style="@visibleStyle">
                        @*Address, selected*@
                        <tr>
                            <td>
                                @Html.HiddenFor(m => m.ComparableSales[saleIndex].Sequence)
                                @Html.HiddenFor(m => m.ComparableSales[saleIndex].Visible, new{ id = visibleId})
                                Address
                            </td>
                            <td colspan="3">
                                @Html.TextBoxFor(m => m.ComparableSales[saleIndex].FullAddress, new {@class = "form-control tt-query", style = "width: 100%;", placeholder = "Full address of sale"})
                                @Html.ValidationMessageFor(m => m.ComparableSales[saleIndex].FullAddress)
                                @Html.HiddenFor(m => m.ComparableSales[saleIndex].Dipid)
                                <script>
                                    $("#Valuation_ComparableSales_@(saleIndex)__FullAddress").on("typeahead:selected typeahead:autocompleted", function(e, datum) {
                                        $("#Valuation_ComparableSales_@(saleIndex)__Dipid").val(datum.SearchKey);
                                    })
                                </script>
                            </td>
                            <td>@Html.DisplayNameFor(m => m.ComparableSales[saleIndex].Selected)</td>
                            <td>
                                @Html.CheckBoxFor(m => m.ComparableSales[saleIndex].Selected, new { id = selectedId })
                            </td>
                        </tr>
                        @*Date, Price*@
                        <tr>
                            <td>@Html.DisplayNameFor(m => m.ComparableSales[saleIndex].Date)</td>
                            <td>
                                <span class="di-mandatory-field-container di-mandatory-field-text" style="position: relative;">
                                    @Html.DropDownListFor(m => m.ComparableSales[saleIndex].Date,
                                        Model.GetSalesDateListInstance(Model.ComparableSales[saleIndex].Date),
                                        new { @class = "form-control" })
                                </span>
                            </td>
                            <td>@Html.DisplayNameFor(m => m.ComparableSales[saleIndex].PriceString)</td>
                            <td>
                                @Html.TextBoxFor(m => m.ComparableSales[saleIndex].PriceString,
                                    new { @class = "form-control currency-0-decimals", placeholder = "Sale price" })
                                @Html.ValidationMessageFor(m => m.ComparableSales[saleIndex].PriceString)
                                @Html.ValidationMessageFor(m => m.ComparableSales[saleIndex].Price)
                            </td>
                            <td></td>
                            <td></td>
                        </tr>
                        @*Floor Area, Bedrooms*@
                        <tr>
                            <td>@Html.DisplayNameFor(m => m.ComparableSales[saleIndex].FloorArea)</td>
                            <td>
                                @Html.TextBoxFor(m => m.ComparableSales[saleIndex].FloorArea,
                                    new { @class = "form-control", placeholder = "Floor Area" })
                                @Html.ValidationMessageFor(m => m.ComparableSales[saleIndex].FloorArea)
                            </td>
                            <td>@Html.DisplayNameFor(m => m.ComparableSales[saleIndex].Bedrooms)</td>
                            <td>
                                @Html.TextBoxFor(m => m.ComparableSales[saleIndex].Bedrooms,
                                    new { @class = "form-control", placeholder = "Bedrooms" })
                                @Html.ValidationMessageFor(m => m.ComparableSales[saleIndex].Bedrooms)
                            </td>
                            <td></td>
                            <td></td>
                        </tr>
                        @*Land Area, Bathrooms*@
                        <tr>
                            <td>@Html.DisplayNameFor(m => m.ComparableSales[saleIndex].LandArea)</td>
                            <td>
                                @Html.TextBoxFor(m => m.ComparableSales[saleIndex].LandArea,
                                    new { @class = "form-control", placeholder = "Land Area" })
                                @Html.ValidationMessageFor(m => m.ComparableSales[saleIndex].LandArea)
                            </td>
                            <td>@Html.DisplayNameFor(m => m.ComparableSales[saleIndex].Bathrooms)</td>
                            <td>
                                @Html.TextBoxFor(m => m.ComparableSales[saleIndex].Bathrooms,
                                    new { @class = "form-control", placeholder = "Bathrooms" })
                                @Html.ValidationMessageFor(m => m.ComparableSales[saleIndex].Bathrooms)
                            </td>
                            <td></td>
                            <td></td>
                        </tr>
                        @*Comment*@
                        <tr>
                            <td>@Html.DisplayNameFor(m => m.ComparableSales[saleIndex].Comment)</td>
                            <td colspan="5">
                                @Html.TextBoxFor(m => m.ComparableSales[saleIndex].Comment,
                                    new { @class = "form-control", placeholder = "Comments" })
                                @Html.ValidationMessageFor(m => m.ComparableSales[saleIndex].Comment)
                            </td>
                        </tr>
                        @*Image*@
                        <tr>
                            <td>
                                <p>Image</p>
                                <p>(@Html.DisplayFor(m => m.FileExtensionConstraintMessages["VALUATION_REPORT_SALE_IMAGE"]))</p>
                            </td>
                            <td colspan="5">
                                @if (Model.ComparableSales[saleIndex].Images != null)
                                {
                                    foreach (var file in Model.ComparableSales[saleIndex].Images)
                                    {
                                        <span>@Html.DisplayFor(f => file.FileName) (Created: @Html.DisplayFor(f => file.CreateDate))</span>
                                        <a href="@Url.Action("PropertyFile", "Files", new { orderNumber = Model.Valuation.OrderNumber, fileTypeCode = file.FileTypeCode, userFileId = file.Id })"><i class="fa fa-file fa-lg"></i></a>
                                        <br/>
                                    }
                                }
                                <span data-toggle="tooltip" data-placement="top" title="@Html.DisplayFor(m => m.FileUploadConstraintMessages["VALUATION_REPORT_SALE_IMAGE"])">
                                    <input type="file" name="sale@(saleIndex)Image"/>
                                </span>
                                @{ var validationMessageKey = String.Format("SaleComparisonImage{0}", saleIndex); }
                                @Html.ValidationMessage(validationMessageKey)
                            </td>
                        </tr>
                        @*Comparability*@
                        <tr>
                            <td>@Html.DisplayNameFor(m => m.ComparableSales[saleIndex].Comparability)</td>
                            <td colspan="5">
                                @Html.TextBoxFor(m => m.ComparableSales[saleIndex].Comparability,
                                    new { @class = "form-control", placeholder = "Comparability - e.g. Inferior, Comparable, Superior" })
                                @Html.ValidationMessageFor(m => m.ComparableSales[saleIndex].Comparability)
                            </td>
                        </tr>
                        </tbody>
                    }
                </table>

            </div>
            <div><input id="addSaleButton" type="button" value="Add a sale" class="btn btn-light" /></div>
        </div>

        @Html.Partial("~/Views/OurProduct/_CompleteNavigationButtonsPartial.cshtml")
    }
    <div class="row">&nbsp;</div>
</div>
<div class="push"></div>
</section>
<script src="~/Content/scripts/moment.js"></script>
<script src="~/Content/scripts/bootstrap.min.js"></script>
<script src="~/content/scripts/jquery.nicefileinput.min.js"></script>
<script src="~/content/scripts/jquery.placeholder.js"></script>
<script src="~/content/scripts/typeahead.js"></script>
<script>

    $("#addSaleButton").click(function() {
        var saleCount = $("#saleCount").val();
        for (var i = 0; i < saleCount; i++) {
            var saleTbodyId = "saleTbody" + i;
            if ($("#" + saleTbodyId + ":hidden").length > 0) {

                // make the sale visible
                $("#" + saleTbodyId).show();

                // check the Selected checkbox
                var selectedId = "selected" + i;
                $("#" + selectedId).prop("checked", true);

                // set the hidden value to ensure it shows after validation errors
                var visibleId = "visible" + i;
                $("#" + visibleId).val("True");

                // done one, don't do any more
                break;
            }
        }
    });

    $(".tt-query").typeahead({
        name: 'SearchValue',
        valueKey: 'SearchValue',
        limit: 20,
        remote: {
            url: '/properties/GetAddressSearchValues?partSearchValue=%QUERY'
        }
    });

    $(document).ready(function() {
        // tooltips
        $("span").tooltip({ placement: 'top' });

        // custom styling for file input
        $("input[type=file]").nicefileinput();

        // placeholder attribute for old browsers
        $('input, textarea').placeholder();

        $("#ComparableSales_0__FullAddress").focus();
    });

</script>

这是强制输入装饰 .js 文件

// Add a CSS class to mandatory text input fields.

// see http://www.robertgray.net.au/posts/2012/11/indicating-required-fields-with-twitter-bootstrap-and-aspnet-mvc-4#.VMqY0miUfeo
function markRequired() {
    // look at every mandatory field
    $('input[data-val-required]').each(function () {

        // not all should be treated
        if (!$(this).parent().hasClass("input-append") && !(this).hasAttribute("readonly")) {
            if ($(this).is("input:text")) {
                $(this).wrap("<div class='di-mandatory-field-container di-mandatory-field-text'>");

                //$(this).wrap("<div class='input-group'>");
                //$(this).after("<span class='input-group-addon'><i class='fa fa-asterisk required-asterisk'></i></span>");
                //$(this).addClass("di-mandatory-field-container");
                //$(this).addClass("di-mandatory-field-text");
                //$(this).wrap("<span>");
            }
        }
    });
}

function addFootnote() {
    var parentForm;

    // check the form for mandatory class (may be manually- or automatically-added)
    var firstMandatory = $('.di-mandatory-field-container').first();

    // find the form containing that element
    if (firstMandatory != null) {
        var parentFinder = firstMandatory[0];
        while (parentForm == null && parentFinder != null) {
            if (parentFinder.tagName.toLowerCase() == "form") {
                parentForm = parentFinder;
            }
            parentFinder = parentFinder.parentNode;
        }
    }

    // add the footnote
    if (parentForm != null) {
        var rubric = $('<span>', { text: "denotes a mandatory field", style: "position: absolute;" })
            .addClass("di-mandatory-field-footnote");
        rubric.appendTo($(parentForm).parent());
    }
}

$(document).ready(function () {
    markRequired();
    addFootnote();
});

【问题讨论】:

标签: html twitter-bootstrap internet-explorer-11 placeholder


【解决方案1】:

这只是答案的一半(仍然不知道为什么会发生奇怪的事情),但有人可能会发现这有帮助......

$(document).ready(function() {
    $("#ComparableSales_0__FullAddress").focus();
    $("#selected0").focus();
});

由于该错误的“微妙”性质,只需聚焦两次即可将其杀死。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-12
    • 1970-01-01
    • 2020-02-22
    • 2011-07-28
    • 2017-05-04
    • 2016-09-18
    相关资源
    最近更新 更多