Traversing the DOM in Angular 4 with jQuery - javascript

I need help in updating the CSS for selectedElement, currently, this works but only for the first element.
Basically, I have a link, when I click on it:
(click)="showUserDetails($event)"
I'm passing the x & y coordinates to my method and want to use this to update the css and enabled a boolean flag which shows the div:
showUserDetails(el: any) {
let target = el.target || el.srcElement;
let selectUsername = (document.getElementById('userDetailsInfo') as HTMLInputElement);
selectUsername.style.top = target.offsetY + "px";
selectUsername.style.left = target.offsetX + "px";
this.selected = true;
el.preventDefault();
}
Currently, the above code only seems to work for the first element and I'm trying to figure out a way to have it so based on the clicked element, go up the dom and find me the div with the id of 'userDetailsInfo' and apply the top/left values to that, and not just to the first item.

I actually solved it like so:
private hostEl: HTMLElement;
constructor(el: ElementRef) {
this.hostEl = el.nativeElement;
}
ngOnInit() {
window.addEventListener('scroll', this.scroll, true);
}
ngOnDestroy() {
window.removeEventListener('scroll', this.scroll, true);
}
//Get the header
let header = (document.querySelector('.header') as HTMLInputElement);
if (window.pageYOffset >= 100){
header.classList.add('sticky');
} else {
header.classList.remove('sticky');
}

Related

How to keep recording the height of an element using javascript even without reloading the page

