简体   繁体   English

在Angular4中处理几个DOM元素

[英]Handling several DOM elements in Angular4

I'm building a SPA in Angular4/typescript. 我正在Angular4 / typescript中建立一个SPA。 I have several javascripts which manipulate DOM (add/remove CSS classes) which I want to integrate to the application. 我有几种JavaScript,它们可以处理要集成到应用程序中的DOM(添加/删除CSS类)。 What is the best way to do it? 最好的方法是什么? Currently the structure of the application is the following: 当前,应用程序的结构如下:

-app:
 -app.component.ts
 -app.module.ts
 -menu.component.ts
 -menu.view.html
 -menu.css

menu.component.ts handles data display of the application. menu.component.ts处理应用程序的数据显示。 I want to integrate the following script: 我想集成以下脚本:

<script>
  const triggers = document.querySelectorAll('.cool > li');
  const background = document.querySelector('.dropdownBackground');
  const nav = document.querySelector('.top');

  function handleEnter(){
    this.classList.add('trigger-enter');
    setTimeout(() => this.classList.contains('trigger-enter') &&
      this.classList.add('trigger-enter-active'), 150);
    background.classList.add('open');

    const dropdown = this.querySelector('.dropdown');
    const dropdownCords = dropdown.getBoundingClientRect();
    const navCoords = nav.getBoundingClientRect();

    const coords = {
      height: dropdownCords.height,
      width: dropdownCords.width,
      top: dropdownCords.top - navCoords.top,
      left: dropdownCords.left- navCoords.left
    };
    background.style.setProperty('width', `${coords.width}px`);
    background.style.setProperty('height', `${coords.height}px`);
    background.style.setProperty('transform', `translate(${coords.left}px, ${coords.top}px)`);
  }

  function handleLeave(){
    this.classList.remove('trigger-enter', 'trigger-enter-active');
    background.classList.remove('open');

  }
  triggers.forEach(trigger => trigger.addEventListener('mouseenter', handleEnter));
  triggers.forEach(trigger => trigger.addEventListener('mouseleave', handleLeave));
</script>

CSS: CSS:

  nav {
    position: relative;
    perspective: 600px;
  }
  nav ul {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    justify-content: center;
  }
  .cool > li {
    position: relative;
    display:flex;
    justify-content: center;
  }
  .cool > li > a {
    color: yellow;
    text-decoration: none;
    font-size: 20px;
    background: rgba(0,0,0,0.2);
    padding:10px 20px;
    display: inline-block;
    margin:20px;
    border-radius:5px;
  }
  .dropdown {
    opacity: 0;
    position: absolute;
    overflow: hidden;
    padding:20px;
    top:-20px;
    border-radius:2px;
    transition: all 0.5s;
    transform: translateY(100px);
    will-change: opacity;
    display: none;
  }
  .trigger-enter .dropdown {
    display: block;
  }
  .trigger-enter-active .dropdown {
    opacity: 1;
  }
  .dropdownBackground {
    width:100px;
    height:100px;
    position: absolute;
    background: #fff;
    border-radius: 4px;
    box-shadow: 0 50px 100px rgba(50,50,93,.1), 0 15px 35px rgba(50,50,93,.15), 0 5px 15px rgba(0,0,0,.1);
    transition:all 0.3s, opacity 0.1s, transform 0.2s;
    transform-origin: 50% 0;
    display: flex;
    justify-content: center;
    opacity:0;
  }
  .dropdownBackground.open {
    opacity: 1;
  }
  .arrow {
    position: absolute;
    width:20px;
    height:20px;
    display: block;
    background:white;
    transform: translateY(-50%) rotate(45deg);
  }

html: 的HTML:

<nav class="top" menuElement>
    <div class="dropdownBackground">
      <span class="arrow"></span>
    </div>

    <ul class="cool">
      <li>
        <a href="#">Some information</a>
        <div class="dropdown dropdown1">
        Info
        </div>
      </li>
      <li>
        <a href="#">More information</a>
        <ul class="dropdown">
          <li>
            some info
          </li>
        </ul>
      </li>
      <li>
        <a href="#">Other Links</a>
        <ul class="dropdown dropdown3">
          <li>some links</li>
        </ul>
      </li>
    </ul>
  </nav>

There are 3 querySelector. 有3个querySelector。 I tried to implement the functionality by defining a Directive: menu.directive.ts: 我试图通过定义一个指令来实现功能:menu.directive.ts:

import {Directive, HostListener, ElementRef, Renderer2} from '@angular/core';

@Directive({
  selector: '[menuElement]',
})

