我在Angular4 /打字稿中构建SPA。 我有几个javascripts操纵DOM(添加/删除CSS类)我想要集成到应用程序。 最好的方法是什么? 目前,该应用程序的结构如下:
-app:
-app.component.ts
-app.module.ts
-menu.component.ts
-menu.view.html
-menu.css
menu.component.ts处理应用程序的数据显示。 我想整合以下脚本:
<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:
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:
<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>
有3个querySelector。我尝试通过定义指令来实现功能: 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);
}
}
如果我将选择器[menuElement]定义为等于上部导航,我将能够选择:
background = this.el.nativeElement.querySelector('.dropdownBackground');
或
triggers = this.el.nativeElement.querySelectorAll('.cool > li');
但事件监听器将绑定在整个导航器上,因此无法知道&#39; .cool&gt;中的哪一个。李&#39;被选中。 如果我将[menuElement]定义为&#39; .cool&gt;我没有办法选择&#39; .dropdownBackground&#39;或上部导航。
const background = document.querySelector('.dropdownBackground');
返回null(我也试过getElementByClassName等) 我无法在不同的指令中定义它们,因为它们是同时操作的。 我还尝试在HTML中添加该功能:
<li (mouseenter)="handleEnter($event.target)" (mouseleave)="handleLeave($event.target)">
但是这些功能无法在menu.directive中定义,也不会在menu.component中定义。 有什么可能的解决方案? - 按原样集成Javascript(在加载DOM后加载) - 选择几个DOM对象,但将EventListener绑定到其中一个。 非常感谢! 薇罗尼卡
答案 0 :(得分:0)
我想出来的方式: 在view.html中添加事件侦听器:
<li (mouseenter)="handleEnter($event.target);" (mouseleave)="handleLeave($event.target)">
在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);
}
}
和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');
}
}
这种简单的功能似乎太复杂了......