You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular Material浮动标签文本截断:Ajax加载后Notch宽度计算异常排查

问题描述

我在用Angular Material组件,页面初始加载时输入框的浮动标签功能正常,但通过Ajax调用把HTML响应追加到指定div后,浮动标签对应的notch宽度计算异常。同时Ajax请求完成后控制台出现CORS错误,不确定是CORS导致(比如阻止CSS加载)还是DOM加载相关问题。

Textbox.TS

public intializeElements = (): void => {
    const textFields1 = [].map.call(document.querySelectorAll('.mdc-text-field'), function (el) {
        return new MDCTextField(el);
    });
    const helperTexts1 = [].map.call(document.querySelectorAll('.mdc-text-field-helper-text'), function (el) {
        return new MDCTextFieldHelperText(el);
    });
    this.initializeLabelPosition();
    this.listenOnValidationAttributeChange();
    this._jQueryValidationExtension.setupShowErrors(this.showErrorCallback);
}

private listenOnChromeAutoFillPrefill = (): void => {
    var outlinedTextbox = this;
    setInterval(function () {
        $('input.mdc-text-field__input:not(.phone-otp-phone-number-field__input)').each(function () {
            var elem = $(this);
            if (elem.val() || elem.is(":-webkit-autofill")) {
                outlinedTextbox.floatLabelAbove(this);
            }
        })
    }, 500);
}

public initializeLabelPosition = (): void => {
    var outlinedTextbox = this;

    $('input.mdc-text-field__input:not(.phone-otp-phone-number-field__input)').each(function () {
         // 检查初始值和自动填充的预填充功能
         const elem = $(this);
    if (!elem.val() && !elem.is(":-webkit-autofill")) return;
    outlinedTextbox.floatLabelAboveWithoutInputCheck(elem[0]);
        });
    $('div.mdc-text-field-helper-line').hide();
}

public listenOnValidationAttributeChange = (): void => {
    var _outlinedTextBox = this;
    $('input.mdc-text-field__input:not(.phone-otp-phone-number-field__input)').attrchange(function (source, id, value) {
        if (source == "aria-invalid") {
            _outlinedTextBox._validationStatus.setValidationStatus(id, value, $(this)[0].name);
        }
    });

    // 处理文本框失焦事件
    $('label.mdc-text-field:not(.phone-otp-phone-number-field)').attrchange(function (source, id, value) {
        if (source == "class") {
            if (value.indexOf("mdc-text-field--focused") === -1) {
                _outlinedTextBox._validationStatus.setValidationStatus(id, _outlinedTextBox._validationStatus.isInvalid(id));
            }
        }
    });

    $('span.phone-otp-helper-text').attrchange(function (source, id, value) {
        if ($(this).hasClass("field-validation-error")) {
            $(this).removeClass("field-validation-error");
        }
    });
}
public floatLabelAboveWithoutInputCheck = (elem: HTMLElement): void => {
    var $label = $(elem).parent();
    var $notchOutline = $label.children(".mdc-notched-outline");
    var $notch = $notchOutline.children(".mdc-notched-outline__notch");
    var $floatingLabel = $notchOutline.children(".mdc-notched-outline__notch").children(".mdc-floating-label");

    if (!$floatingLabel.hasClass("mdc-floating-label--float-above")) $floatingLabel.addClass("mdc-floating-label--float-above");
    if (!$label.hasClass("mdc-text-field--label-floating")) $label.addClass("mdc-text-field--label-floating");
    if (!$notchOutline.hasClass("mdc-notched-outline--notched")) $notchOutline.addClass("mdc-notched-outline--notched");
    var notchWidth = this.getFloatingLabelWidth($floatingLabel) / 1.3333 + 8;
    $notch.css("width", notchWidth.toString());
}

private getFloatingLabelWidth = ($elem: JQuery<HTMLElement>): number => {
    var html_org = $elem.html();
    var html_calc = '<span>' + html_org + '</span>';
    $elem.html(html_calc);
    var width = $elem.find('span:first').width();
    $elem.html(html_org);
    return width;
};

Ajax 操作代码

getCaptcha= (userAction): any => {
    let self = this;
    let $form = this._form.$form;
    var clientQueryString = self.getClientQueryString();
    this._httpClient.sendHttpRequestAsync(
        HttpMethod.Get,
        `getCaptcha?userAction=${userAction}&${clientQueryString}`,
        false,
        DataType.Text,
        ContentType.FormUrlEncoded,
        null,
        this.getCsrfInHeader($form)
    ).then(response => {
        var strResponse = response.toString();
        if (strResponse.startsWith('{')) {
            var jsonResponse = JSON.parse(response.toString());
            if (typeof jsonResponse?.data?.status === 'boolean' && !jsonResponse.data.status) {
                $form.find('.cpt-section').html('');
                this._form.showError(jsonResponse?.data?.messages);
            }
        } else {
            $form.find('.cpt-section').html(strResponse);
            
        }
    })
        .catch((err) => {
            console.error('Get Department: ', err);
        });
}

文本框响应HTML

