AngularJS: How do i scroll to an object with an offset? - javascript

I am working on an application built in AngularJS. One requirement that has been passed to me is that when a form is invalid and the user clicks submit, the window should scroll the first invalid element into view.
This is pretty easily accomplished using element.scrollIntoView() but I need to set an offset. You see, the top of the page has a header that 'fades into' the rest of the page. See the image below.
So i'm left to try to figure out some method of offsetting. I have found a bunch of examples but i'm not finding exactly what i'm looking for.
Here is my current code (
var visibleInvalids = angular.element.find('.ng-invalid:visible');
if (angular.isDefined(visibleInvalids)){
// if we find one, set focus and anchor
visibleInvalids[0].scrollIntoView(true);
visibleInvalids[0].focus();
}

Proposed answer to my own question. Inject $anchorScroll and use that, but i'm open to ideas...
var visibleInvalids = angular.element.find('.ng-invalid:visible');
if (angular.isDefined(visibleInvalids)){
// if we find one, set focus and anchor
// Offset is used to keep items 'below' that fade-in header.
$anchorScroll.yOffset = 200;
if (visibleInvalids[0].id) {
$anchorScroll($location.hash(visibleInvalids[0].id));
}
visibleInvalids[0].focus();
}

Related

How do I use Javascript to delay visibility of a text box and then hide another box after the first becomes visible

Im very new to this and have reviewed other posts similar to this question. However, I'm finding that those solutions don't work for me.
Background: I'm working in Wix's Velo platform for Javascript. (forgive me if that's not the right technical terminology here)
My goal: When my website home page loads, I want one of the text boxes on the page (#text45) to NOT be visible until 5 seconds have passed. Then, when box #text45 is visible, I want another plain box (#box2) to turn to hidden.
I have found some examples like the one below: (not all code has been pasted and I realize some elements like div1 would need to change to my specific element names)
document.getElementById("div1").style.visibility = "visible";
}
setTimeout("showIt()", 5000);
However, I get an error code: Cannot find name 'document'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'dom'.
When researching this, I found out that Velo cannot access the dom and elements can only be accessed via "$w".
Would someone be kind enough to set me in the right direction on how to accomplish the "goal" above? I would really appreciate it! Thank you in advance.
Here's how you would do it. Note, that it's good practice to change the IDs of your elements to more descriptive names, but I've stuck with the names you provided in your question.
Start by setting #text45 to hidden in using the Properties & Events panel.
Then use this code (note that your page might already have an onReady. If it's there an you're not using it yet, delete all the code on the page and replace it with this):
$w.onReady( () => {
setTimeout(() => {
$w('#text45').show();
$w('#box2').hide();
}, 5000)
} );

How can I position an Angular Material panel dialog relative to a button?

According to a discussion at Github one cannot position a standard dialog (api), but panel dialogs (api) can be positioned.
A simplified demo shows that this is true:
var position = this._mdPanel.newPanelPosition().bottom(0).right(0);
The Angular Material docs show a method that allows positioning relative to the clicked element (or whatever is passed in). I'm unable to get this to work, however.
var target = el.target;
var position = this._mdPanel.newPanelPosition().relativeTo(target);
Passing in hard values for .top() and .right(), for example, allows positioning relative to the viewport. I can't get positioning relative to the clicked element, though. How is this supposed to work?
I've been working with Angular Material for the past several months and still find the documentation lacking, so forgive the length of this post as my pseudo documentation on the issue. But here is what I do know:
I've only been able to get the panel location to work, relative to a target element, by chaining the addPanelPosition function onto the relativeTo function as such:
var position = this._mdPanel
.newPanelPosition()
.relativeTo(ev.target)
.addPanelPosition('align-start', 'below') // or other values
(in this case, ev is the $event object passed by ng-click)
I was able to track down the acceptable parameters for addPanelPosition and they are the following:
Panel y position only accepts the following values:
center | align-tops | align-bottoms | above | below
Panel x Position only accepts the following values:
center | align-start | align-end | offset-start | offset-end
Interstingly enough, in the Angular Material demo, they use the this._mdPanel.xPosition.ALIGN_START and this._mdPanel.yPosition.BELOW properties which simply resolve to strings as their x and y values for the addPanelPosition function. I've always gone straight with the string values. However, using string values could be problematic if the development of this feature is still in flux and they change the acceptable string values.
I'll point out one more issue I've seen.
Another trick they use in the demo is to specify a class name in the relativeTo function instead of a target element, then place that class on the target element itself. The reason this approach can be helpful is because the $event object from ng-click can provide different target elements based on what exactly was clicked. For example, clicking the button <div> is going to give a different target than clicking the <span> text inside the button. This wil cause your panel to shift locations unless you provide the additional functionality not to do so.
Codepen
I took their demo and really cut it down to size to focus on this issue. You can see the updated codepen here
As I post in a comment, here you can see it working on a plunker.
My solution is very close the to #I think I can code answer. However, in my answer, instead of a menu, a <md-dialog> is displayed when the button is clicked, as it's requested in the OP.
Besides the working plunker with a dialog, there is no much to add to the good #I think I can code answer. As it's shown in the angular-material md-panel demo, the key here is to set the position of the panel relative to the button. To do that (like in the angular-material demo), we can use a specific css class (demo-dialog-open-button in my example) to find the target element. this is a tricky thing in my opinion...but it works well for this use case (it's also well explained in the other answer).
Code for reference, see the plunker for the complete details:
html (note the css class added to the button):
<md-button class="md-primary md-raised demo-dialog-open-button" ng-click="ctrl.showDialog($event)">
Dialog
</md-button>
JS controller.
var position = this._mdPanel.newPanelPosition()
.relativeTo('.demo-dialog-open-button')
.addPanelPosition(this._mdPanel.xPosition.ALIGN_START, this._mdPanel.yPosition.BELOW);
Hope it helps
Dialogs are very simple widgets. Trapping focus is about the most complicated thing they do. It pains me that your issue has evolved into such a complex one.
Just to state the obvious, you do have complete control over positioning any individual dialog thanks to your configured class name.
.demo-dialog-example {
top: 20px;
left: 20px;
}
Also, in your showDialog method, why not set up a call-back via a promise for the open method? Something like:
this._mdPanel.open(config).then(function() {
var dialog = angular.element(document.querySelector('.demo-dialog-example'));
//Centering, positioning relative to target, or draggable logic goes here
});
I respect that you are trying to improve the logic of the plugin and do things the "Angular way", but these relatively simple requirements should not be causing you this much heartache.

Load page partially via javascript?

I guess there are already posts on this topic, but I couldn't find anything with search terms like: "load page partially javascript", "content loading javascript", etc.
My question is how would you load a page to not display 100% of the content, but load it, for example, when you scroll.
Example: https://chrome.google.com/webstore/category/extensions?hl=de
I don't need someone to code this now, I would highly appreciate if someone just had a tutorial or anything similar on this.
My approach for doing this was to use a combination of javascript and JQuery. Something like this:
window.onScroll = checkScroll(event);
function checkScroll(event)
{
position = $(window).scrollTop();
/* note the () are necessary to obtain a value that represents
the page vertical position, otherwise you will be just sending
the browser to the top, like a refresh */
if (position == 1000) /* some pre-determined vertical position */
{
$(#AnchorDiv).append(htmlString);
/* Where AnchorDiv is the id of the element where you want to
insert new code, and htmlString is a javascript string which contains
the new HTML block.*/
}
}
Note: you can also use Jquery's .position method to obtain the position of an element rather than hard-coding it like I did with the 1000 example.
I Hope this helps. I'll be curious to see how it works out - and/or how other folks respond to this question - and/or of you find another good working alternative.

The offset position is wrong when setting a new element with the same offset of the clicked element

So what i'm attempting to do is a tooltip/popover. Now i'm trying to ensure that whether you use a button format or just an anchor text that the tooltip is positioned based on the position of the button/anchor so that essentially whatever the element used with output the correct position.
FIDDLE GOES HERE
As you will notice, if you click on the button or link, there will be two alerts:
// $el is essentially $(this) for the clicked element
var linkPosition = $el.offset();
alert(linkPosition.top);
Then I alert:
// $popover is the created popover based on the position of the $el (this instance)
var popoverPosition = $popover.offset();
alert(popoverPosition.top);
What you will notice, on the first alert instance the browser returns 8 and on the second instance 28 which confuses me greatly as I was expecting both values to be the same as the offset of the popover was set to be the offset of the button as you can see here:
$popover.css({
top: linkPosition.top,
)};
Can anyone help me understand why the values are therefore different? As JQuery states for offset that
jQuery does not support getting the offset coordinates of hidden
elements or accounting for borders, margins, or padding set on the
body element.
However for what i've done I feel that this should therefore output the correct values.
Any ideas? Thank you.
PLEASE NOTE: It looks like i've used twitter bootstrap, however I haven't I have simply used a similar approach of styles for this demonstration, thank you.
You get different value because that Javascript does not wait untill your element is transit to its new position, So, it take old value and alert them.
try to put some timeout to get offset and then alert them.
I put some timeout in your code see :
http://jsfiddle.net/egycjo7b/4/

Automate page turning with jFlip

I want to have a page turn effect like the one seen on this page: jFlip demo except I want to automate the page turning, like make it happen every second, 20 times (or however many images I have). I want to be able to trigger this animation to run either on page load, when a button is clicked, etc.
Unfortunately I don't understand jQuery all that well and the plugin's events seem rather complicated to me, probably mostly due to my inexperience with jQuery. Any help on which direction I should go, methods I should try? I am not limiting myself to jQuery or even Javascript, this is just the example I have found that achieves my desired effect.
You can find the updated code here. replace this with old one.
Usage :
var jFlip = new Flip("#g1",300,300,{background:"green",cornersTop:true,scale:"fit"});
// here #g1 is a jquery selector (make sure it returns only one).
// The structure is Flip(JQselector,width,height,options)
then use the jFlip object to flip slides/pages
jFlip.flipMe() // for next slide
jFlip.flipMe(true) // for prev slide
for binding a function to slide change event you can use
$("#g1").bind("flip.jflip",function(event,index,total){
$("#l1").html("Image "+(index+1)+" of "+total);
});
// here the selector is same as the one passed inside jFlip function.
Try this and let me know the feedback.

Categories

Resources