In my javascript code I am taking the width of body and checking if the width of the body is at specific value then add or remove the onclick function from the elements but when i decrease/increase the size of the page the onclick function does not add/remove respectively according to the condition i just wanted to keep record of the width of the body so that without reloading the page the onclick function gets add/remove from the element.
let body = document.querySelector(".body");
let bodyWidth = body.offsetWidth;
if (bodyWidth < 785) {
var myOpener = false;
document.querySelector("#contact-
h2").setAttribute("onclick","openTheContact()");
document.querySelector("#service-
h2").setAttribute("onclick","openTheServices()");
}
if (bodyWidth > 785) {
document.querySelector("#contact-h2").removeAttribute("onclick");
document.querySelector("#service-h2").removeAttribute("onclick");
}
Instead of removing and adding all the time, you may check the size after click event:
function isWindowSmall() {
return window.innerWidth <= 785;
}
document.querySelector('#contact-h2').addEventListener('click', function () {
if (isWindowSmall()) {
openTheContact();
}
});
document.querySelector('#service-h2').addEventListener('click', function () {
if (isWindowSmall()) {
openTheServices();
}
});
You should try something like this.
Using onresize event.
window.onresize=function(){
let body = document.querySelector(".body");
let bodyWidth = body.offsetWidth;
console.log("bodyWidth"+bodyWidth)
if (bodyWidth < 785) {
var myOpener = false;
document.querySelector("#contact-h2").setAttribute("onclick","openTheContact()");
document.querySelector("#service-h2").setAttribute("onclick","openTheServices()");
}
if (bodyWidth > 785) {
document.querySelector("#contact-h2").removeAttribute("onclick");
document.querySelector("#service-h2").removeAttribute("onclick");
}
}

Vue Transition - JavaScript hooks

Based on this answer, I'm trying to create a Vue slideToggle component using transition.
The slideToggle is a classic paradigm in height animation. I've been successful so far...
I don't want to set a fixed max-height or height, I want the height to be dynamic.
My animation is working properly when displaying and hiding. The problem is in canceling while displaying or hiding.
How to handle the #enter-cancelled and the #leave-cancelled hooks? I'm new to vue transitions :)
I put my code inside this CodeSandBox: https://codesandbox.io/s/vue-template-3b7oj
I don't know if this helps you, but try this:
declare a new variable:
data() {
return {
height: null,
toggling: false
};
},
when the open or close function start, verify if toggling is true, if yes, just cancel, like this:
enter(el) {
if (this.toggling) return;
this.toggling = true;
this.height = el.offsetHeight;
el.style.overflow = "hidden";
el.style.height = 0;
el.style.paddingTop = 0;
el.style.paddingBottom = 0;
el.style.marginTop = 0;
el.style.marginBottom = 0;
setTimeout(() => {
el.style.transitionProperty = `height, margin, padding`;
el.style.transitionDuration = this.duration + "ms";
el.style.height = this.height + "px";
el.style.removeProperty("padding-top");
el.style.removeProperty("padding-bottom");
el.style.removeProperty("margin-top");
el.style.removeProperty("margin-bottom");
this.toggling = false;
});
},
Will be something like this:
https://codesandbox.io/s/vue-template-78n7t?fontsize=14
Maybe i broke your code, sorry, but will you get the idea.
As per the offical documentation Javacript transition hooks
the #leave-cancelled is only available with v-show, where are in your sample code you are using v-if, if you change this you will be able to capture the #leave-cancelled hook,#leave-cancelled and #enter-cancelled are triggered when enter or leave are interrupted, say you press the toggle button while opening as well as pressing the button while its closing.
Vue-Transition-Cancel
tl;dr
leave event cancels not yet called enter
enter cancels not yet called leave
cancel state is stored in
el._enterCb.cancelled
el._leaveCb.cancelled
analysis
Consider:
const cb = el._enterCb = once(() => {
if (expectsCSS) {
removeTransitionClass(el, toClass)
removeTransitionClass(el, activeClass)
}
if (cb.cancelled) {
if (expectsCSS) {
removeTransitionClass(el, startClass)
}
enterCancelledHook && enterCancelledHook(el)
} else {
afterEnterHook && afterEnterHook(el)
}
el._enterCb = null
})
source: _enterCb
So a naive solution to cancel #enter is
el => {el._enterCb.cancelled = true; done()}
This is what actually happens when one triggers leave
// call enter callback now
if (isDef(el._enterCb)) {
el._enterCb.cancelled = true
el._enterCb()
}
source: leave
Same applies to:
const cb = el._leaveCb = once(() => {
if (el.parentNode && el.parentNode._pending) {
el.parentNode._pending[vnode.key] = null
}
if (expectsCSS) {
removeTransitionClass(el, leaveToClass)
removeTransitionClass(el, leaveActiveClass)
}
if (cb.cancelled) {
if (expectsCSS) {
removeTransitionClass(el, leaveClass)
}
leaveCancelled && leaveCancelled(el)
} else {
rm()
afterLeave && afterLeave(el)
}
el._leaveCb = null
})
source: _leaveCb
One can check for possible assignments:
https://github.com/vuejs/vue/search?q=_leaveCb&unscoped_q=_leaveCb

Custom event on fragment change in Reveal.js

What would be the right way to programmatically add fragments to a slide in Reveal.js? I have a JavaScript widget on a slide that can go through 5 states, and I would like to go through them with fragment transitions.
I tried to achieve something similar with dummy fragments, like in the representative example below. This is intended to change the src of an image on fragment change. The example has an issue, though. When approaching a slide by pressing previous a number of times, the slide should start at its last fragment state. In the example, however, the image src starts in state 1, and doesn't know how to go further back on additional previous-steps.
Any pointers would be appreciated!
<img src="img1.png" id="my-image">
<span class="fragment update-img-src" data-target="my-image" data-src="img2.svg"></span>
<script>
Reveal.addEventListener('fragmentshown', function(event) {
if (event.fragment.classList.contains('update-img-src')) {
// Find the target image by ID
var target = document.getElementById(event.fragment.dataset.target);
// Keep a stack of previously shown images, so we can always revert back on 'fragmenthidden'
if (target.dataset.stack == null) {
target.dataset.stack = JSON.stringify([target.getAttribute('src')]);
}
target.dataset.stack = JSON.stringify([event.fragment.dataset.src, ...JSON.parse(target.dataset.stack)]);
// Update the image
target.setAttribute('src', event.fragment.dataset.src);
}
});
Reveal.addEventListener('fragmenthidden', function(event) {
if (event.fragment.classList.contains('update-img-src')) {
// Return to the previously shown image.
// Remove the top from the history stack
var target = document.getElementById(event.fragment.dataset.target);
if (target.dataset.stack == null) {
console.log('Trying to hide', event.fragment.dataset.src, 'but there is no stack.');
} else {
var [_, ...tail] = JSON.parse(target.dataset.stack);
target.dataset.stack = JSON.stringify(tail);
// Set the image source to the previous value
target.setAttribute('src', tail[0]);
}
}
});
</script>
Here's a hacky solution that I put together. It allows you to register any number of fragments on a slide with a callback function.
function registerFakeFragments(slide, fragmentIndices, stateChangeHandler) {
const identifier = `fake-${Math.round(1000000000*Math.random())}`;
let i = 1;
for (let fragmentIndex of fragmentIndices) {
const span = document.createElement('span');
span.dataset.target = identifier;
span.classList.add('fragment');
span.classList.add('fake-fragment');
span.setAttribute('data-fragment-index', JSON.stringify(fragmentIndex));
span.dataset.stateIndex = JSON.stringify(i);
slide.appendChild(span);
++i;
}
let currentState = null; // last reported state
const listener = () => {
const currentSlide = Reveal.getCurrentSlide();
if (currentSlide && currentSlide === slide) {
// Find the latest visible state
let state = 0;
currentSlide.querySelectorAll(`.fake-fragment.visible[data-target=${identifier}]`).forEach(f => {
const index = JSON.parse(f.dataset.stateIndex);
if (index > state) {
state = index;
}
});
// If the state changed, call the handler.
if (state != currentState) {
stateChangeHandler(state);
currentState = state;
}
}
};
Reveal.addEventListener('fragmentshown', listener);
Reveal.addEventListener('fragmenthidden', listener);
Reveal.addEventListener('slidechanged', listener);
}

Angular 2 add class conditionally

My target is to set or remove class depending on component boolean with Angular 2. For example: isRed = true > add class "red", if isRed = false > remove class "red". How is that possible? Code tried:
isRed: boolean;
constructor() {
$(document).scroll(function(){
var scrollTop = $(this).scrollTop();
if(window.location.hash) {
} else{
this.isRed = true;
}
if(scrollTop > 50) {
this.isRed = true;
}
else {
this.isRed = false;
}
});
}
and html:
[ngClass]="{red: isRed}"
The most concise way is IMHO
[class.red]="isRed"
update
The cause of your problem is function in
$(document).scroll(function(){
it should use arrow function
$(document).scroll(() => {
otherwise this within the callback won't point to the current class, but instead to the caller.
I'd suggest you try to avoid jQuery with Angular2. Use instead
class MyComponent {
constructor(private elRef:ElementRef) {}
isRed:boolean;
#HostListener('document:scroll', ['$event'])
onScroll(event:any) {
var scrollTop = this.elRef.nativeElement.scrollTop;
// or
$(this.elRef.nativeElement).scrollTop();
if(window.location.hash) {
} else{
this.isRed = true;
}
if(scrollTop > 50) {
this.isRed = true;
}
else {
this.isRed = false;
}
}
}
This is javascript so I would try something like:
isRed; // there's no need to initialize this variable
since the constructor has its own scope but hey,
do it if you wish so
Also it doesn't seem that you are working inside an object since you are using ; instead of , which means that you shouldnt use ":" but rather "="

How to give a div a higher z-index on click with JS?

I asked this question yesterday hopefully this one is clearer as I've now provided a working example of my store.
I'm developing a Shopify Theme. I've been using Timber as my base and I'm currently having a problem with my Quick Cart and Quick Shop/View drawers.
I have 2 drawers on the right of my site, 1 for the cart and 1 for the product quick view option. The drawers currently slide open - #PageContainer moves to the left on click to reveal each drawer.
As they are currently sitting on top of each other I need to alter the JS so that on click the z-index changes so that the correct drawer being called is highest in the stack.
I'm not great with JS so not sure if this is a simple task?
Here is a link to my Dev Store
JS:
timber.Drawers = (function () {
var Drawer = function (id, position, options) {
var defaults = {
close: '.js-drawer-close',
open: '.js-drawer-open-' + position,
openClass: 'js-drawer-open',
dirOpenClass: 'js-drawer-open-' + position
};
this.$nodes = {
parent: $('body, html'),
page: $('#PageContainer'),
moved: $('.is-moved-by-drawer')
};
this.config = $.extend(defaults, options);
this.position = position;
this.$drawer = $('#' + id);
if (!this.$drawer.length) {
return false;
}
this.drawerIsOpen = false;
this.init();
};
Drawer.prototype.init = function () {
$(this.config.open).on('click', $.proxy(this.open, this));
this.$drawer.find(this.config.close).on('click', $.proxy(this.close, this));
};
Drawer.prototype.open = function (evt) {
// Keep track if drawer was opened from a click, or called by another function
var externalCall = false;
// Prevent following href if link is clicked
if (evt) {
evt.preventDefault();
} else {
externalCall = true;
}
// Without this, the drawer opens, the click event bubbles up to $nodes.page
// which closes the drawer.
if (evt && evt.stopPropagation) {
evt.stopPropagation();
// save the source of the click, we'll focus to this on close
this.$activeSource = $(evt.currentTarget);
}
if (this.drawerIsOpen && !externalCall) {
return this.close();
}
// Add is-transitioning class to moved elements on open so drawer can have
// transition for close animation
this.$nodes.moved.addClass('is-transitioning');
this.$drawer.prepareTransition();
this.$nodes.parent.addClass(this.config.openClass + ' ' + this.config.dirOpenClass);
this.drawerIsOpen = true;
// Run function when draw opens if set
if (this.config.onDrawerOpen && typeof(this.config.onDrawerOpen) == 'function') {
if (!externalCall) {
this.config.onDrawerOpen();
}
}
if (this.$activeSource && this.$activeSource.attr('aria-expanded')) {
this.$activeSource.attr('aria-expanded', 'true');
}
// Lock scrolling on mobile
this.$nodes.page.on('touchmove.drawer', function () {
return false;
});
this.$nodes.page.on('click.drawer', $.proxy(function () {
this.close();
return false;
}, this));
};
Drawer.prototype.close = function () {
if (!this.drawerIsOpen) { // don't close a closed drawer
return;
}
// deselect any focused form elements
$(document.activeElement).trigger('blur');
// Ensure closing transition is applied to moved elements, like the nav
this.$nodes.moved.prepareTransition({ disableExisting: true });
this.$drawer.prepareTransition({ disableExisting: true });
this.$nodes.parent.removeClass(this.config.dirOpenClass + ' ' + this.config.openClass);
this.drawerIsOpen = false;
this.$nodes.page.off('.drawer');
};
return Drawer;
})();
Update
As instructed by Ciprian I have placed the following in my JS which is making the #CartDrawer have a higher z-index. I'm now unsure how I adapt this so that it knows which one to have higher dependant on which button is clicked. This is what I've tried:
...
Drawer.prototype.init = function () {
$(this.config.open).on('click', $.proxy(this.open, this));
$('.js-drawer-open-right-two').click(function(){
$(this).data('clicked', true);
});
if($('.js-drawer-open-right-two').data('clicked')) {
//clicked element, do-some-stuff
$('#QuickShopDrawer').css('z-index', '999');
} else {
//run function 2
$('#CartDrawer').css('z-index', '999');
}
this.$drawer.find(this.config.close).on('click', $.proxy(this.close, this));
};
...
The approach would be like this:
$('.yourselector').css('z-index', '999');
Add it (and adapt it to your needs) inside your onclick() function.
if you need to modify the z-index of your div when clicking a buton, you shoud put in this code on your onclick() function, else if you need to activate it when you looding the page you shoud put it on a $( document ).ready() function , the code is :
$('#yourID').css('z-index', '10');
You can use:
document.getElementById("your-element-id").style.zIndex = 5;
It's pure Javascript and sets the z-index to 5. Just bind this to onClick event!

Categories

Resources