<div class="clearfix captcha-container">
<label for="imageText_abba3833-398b-4703-94e6-a72bd48aed87" class="mdc-text-field text-field mdc-text-field--outlined phone-otp-label">
    <input id="imageText_abba3833-398b-4703-94e6-a72bd48aed87" class="mdc-text-field__input" aria-labelledby="lblImage_abba3833-398b-4703-94e6-a72bd48aed87" aria-describedby="captchaHelperText_abba3833-398b-4703-94e6-a72bd48aed87" aria-controls="captchaHelperText_abba3833-398b-4703-94e6-a72bd48aed87" type="text" data-val="true" data-val-regex="输入的字符与图片不符,请输入图片中显示的字符以继续。" data-val-regex-pattern="^([a-zA-Z0-9]{5,7})$" data-val-required="请输入图片中显示的字符以继续。" name="ImageText" value="" />
    <span class="mdc-notched-outline">
        <span class="mdc-notched-outline__leading"></span>
        <span class="mdc-notched-outline__notch">
            <span class="mdc-floating-label" id="lblImage_abba3833-398b-4703-94e6-a72bd48aed87">输入上方显示的文本</span>
        </span>
        <span class="mdc-notched-outline__trailing"></span>
    </span>
</label>
<div class="mdc-text-field-helper-line phone-otp-helper-line">
    <span class="mdc-text-field-helper-text mdc-text-field-helper-text--validation-msg phone-otp-helper-text field-validation-valid" id="captchaHelperText_abba3833-398b-4703-94e6-a72bd48aed87" aria-hidden="true" data-valmsg-for="ImageText" data-valmsg-replace="true"></span>
</div>

显示效果

  • 加载完成后文本框状态:
    文本框初始显示状态
  • 聚焦文本框时状态:
    文本框聚焦显示状态

解决方案

1. 排除CORS影响

CORS错误仅限制跨域数据交互,不会影响CSS加载,当前notch宽度异常和CORS无关,可忽略该错误对本问题的影响。

2. 修复动态DOM初始化问题

Ajax加载的新DOM元素未执行MDCTextField初始化逻辑,这是核心问题:

  • 修改intializeElements方法,支持指定目标容器,避免重复初始化旧元素:
public intializeElements = (target?: JQuery): void => {
    const container = target || $(document);
    // 初始化目标容器内的MDCTextField组件
    const textFields1 = [].map.call(container.find('.mdc-text-field').get(), function (el) {
        return new MDCTextField(el);
    });
    const helperTexts1 = [].map.call(container.find('.mdc-text-field-helper-text').get(), function (el) {
        return new MDCTextFieldHelperText(el);
    });
    // 初始化标签位置
    if (target) {
        target.find('input.mdc-text-field__input:not(.phone-otp-phone-number-field__input)').each(function () {
            const elem = $(this);
            if (!elem.val() && !elem.is(":-webkit-autofill")) return;
            this.floatLabelAboveWithoutInputCheck(elem[0]);
        });
        target.find('div.mdc-text-field-helper-line').hide();
    } else {
        this.initializeLabelPosition();
    }
    this.listenOnValidationAttributeChange();
    this._jQueryValidationExtension.setupShowErrors(this.showErrorCallback);
}
  • 在Ajax回调中插入HTML后,调用初始化方法:
// 原代码中$form.find('.cpt-section').html(strResponse);之后添加:
const $cptSection = $form.find('.cpt-section');
$cptSection.html(strResponse);
this.intializeElements($cptSection);

3. 修正宽度计算时机

临时span的宽度计算可能因DOM未完全渲染出错,改用requestAnimationFrame确保元素渲染后再计算:

public floatLabelAboveWithoutInputCheck = (elem: HTMLElement): void => {
    var $label = $(elem).parent();
    var $notchOutline = $label.children(".mdc-notched-outline");
    var $notch = $notchOutline.children(".mdc-notched-outline__notch");
    var $floatingLabel = $notchOutline.children(".mdc-notched-outline__notch").children(".mdc-floating-label");

    if (!$floatingLabel.hasClass("mdc-floating-label--float-above")) $floatingLabel.addClass("mdc-floating-label--float-above");
    if (!$label.hasClass("mdc-text-field--label-floating")) $label.addClass("mdc-text-field--label-floating");
    if (!$notchOutline.hasClass("mdc-notched-outline--notched")) $notchOutline.addClass("mdc-notched-outline--notched");
    
    // 等待DOM渲染完成后计算宽度
    requestAnimationFrame(() => {
        var notchWidth = this.getFloatingLabelWidth($floatingLabel) / 1.3333 + 8;
        $notch.css("width", notchWidth.toString());
    });
}

4. 修正选择器语法错误

原listenOnChromeAutoFillPrefill方法中选择器缺少闭合括号,已在上方代码中修正:

// 修正前:
$('input.mdc-text-field__input:not(.phone-otp-phone-number-field__input')
// 修正后:
$('input.mdc-text-field__input:not(.phone-otp-phone-number-field__input)')

内容的提问来源于stack exchange,提问作者ramkee gurrala

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 19:05:56