export class MenusDirective {
  constructor(
    private renderer: Renderer2,
    private el: ElementRef
  ){}
  //menuLi = this.el.nativeElement.querySelectorAll('.cool > li');
  background = this.el.nativeElement.querySelector('.dropdownBackground');

handleEnter(target){
  this.renderer.addClass(target, 'trigger-enter');
  setTimeout(() => target.classList.contains('trigger-enter') &&
    this.renderer.addClass(target, 'trigger-enter-active'), 150);
    this.background.classList.add('open');

  const dropdown = target.querySelector('.dropdown');
  const dropdownCords = dropdown.getBoundingClientRect();
  const filterNavCoords = this.el.nativeElement.getBoundingClientRect();

  const coords = {
    height: dropdownCords.height,
    width: dropdownCords.width,
    top: dropdownCords.top - filterNavCoords.top,
    left: dropdownCords.left- filterNavCoords.left
  };

  this.background.style.setProperty('width', `${coords.width}px`);
  this.background.style.setProperty('height', `${coords.height}px`);
  this.background.style.setProperty('transform', `translate(${coords.left}px, ${coords.top}px)`);

}

handleLeave(target){
  this.renderer.removeClass(target, 'trigger-enter');
  this.renderer.removeClass(target, 'trigger-enter-active');
  this.background.classList.remove('open');

}


@HostListener('mouseenter', ['$event']) onMouseEnter(event: Event) {
  this.handleEnter(event.target);
}

@HostListener('mouseleave', ['$event']) onMouseLeave(event: Event) {
  this.handleLeave(event.target);
}

}

If I define the selector [menuElement] equals to the upper nav, I'll be able to select : 如果我定义选择器[menuElement]等于上层导航,则可以选择:

background = this.el.nativeElement.querySelector('.dropdownBackground');

or 要么

triggers = this.el.nativeElement.querySelectorAll('.cool > li');

but event Listener would be bind on the whole nav, so there is no way to know which one of the '.cool > li' is selected. 但是事件监听器将绑定在整个导航上,因此无法知道选择了哪个“ .cool> li”。 If I define [menuElement] as '.cool > li', I don't find a way to select '.dropdownBackground' or the upper nav. 如果将[menuElement]定义为“ .cool> li”,则找不到选择“ .dropdownBackground”或上方导航栏的方法。

const background = document.querySelector('.dropdownBackground');

returns null (I tried also getElementByClassName, etc.) I can't define them in different Directives as they are manipulated simultaneous. 返回null(我也尝试过getElementByClassName等),因为它们是同时操作的,所以无法在不同的Directive中定义它们。 Also I tried to add the function in HTML: 我也尝试在HTML中添加功能:

<li (mouseenter)="handleEnter($event.target)" (mouseleave)="handleLeave($event.target)">

But the functions are not recognized as defined in menu.directive and not in menu.component. 但是无法识别在menu.directive和menu.component中定义的功能。 What are the possible solution? 有什么可能的解决方案? - integrate Javascript as is (loaded after the DOM is loaded) - select several DOM object, but bind a EventListener to one of them. -按原样集成Javascript(在DOM加载后加载)-选择多个DOM对象,但将EventListener绑定到其中之一。 Thanks a lot! 非常感谢! Veronika 维罗尼卡

The way I figured out to work: add Event listeners in view.html: 我想出的工作方式:在view.html中添加事件侦听器:

<li (mouseenter)="handleEnter($event.target);" (mouseleave)="handleLeave($event.target)">

Define handleEnter and handleLeave functions in menu.component.ts, add MenuDirective as a provider: menu.component.ts: 在menu.component.ts中定义handleEnter和handleLeave函数,将MenuDirective添加为提供者:menu.component.ts:

import { MenuDirective } from './menu.directive';

@Component({
  selector: 'menu',
  templateUrl: './menu.view.html',
  styleUrls: [ './app.component.css'],
  providers: [MenuDirective]
})
export class MenuComponent {
  constructor(
    private menuDirective: MenuDirective,
  ){}
handleEnter(target): void {
  this.menuDirective.handleEnter(target);
}

handleLeave(target): void {
  this.menuDirective.handleLeave(target);
}
}

and menu.directive.ts: 和menu.directive.ts:

import {Directive, HostListener, ElementRef, Renderer2} from '@angular/core';

@Directive({
  selector: '[menuElement]',
})

export class MenuDirective {
  constructor(
    private renderer: Renderer2,
    private el: ElementRef
  ){}

handleEnter(target){
  this.renderer.addClass(target, 'trigger-enter');
  setTimeout(() => target.classList.contains('trigger-enter') &&
    this.renderer.addClass(target, 'trigger-enter-active'), 150);
    this.el.nativeElement.querySelector('.dropdownBackground').classList.add('open');

  const dropdown = target.querySelector('.dropdown');
  const dropdownCords = dropdown.getBoundingClientRect();
  const filterNavCoords = this.el.nativeElement.getBoundingClientRect();
  const coords = {
    height: dropdownCords.height,
    width: dropdownCords.width,
    top: dropdownCords.top - filterNavCoords.top,
    left: dropdownCords.left- filterNavCoords.left
  };
  this.el.nativeElement.querySelector('.dropdownBackground').style.setProperty('width', `${coords.width}px`);
  this.el.nativeElement.querySelector('.dropdownBackground').style.setProperty('height', `${coords.height}px`);
  this.el.nativeElement.querySelector('.dropdownBackground').style.setProperty('transform', `translate(${coords.left}px, ${coords.top}px)`);

}

handleLeave(target){
  this.renderer.removeClass(target, 'trigger-enter');
  this.renderer.removeClass(target, 'trigger-enter-active');
  this.el.nativeElement.querySelector('.dropdownBackground').classList.remove('open');
}

}

It seems too complex for such simple feature... 如此简单的功能似乎太复杂了...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM