How to trigger animations in a React app based on the scroll position - reactjs

Let's say I need to add an element to the navbar when the user have scrolled past the header of the site. How can I do something like this in React without using jQuery?

You can do some thing like this: (this function was copied from my own react-sticky-dynamic-header that I created before: https://github.com/thinhvo0108/react-sticky-dynamic-header )
componentDidMount() {
var h1 = parseInt(this.refs.header.offsetHeight);
window.addEventListener('scroll', this._calcScroll.bind(this, h1));
}
componentWillUnmount() {
window.removeEventListener('scroll', this._calcScroll)
}
_calcScroll(h1) {
var _window = window;
var heightDiff = parseInt(h1);
var scrollPos = _window.scrollY;
if (scrollPos > heightDiff) {
// here this means user has scrolled past your header,
// you may rerender by setting State or do whatever
this.setState({
//stateKey: stateValue,
});
} else {
// here the user has scrolled back to header's territory,
// it's optional here for you to remove the element on navbar as stated in the question or not
this.setState({
//stateKey: stateValue,
});
}
}
render() {
return (
<div ref="header">YOUR HEADER HERE</div>
);
}
For a smooth animation when your element added or removed from the navbar, you can just add this into the element's CSS style:
#your-element{
transition: opacity 0.3s ease-in;
}
You can try to install my library to see if it can extend your needs:
https://www.npmjs.com/package/react-sticky-dynamic-header
Feel free to post here some errors if any, thanks

Related

Anchor tags to jump halfway down a page in NetSuite (SuiteCommerce Advanced)

Does NetSuite SuiteCommerce not support anchor tags? I want to include some links at the top of my page that let the viewer jump down to that category (on the same page). I know I have my code set up properly, but all that happens is that it tries to open a new page with no content on it:
https://www.prospectfastener.com/product-interchange
Using Anchor Tags with NetSuite SuiteCommerce
Bundled together a few answers from other Backbone related questions. This can be used in custom extensions or it can be placed in to SMT CMS HTML Content as an inline script.
Tested with SC 2022.1
// This breaks the function out of the try/catch for use on page
var scrollToAnchorFn = null;
// SB env keeps doubling loading inline scripts for testing
// This tests/sets its own property on the window to test against for that
if (typeof window.anchorSet === 'undefined') {
try {
window.anchorSet = true;
// Call scrollToAnchor with anchor ID whenever you want to jump between points on a page
const scrollToAnchor = (anchorId) => {
try {
var aTag = $(anchorId);
$('html,body').animate({
scrollTop: aTag.offset().top
},
'slow',
);
} catch (err) {
console.error(err);
}
};
const initAnchorScroll = () => {
// Test if jQuery is loaded yet, if not set timeout and repeat fn
if (window.jQuery) {
// Check if document is already loaded and ready, run code now if dom is ready
if (document.readyState === 'complete')
return scrollToAnchor(PageHash);
// Before images or styling is finished loading, move to part of dom requested which is ready
return $(document).ready(() => {
scrollToAnchor(PageHash);
});
}
setTimeout(function() {
initAnchorScroll();
}, 50);
};
// Get our page hash
// const PageHash = window.location.hash;
// Snippet demo hash
const PageHash = '#testAnchor';
// Initialize on page load if hash detected in url
// Since hashes return the #,
// we want a length greater than 1 to ensure there is an id
if (PageHash.length > 1) initAnchorScroll();
scrollToAnchorFn = scrollToAnchor;
} catch (err) {
console.error(err);
}
}
.fillerDiv {
height: 100vh;
width: 1000vw;
background-color: #fedd00;
}
#testAnchor {
background-color: #4285f4;
color: #ffffff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.2.3/jquery.min.js"></script>
<div class="container">
<div class="fillerDiv"></div>
<div id="testAnchor">this is test content down the page with anchor ID.
Go to Next Anchor</div>
<div class="fillerDiv "></div>
<div id="testAnchor2">Second anchor tag</div>
<div class="fillerDiv "></div>
</div>
Old Answer
I'm going to assume not, but I'd be happy to be wrong.
Note: Anchor Tags are not supported by Content Delivery.
NetSuite Applications Suite -- Understanding Content Delivery

React - Remove image based on position within window

