Получение атрибута элемента с состоянием (.active)

#javascript #angular #typescript

#javascript #угловой #typescript

Вопрос:

У меня возникли проблемы с проверкой активного состояния атрибута элемента. Я попробовал ниже, но он вернул false, даже если элемент имеет атрибут в активном состоянии — ( .c-banner.active присутствует)

 ngAfterViewInit() {
    const bannerElm = document.getElementById("banner");
    const isActive = bannerElm amp;amp; bannerElm.getAttribute("class")
      .indexOf("c-banner.active") !== -1;  // returned false
}
  

Комментарии:

1. Итак, каково значение bannerElm.getAttribute("class") ?

2. .getAttribute("class") собирается вернуть атрибут class точно таким, какое значение вы видите в DOM, так почему вы ищете c-banner.active с точкой и без пробела, как в css-селекторе?

3. он вернул «c-banner». и да, @BenSewards, вы правы, он вернул значение точно таким, как есть. Однако прикрепленным атрибутом является .c-banner.active, поэтому мой вопрос заключается в том, как мне проверить, был ли применен CSS-стиль .active state. Спасибо.

4. Я вижу много ответов о том, как правильно получить доступ к классу / classList . Предположительно, значение атрибута class при проверке в перехватчике жизненного цикла ngAfterViewInit равно c-banner . Возможно, элемент находится не в ожидаемом состоянии из-за операции, которая является асинхронной по своей природе? Как установить активное состояние баннера? можете ли вы попытаться получить класс элемента по истечении времени ожидания? (только для целей отладки)

5. @JanWendland Настройка времени в сочетании с classList.contains сработала для меня!! Спасибо вам, ребята!

Ответ №1:

Почему вы не используете classList и contains?

classList . contains();

  ngAfterViewInit() {
    const bannerElm = document.getElementById("banner");
    const isActive = bannerElm amp;amp; bannerElm.classList.contains('c-banner') amp;amp; bannerElm.classList.contains('active');
}
  

Комментарии:

1. @user11151040 Приведенный вами пример кода был неправильным. Это должен быть .classList.contains(). Несмотря на то, что вы были быстрее, но я выбираю другой ответ. К вашему сведению, вы всегда можете нажать «Редактировать» и повторно подтвердить ответ.

2. @displayname да, вы правы. После упоминания объяснения я забыл, как добавить его в содержимое, это очень забавно 🙂 Теперь отредактировал его.

Ответ №2:

Вы можете использовать classList.contains метод, чтобы проверить, имеет ли элемент класс active.

 ngAfterViewInit() {
    setTimeout(() => {
        const isActive = bannerElm amp;amp;
                         bannerElm.classList.contains('c-banner') amp;amp;
                         bannerElm.classList.contains('active');
    }, 1000);
}
  

[ОБНОВЛЕНО] оберните его в setTimeout(), и это сработало! На случай, если у кого-то еще возникла проблема с заказами инициализации компонента, с которой я сталкивался ранее.

Комментарии:

1. @user11151040 довел тебя до этого 🙂

2. @benshabatnoam да, но в целом это содержит трудозатраты 🙂

Ответ №3:

Помимо того, что это наилучший способ фактического доступа к значению атрибута класса, судя по вашим комментариям, проблема, по-видимому, носит асинхронный характер. Чтобы избежать использования хакерского setTimeout решения, я бы рекомендовал применить mutation observer и соответствующим образом реагировать на изменения атрибута. Вот один из способов, как это сделать.

PS: Отредактировал ответ, чтобы сделать его более подходящим для того, чего вы пытаетесь достичь. В конце концов, это не будет иметь большого значения, кроме того, что в случае изменения состояния баннера до истечения времени отмены, объект будет отправлен сразу, и вы потенциально сэкономите некоторое время ожидания по сравнению с использованием setTimeout

 bannerIsActive$ = new BehaviorSubject<boolean>(false);

ngAfterViewInit() {
    const banner = document.getElementById('banner');

    const observer = new MutationObserver((mutations: MutationRecord[]) => {
        const mutation = mutations[0];
        const classList: DOMTokenList = mutation.target['classList'];
        this.bannerIsActive$.next(mutation.attributeName === 'class' amp;amp; classList.contains('active'));
    });

    observer.observe(banner, {
        attributes: true
    });

    this.bannerIsActive$.pipe(
         debounce(isActive => timer(isActive ? 0 : 1000)),
         take(1)
    ).subscribe(isActive => {
       // do something with the banner state
    });
}
  

Комментарии:

1. @janwedland отлично! Вопрос. Что, если я хочу получить состояние только один раз и игнорировать изменение состояния? Я думал о subscribe и onsubscribe, но, возможно, есть лучшее решение!

2. Я бы сказал, это зависит от того, что вы пытаетесь сделать с состоянием. Используете ли вы состояние в своем шаблоне компонента? В этом случае вы могли бы просто инициализировать его с помощью false в вашем компоненте и попросить обратный вызов прослушивателя мутаций обновить его в случае, если баннер установлен активным.

3. Если сценарий немного сложнее, и вы хотите, например, сделать какой-то запрос на основе состояния баннера, вы могли бы создать BehaviourSubject в своем компоненте, инициализировать его с помощью false, подписаться на него со временем отмены, которое вам кажется разумным для выполнения асинхронной операции, и вызвать следующий метод субъекта из наблюдателя мутации. Затем вы можете подписаться на него, и независимо от того, изменилось состояние баннера или нет, вы получите текущее состояние баннера по истечении времени отмены. Но, может быть, вы можете подробнее рассказать о том, чего вы пытаетесь достичь? @displayname

4. @displayname отредактировал этот подход в моем ответе.

5. Я попробовал это, но это не зафиксировало активное состояние.

Ответ №4:

вам следует работать с ViewChild, а не обращаться к вашему DOM напрямую. оба будут работать, но это строгий способ сделать это.

 @ViewChild('banner') input; 
ngAfterViewInit() {
    var classAttributes= this.input.nativeElement.getAttribute('class');
    var result=classAttributesamp;amp;classAttributes('c-banner')
}