I have a video running in the background of my app. The problem is when you go all the way to the bottom of the site, the video pokes out from underneath if you overscroll. I need to not see the video peak out from under the rest of the app.
I'm in React, so it's a bit tricky. I tried this:
let styles = "video-foreground"
function parallax(){
let ypos = window.pageYOffset;
if(ypos > 420) {
styles = "video-blackout"
} else {
styles = "video-foreground"
}
}
window.addEventListener('scroll', parallax);
CSS
.video-blackout {
display: none;
}
"styles" is the style I put on the video. But this doesn't work well. It sometimes doesn't get rid of the video for a few seconds. Other times it doesn't put the video back after you scroll back up for a few seconds. I'm not sure why the delay happens, but it does.
Is there a better way to do this?
This worked. Injecting the style was creating a delay. Doing it directly worked.
function parallax(){
let ypos = window.pageYOffset;
if(ypos > 420) {
document.getElementById('banner').style.display = 'none';
} else {
document.getElementById('banner').style.display = 'block';
}
}
window.addEventListener('scroll', parallax);

How to make successful calls to execCommand('copy') from React mouse event handler?

Summary
I wrote a small method to copy text to the clipboard, which works (in Chrome, Edge and IE11) if I trigger the code from a simple HTML button event handler like this:
class MyController
{
constructor()
{
...
testButton_.onclick = (event: MouseEvent) => { this.onTestButtonClicked(event); }
...
}
onTestButtonClicked(event: MouseEvent)
{
Utilities.copyTextToClipboard("Testing the clipboard functionality!");
}
...
}
where testButton_ is an HTMLButtonElement.
Now, when I call this same method from the event handler of a React component, the method fails in Edge and Chrome, but succeeds in IE11.
It appears that the document.execCommand("copy") call returns false.
Can someone please explain me how I can make a successful call to document.execCommand("copy") from within a React mouse event handler?
Note: I did read various other posts on here and on https://w3c.github.io/editing/execCommand.html#dfn-the-copy-command but since this is all called from within a user (right-)click event, I would expect this to work (as it does with the plain HTML event handler) also in React.
Details
(I'm using React and TypeScript by the way)
This is what's in the React component:
class DashboardItemComponent extends React.Component<DashboardItemComponentProps, DashboardItemComponentState>
{
static defaultProps: DashboardItemComponentProps = {
projectName: ""
};
constructor(props: DashboardItemComponentProps)
{
super(props);
this.onContextMenu = this.onContextMenu.bind(this);
}
render(): JSX.Element
{
return (
<div onContextMenu={this.onContextMenu}>
<div className="inner">
<header>
<h2>{this.props.projectName}</h2>
</header>
<div>more text</div>
</div>
</div>
);
}
protected onContextMenu(e: React.MouseEvent)
{
e.preventDefault();
Utilities.copyTextToClipboard("testing 1 2 3");
}
}
And this is what's in the copyTextToClipboard method of my Utilities class (parts gathered from other posts on SO and this code does work fine):
static copyTextToClipboard(text: string): boolean
{
// Find the dummy text area or create it if it doesn't exist
const dummyTextAreaID = "utilities-copyTextToClipboard-hidden-TextArea-ID";
let dummyTextArea: HTMLTextAreaElement = document.getElementById(dummyTextAreaID) as HTMLTextAreaElement;
if (!dummyTextArea)
{
console.log("Creating dummy textarea for clipboard copy.");
let textArea = document.createElement("textarea");
textArea.id = dummyTextAreaID;
// Place in top-left corner of screen regardless of scroll position.
textArea.style.position = "fixed";
textArea.style.top = "0";
textArea.style.left = "0";
// Ensure it has a small width and height. Setting to 1px / 1em
// doesn't work as this gives a negative w/h on some browsers.
textArea.style.width = "1px";
textArea.style.height = "1px";
// We don't need padding, reducing the size if it does flash render.
textArea.style.padding = "0";
// Clean up any borders.
textArea.style.border = "none";
textArea.style.outline = "none";
textArea.style.boxShadow = "none";
// Avoid flash of white box if rendered for any reason.
textArea.style.background = "transparent";
document.querySelector("body").appendChild(textArea);
dummyTextArea = document.getElementById(dummyTextAreaID) as HTMLTextAreaElement;
console.log("The dummy textarea for clipboard copy now exists.");
}
else
{
console.log("The dummy textarea for clipboard copy already existed.")
}
// Set the text in the text area to what we want to copy and select it
dummyTextArea.value = text;
dummyTextArea.select();
// Now execute the copy command
try
{
let status = document.execCommand("copy");
if (!status)
{
console.error("Copying text to clipboard failed.");
return false;
}
else
{
console.log("Text copied to clipboard.");
return true;
}
}
catch (error)
{
console.log("Unable to copy text to clipboard in this browser.");
return false;
}
}

AngularJS animate dynamic margin

I have an element that appears when the user clicks a button elsewhere on the screen. The element appears to come out of the top of the screen. The element by default needs to be tucked out of view above the screen, so I will have a margin-top style that is based on the height of the element (and will be a negative value). This cannot be hardcoded in css because the element height may vary. When I click the button, I want the element margin-top to change to 0 and I want a transition animation.
The sample shown on angularJS documentation is for adding a removing a class. This would work fine if I knew the values to be set and could code them in CSS, however I cannot. What is the correct way to solve this?
The code below works for displaying and hiding my element using a margin but there is no animation. How do I trigger an animation here when the margin changes?
https://docs.angularjs.org/guide/animations
Quote Total: {{salesPriceTotal + taxesTotal - tradeInsTotal | currency}}
<div class="totals" ng-style="setTopMargin()">
// totals stuff here.
</div>
$scope.setTopMargin = function() {
return {
marginTop: $scope.marginTop
}
};
$scope.$watch('showTotals', function() {
var margin = $scope.showTotals ? 10 : -160 + $scope.modelTotals.length * -200;
$scope.marginTop = margin.toString() + 'px';
});
I added the following code per a suggested solution, but this code is never hit.
myApp.animation('.totals', function () {
return {
move: function (element, done) {
element.css('opacity', 0);
jQuery(element).animate({
opacity: 1
}, done);
// optional onDone or onCancel callback
// function to handle any post-animation
// cleanup operations
return function (isCancelled) {
if (isCancelled) {
jQuery(element).stop();
}
}
},
}
});
As the documentation explains: "The same approach to animation can be used using JavaScript code (jQuery is used within to perform animations)".
So you basically needs to use animate() from jQuery to do what you want.

Twitter Bootstrap Popover/Tooltip Bug with Mobile?

I am working with Twitter Bootstrap and ran into something I could not fix when testing on iPad and iPhone. On mobile (at least those devices) you need to click to engage the tip or popover (as expected). The issue is that you can never close it once you do. I added a listener to close it if you click it again, but I find it hard to believe that the default behavior would not be to click to remove it. Is this a bug in Bootstrap popover and tooltip?? My code is below - it seems to work, but ONLY if you click the same item that created the tip or popover - not anywhere on the page (could not get that to work).
Code to fire:
$(function () {
//Remove the title bar (adjust the template)
$(".Example").popover({
offset: 10,
animate: false,
html: true,
placement: 'top',
template: '<div class="popover"><div class="arrow"></div><div class="popover-inner"><div class="popover-content"><p></p></div></div></div>'
//<h3 class="popover-title"></h3>
//Need to have this click check since the tooltip will not close on mobile
}).click(function(e) {
jQuery(document).one("click", function() {
$('.Example').popover('hide')
});
});
});
HTML:
<a href="javascript:void(0);" class="Example" rel="popover" data-content="This is the Data Content" data-original-title="This is the title (hidden in this example)">
Thanks in advance!
Dennis
I tried dozens of solutions posted to stackoverflow and other various corners of the web, and the following is the only one that worked for me!
Explanation
As noted here, you can a CSS-directive the element in order to make it touch-device-clickable. I can't tell you why that works or what's going on there, but that seems to be the case. So, I want to make the entire document aka body clickable on mobile devices, which will allow me to touch anywhere to dismiss the popover.
Popover JS
$(function () {
$('[data-toggle="popover"]').popover({ trigger: "hover"}})
});
Directions
1. Install Modernizr
I'm using rails, so I used the gem.
gem 'modernizr-rails'
2. Create a touch class with a css-directive
Add the following to your CSS:
.touch {
cursor: pointer
}
3. On touch devices only, add the touch class to the body
If you want other elements to be clickable, instead of the entire body, add the touch class to them.
if (Modernizr.touch) {
$( "body" ).addClass( "touch" );
}
That's it! Now, you can use your popover normally on desktop (even with hover-trigger) and it will be touch-dismissible on mobile.
I had the same problem with my IPad. But in browser it works fine. Solution for me was adding listeners for all possible element that i can hide tooltip:
$('*').bind('touchend', function(e){
if ($(e.target).attr('rel') !== 'tooltip' && ($('div.tooltip.in').length > 0)){
$('[rel=tooltip]').mouseleave();
e.stopPropagation();
} else {
$(e.target).mouseenter();
}
});
Yes, it's small overhead to send event for all tooltips, but you can't define which element tooltip is showing.
Main concept is that make popover manually on mobile device
$(document).ready(function() {
if ('ontouchstart' in window) {
$('[data-toggle="popover"]').popover({
'trigger': 'manual'
});
}
});
Refer following code snippet to get it works:
$('[data-toggle="popover"]').popover();
$('body').on('click', function (e) {
$('[data-toggle="popover"]').each(function () {
//the 'is' for buttons that trigger popups
//the 'has' for icons within a button that triggers a popup
if (!$(this).is(e.target) && $(this).has(e.target).length === 0 && $('.popover').has(e.target).length === 0) {
$(this).popover('hide');
}
});
});
This is the easiest way of detecting clicks on the body and close all the tooltips on the page.
You can check the live example here
Solution on this jsfiddle,
test on iOS (iPad and iPhone), Android and Windows.
$(document).ready(function(){
var toolOptions;
var toolOptions2;
var isOS = /iPad|iPhone|iPod/.test(navigator.platform);
var isAndroid = /(android)/i.test(navigator.userAgent);
///////////////////////////////////////// if OS
if (isOS){
toolOptions = {
animation: false,
placement:"bottom",
container:"body"
};
$('.customtooltip').tooltip(toolOptions);
$('.customtooltip').css( 'cursor', 'pointer' );
$('body').on("touchstart", function(e){
$(".customtooltip").each(function () {
// hide any open tooltips when the anywhere else in the body is clicked
if (!$(this).is(e.target) && $(this).has(e.target).length === 0 && $('.tooltip').has(e.target).length === 0) {
$(this).tooltip('hide');
}////end if
});
});
///////////////////////////////////////// if Android
} else if(isAndroid){
toolOptions = {
animation: false,
placement:"bottom",
container:"body"
};
toolOptions2 = {
animation: false,
placement:"left",
container:"body"
};
$('.c_tool1').tooltip(toolOptions);
$('.c_tool2').tooltip(toolOptions);
$('.c_tool3').tooltip(toolOptions2);
///////////////////////////////////////// if another system
} else {
toolOptions = {
animation: true,
placement:"bottom",
container:"body"
};
$('.customtooltip').tooltip(toolOptions);
}//end if system
document.getElementById("demo").innerHTML = "Sys: "+navigator.platform+" - isOS: "+isOS+" - isAndroid: "+isAndroid;
});
<h6>
first tooltip
Second tooltip
third tooltip
</h6>
<p id="demo"></p>
Bootstap-tooltip v3.3.7
Actual: tooltip on hover doesn't work with touch devices in our project
Solution: Subscribe to tooltip's show event and call mouseenter
$body = $('body');
$body.tooltip({selector: '.js-tooltip'});
// fix for touch device.
if (Modernizr.touch) { // to detect you can use https://modernizr.com
var hideTooltip = function(e) {
tooltipClicked = !!$(e.target).closest('.tooltip').length;
if (tooltipClicked) { return; }
$('.js-tooltip').tooltip('hide');
}
var emulateClickOnTooltip = function(e) {
tooltipsVisible = !!$('.tooltip.in').length;
if (tooltipsVisible) { return; }
$(e.target).mouseenter();
}
var onTooltipShow = function(e) {
tooltipClicked = !!$(e.target).closest('.tooltip').length;
if (tooltipClicked) { return; }
$body.on('touchend', hideTooltip);
}
var onTooltipHide = function() {
$body.off('touchend', hideTooltip);
}
$body
.on('touchend', '.js-tooltip', emulateClickOnTooltip)
.on('show.bs.tooltip', onTooltipShow)
.on('hide.bs.tooltip', onTooltipHide);
}

Resources