Get AngularJS to play nice with other javascript - javascript

I have started using AngularJS with some code that was already written and need to get it to play nice together.
So I have
<html ng-app="MainPage">
<head>
...Some JS includes
</head>
<body>
<div id="divDropDownMenu" class="DropDownMenu">
....AngularJS stuff in here
</div>
</body>
I'm using this code to append a button at the end of "divDropDownMenu" and when the button is clicked slide the up and down to reveal the menu items.
var divPanel = $("<div class='slide-panel'>");
var divContent = $("<div class='content'>");
(function ($, $scope) {
$.fn.slideBox = function(params){
var content = $(this).html();
var defaults = {
width: "100%",
height: "500px",
position: "top" // Possible values : "top", "bottom"
}
// extending the function
if(params) $.extend(defaults, params);
$(divContent).html(content);
$(divPanel).addClass(defaults.position);
$(divPanel).css("width", defaults.width);
// centering the slide panel
$(divPanel).css("left", (100 - parseInt(defaults.width))/2 + "%");
// if position is top we're adding
if(defaults.position == "top")
$(divPanel).append($(divContent));
// adding buttons
$(divPanel).append("<div class='slide-button'>Open Menu</div>");
$(divPanel).append("<div style='display: none' id='close-button' class='slide-button'>Close Menu</div>");
if(defaults.position == "bottom")
$(divPanel).append($(divContent));
//$(this).replaceWith($(divPanel));
// Buttons action
$(".slide-button").click(function(){
if($(this).attr("id") == "close-button")
$(divContent).animate({height: "0px"}, 1000);
else
$(divContent).animate({height: defaults.height}, 1000);
$(".slide-button").toggle();
});
};
})(jQuery);
function SlidePanelExpandCollapse(ExpandCollapse)
{
if (ExpandCollapse == "Expand") {
$(divContent).animate({ height: defaults.height }, 1000);
}
else {
$(divContent).animate({ height: "0px" }, 1000);
}
$(".slide-button").toggle();
}
The problem is that any angular inside the targeted div does not fire when using the above code.
this is the CSS that goes with the above JS menu slider
/* #override
http://samuelgarneau.com/slidebox.css
http://samuelgarneau.com/lab/validator/slidebox.css
http://samuelgarneau.com/lab/slidebox.css
http://samuelgarneau.com/lab/slidebox/style/slidebox.css
*/
body {
margin: 0;
padding: 75px 0 0;
}
.slide-panel {
z-index: 9999;
width: 5px;
position:absolute;
}
.bottom {
bottom: 0;
}
.right {
}
.left {
position: absolute;
left: 0;
}
.top {
top: 0;
}
.content {
margin-left: auto;
margin-right: auto;
z-index: 10;
overflow: hidden;
text-align: left;
background-color: #343434;
height: 0;
width: 100%;
color: #fff;
}
.slide-button {
background: none repeat scroll 0 0 gray;
margin-left: auto;
margin-right: auto;
position:relative;
width: 150px;
z-index: 20;
cursor: pointer;
height: 30px;
padding-top: 10px;
text-align: center;
}
.slide-button:hover {
color: #ffffff;
}

I don't see any actual use of AngularJS in your example at all. The only thing which reminded me of AngularJS was the mention of $scope in your function definition. Now while AngularJS appears to work magically in some areas this still won't do anything.
Also it might appear tempting to keep existing code and just add some AngularJS parts. However seeing your example I would recommend that you rewrite this functionality using the means of AngularJS. Showing and Hiding stuff can be easily done with ng:hide or ng:show. The animation stuff you do can be done with ng:animate. And if you need to dynamically show data, put it into a model (in your scope) and use that with functions like ng:repeat.
It appears that what you are doing in your example would boil down to only a couple of lines using the AngularJS functionality, so the result would be better and easier to read and maintain. Do yourself a favor and get familiar with what AngularJS can provide you with and stop bothering with direct DOM manipulation. It is a bad pattern. DOM manipulation is pesky, complicated and prone to break. It is also usually an unmaintable mess. And the best: If you are using AngularJS anyway, it is complete superflous because everything can be achieved much easier by having a model triggering conditional logic in your HTML.
Some impressive fact from the talk of the AngularJS creator at Google I/O 2013: He used AngularJS in its beginning to rewrite a internal project of Google. The result was that 14000 lines of code were reduced to 1500.
I recognize that this does not really count as an answer to your problem but I really believe that you would be better off stopping this approach right here and now and instead of wasting time to get this working rewrite it in AngularJS. This seriously should not take longer than half an hour even if you have to read up all the details still.
Edit/Addendum: The talk I mentioned is "Google I/O 2013 - Design Decisions in AngularJS" at http://www.youtube.com/watch?v=HCR7i5F5L8c I highly recommend watching this video even to people who have already experience with AngularJS. It gives good insight to the AngularJS way and its points come handy when trying to convince someone else in trying AngularJS :)
Edit/Addendum2: As Sprottenwels helpfully mentioned above, there is another question here on stackoverflow which gives a much more thorough explanation of what I boiled down above, so please give it a read: "Thinking in AngularJS" if I have a jQuery background?
Edit/Addendum3: As again Sprottenwels helpfully mentioned: The videos at http://www.egghead.io/lessons are a great resource. I personally found them sometime a little hard to understand (you might need to stop the video and read up in the documentation of AngularJS which thankfully is nowadays much better than it used to be).
Oh and on a personal note: While I did web development since about 1997, I was never a friend of doing application like stuff in JavaScript because all those frameworks are so complicated with lots of boilerplate and you are doing stuff which feels like a waste of time. AngularJS really did wonders to my motivation doing such stuff because it finally is totally logical and its magically like inner workings totally freed me from doing stuff I hated like synchronisation of data and view.
I am totally thankful to the AngularJS people and I absolutely believe that this is the only real future of doing web application programming. Right now AngularJS can be a problem performance-wise because of doing "bad" stuff like dirty-checking but this is going to change with new browser features like Object.observe. So I really think that choosing to use AngularJS to its full extent is a wise and future-safe move.

Related

How do I animate things on scroll?

This theme demo on themeforest has neat animations including for example showing text when the users scrolls down, or making icons appear from the sides.
I'm guessing CSS3 and JavaScript, possibly with jQuery or a framework specialized in animations would be used for this, but I have no idea where to start learning how to accomplish these kinds of animations.
Where do I start?
Checkout WowJS.
And try going through their source code if you're curious about it works under the hood.
Otherwise its a pretty good library to get started with scroll animations.
Basically animations that are triggered by scrolling actions are Javascript / jQuery stuff (not CSS). There's a nice JS plugin which you can take a look at which helps to do all that: Scrollmagic, to be found here
If you are using Wordpress, chances are you can buy a theme that includes very nice animations ready for use with a few options (say how fast it should occur or such) - similarly there are many neat animations on codepen and similar sites so maybe if you search long enough you could find and copy what you want.
However, if you really want to learn how to do that yourself, there's no short cuts and you have to start with understanding JavaScript and the capabilities of CSS3 animations and deeply, so you know which can do what.
CSS Transitions, CSS Animations, Simple JS animations
These are all w3school tutorials but there are many other resources out there. Start with something simple and work your way up.
Since you mentioned the behavior on scroll, the relevant JavaScript is
object.addEventListener("scroll", myScript);
Here's a simple use case to start.
Well there are lots of ways to do it, but jQuery works nicely.
You may find this and this helpful.
$(window).scroll(function () {
if($('body').scrollTop() > 110) {
$('.box').css({
'width': '50px',
'height': '50px',
'background': 'coral'
});
} else {
$('.box').css({
'width': '100px',
'height': '100px',
'background': 'red'
});
}
});
body {
padding-top: 175px;
background: whitesmoke;
height: 600px;
}
.box {
margin-top: 150px;
background: red;
width: 100px;
height: 100px;
margin: 0 auto;
transition: all .6s ease;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="box">
</div>

Radio button remove down correctly

I've two radio buttons with Drop down and I need to put the drop down
in parallel to the second radio button,when we add to the css code
the following its working but this is not a good solution since if I've
bigger page with other control this can override them either
#__box0 {
margin-top: 20px;
margin-left: 20px;
}
there is another option to do that with CSS?
http://jsbin.com/ziziqeyopu/edit?css,js,output
The Html is renders in the renderer method
This is SAPUI5
http://openui5.org/
code but for the question its not relevant since
renderer is related to pure html/css...
i've tried with the following which doesnt works.
.mylist-content>div:first-child {
margin-right:30px
margin-top:50px
}
.mylist-radiolist>DIV:last-child {
margin-left: 30px;
margin-top:100px;
}
If you still haven't figured it out, give this a try:
.mylist-content #__box0 {
position: relative;
top: 20px;
left: 20px;
}
What you see above should do the same thing as your first attempt, but not interfere with anything else on your page, by:
Adding extra application restrictions to the CSS rule, by having the .mylist-content scope restriction (even though this should not be necessary, in theory, because #__box0 is an ID and should be unique on the page).
Shifting the position of the dropdown without affecting any other elements - this is done with position: relative and the corresponding top and left offsets.
Without knowledge of SAP UI and/or your particular situation, I doubt someone will be able to give you a more appropriate answer.

Sandbox WebGL Plugin

I am doing some work with Pipeline Pilot and noticed that all of the built in HTML components that do things, like collapsible panels, tabs, or anything else that presumably has some javascript that I can't access causes my otherwise working WebGL component to break on load.
Is there a way to "sandbox" or otherwise isolate a WebGL component for it's own protection? Weird question, and not the best way to look at it, but I can't change any of the code inside of the WebGL component, and I can't change any of the internal Pipeline Pilot code, so I need an inelegant solution of any kind.
What #David said, using an iframe would probably do what you want. In that vain you can detect if you're in an iframe with
var isInIFrame = function() {
return window != window.top;
};
I use that the change my CSS depending on if I'm in an iframe or not
var updateCSSIfInIFrame = function() {
if (isInIFrame()) {
document.body.className = "iframe";
}
};
Then I can use CSS to change formatting. eg:
/* only applies if in an iframe assuming the function above was called. */
body.iframe {
width: 100%;
height: 100%;
margin: 0px;
padding: 0px;
overflow: hidden;
}
.iframe>canvas {
width: 100%;
height: 100%;
}
To be honest, I have trouble comprehending your exact question, however if you want to sandbox html the go to place to go to is the <iframe> in combination with an external domain. By hosting the relevant things on a different domain the cross domain policy kicks in fully sandboxing the two environments. Another option is using the sandbox attribute of the <iframe> tag in HTML5, but as it's not fully supported yet I would not actually advise that.

Turning scripts off and on based on Media Queries?

I am using a widget in my layout and I have it now so when a certain breakpoint is hit it will not display that larger widget and then goes to the smaller one. The larger widget does hide and the smaller one shows up but the text that is associated with both isn't right.
The text for the large widget displays and the smaller text for the small widget doesn't. I am pretty sure it has to do with the scripts each are using. The display none does hide the elements but the scripts seem to be still running.
I have absolutely no clue about JavaScript yet and would prefer a HTML or CSS answer if possible. If not then I will go with JS but will need some direction please. I have read numerous articles and even in the process of learning JS but still not sure how some of what I've read applies.
#media all and (max-width: 900px) {
// styles
}
if (document.documentElement.clientWidth < 900) {
// scripts
}
This is what I've found that seems like it is what I need but I'm not sure on the syntax of how to call the script I need. Do I just put the script itself in there without any other information? I have also read about using jquery to do this with something like this
$(window).resize(function() {
if ($(this).width() > 480) {
// call supersize method
}
});
And I've even read about using Modernizer to do this but I still have to go through the documentation.
In the bin it doesn't show any of the text at all but the larger text is there and off to the side of the small widget. I just need to shut that large script off and turn the other on at a certain media query.
Any help is greatly appreciated.
HTML
<aside class="smallScreen">
<div class="smallWeather" style='width: 200px; height: 440px; background-image:
url(http://vortex.accuweather.com/adcbin/netweather_v2/backgrounds/red_500x440_bg.jpg
); background-repeat: no-repeat; background-color: #993333;' ><div
id='NetweatherContainer' style='height: 420px;' ><script src='http://...'></script>
</div></div></aside>
</div></div></div></aside>
<aside class="largeScreen">
<div class="largeWeather" style='width: 500px; height: 440px; background-image:
url(http://vortex.accuweather.com/adcbin/netweather_v2/backgrounds/red_500x440_bg.jpg
); background-repeat: no-repeat; background-color: #993333;' ><div
id='NetweatherContainer' style='height: 420px;' ><script src='http://...'></script>
</div></div></aside>
CSS
#media screen and (min-width: 564px) and (max-width: 604px) {
.largeScreen {
display: none;
}
.smallScreen {
display: block;
width: 55%;
min-width: 240px;
height: 100%;
font-size: 0;
margin-left: auto;
margin-right: auto;
margin-bottom: 1.2rem;
}
.smallWeather {
position: relative;
display: block;
width: 240px;
height: 420px;
background: white;
margin-left: auto;
margin-right: auto;
}
What is the best way to do this and why please? Is jQuery the best way from a mobile performance standpoint?
UPDATE: Going to use enquire.js because of it's straightforward approach (although I'm still a bit sketchy on it's use) and how small it is.
This is the basic code:
enquire.register("screen and (max-width: 605px)", {
// OPTIONAL
// If supplied, triggered when a media query matches.
match : function() {},
// OPTIONAL
// If supplied, triggered when the media query transitions
// *from a matched state to an unmatched state*.
unmatch : function() {},
// OPTIONAL
// If supplied, triggered once, when the handler is registered.
setup : function() {},
// OPTIONAL, defaults to false
// If set to true, defers execution of the setup function
// until the first time the media query is matched
deferSetup : true,
// OPTIONAL
// If supplied, triggered when handler is unregistered.
// Place cleanup code here
destroy : function() {}
});
Still not done and looking for more support with this. Now that I've chose this route, I see that there is quite a few articles and questions already about enquire.js. I will update my situation as I read up.
UPDATE: This is where I'm at but it's not working yet. I have the styles associated with each script still in the HTML and display none used accordingly. Will doing this work once I get the enquire.js correct?
Here is the new jsbin
Thanks again for everything!!
I think you are looking for something like enquire.js, which is a lightweight JavaScript library for responding to CSS media queries.
If you don't want to use a library, this post on reacting to media queries in JavaScript runs through a way of doing what you are after with vanilla JavaScript.
Here's a jsFiddle Demo with some working example code, and here's a Fullscreen jsFiddle Demo, which is handy when trying to test how it works. If you use the fullscreen version and make the browser window less than 600px wide, a javascript alert will tell you that you have done so. Because the alert comes up, the browser will jump back to its original size and tell you that it got bigger than 600px again. So you can see, it calls the match function when it matches (so at the time of loading and at the time of resizing to a larger width), and it calls the unmatch function when resizing to a smaller width, because then the media query doesn't match any more. That's how you call a certain function only for mobile or only for desktop based on their screen size.
JS
enquire.register("screen and (min-width:600px)", {
match: function () {
alert("Now the screen width is more than 600px");
},
unmatch: function () {
alert("Now the screen width is less than 600px");
}
});

Using addClass() and CSS transition on render function in Backbone app not working correctly

In my backbone.js application, I'm trying to fade in the view element after it's been appended. However it doesn't work.
Live example here: http://metropolis.pagodabox.com
var itemRender = view.render().el;
$('#items-list').append(itemRender);
$(itemRender).addClass('show');
However if I add a small setTimeout function, it works.
var itemRender = view.render().el;
$('#items-list').append(itemRender);
setTimeout(function(){
$(itemRender).addClass('show');
},10);
Using fadeIn() also works but I prefer to use straight CSS for the transition as it's more efficient, and prefer not to use any setTimeout "hacks" to force it to work. Is there a callback I can use for append? Or any suggestions? The full code is below:
itemRender: function (item) {
var view = new app.ItemView({ model: item }),
itemName = item.get('name'),
itemRender = view.render().el;
$('#items-list').append(itemRender);
$(itemRender).addClass('show');
app.itemExists(itemName);
}
CSS/LESS:
#items-list li {
padding: 0 10px;
margin: 0 10px 10px;
border: 1px solid #black;
.border-radius(10px);
position: relative;
.opacity(0);
.transition(opacity)
}
#items-list li.show {.opacity(1)}
This "hack" you mention (or some variant of it) is occasionally necessary for web development, simply due to the nature of how browsers render pages.
(NOTE: This is all from memory, so while the overall idea is right please take any details with a small grain of salt.)
Let's say you do the following:
$('#someElement').css('backgroundColor', 'red');
$('#someElement').css('backgroundColor', 'blue');
You might expect to see the background color of #someElement flash red for a brief moment, then turn blue right? However, that won't happen, because browsers try to optimize rendering performance by only rendering the final state at the end of the JS execution. As a result, the red background will never even appear on the page; all you'll ever see is the blue.
Similarly here, the difference between:
append
set class
and:
append
wait 1ms for the JS execution to finish
set class
Is that the latter allows the element to enter the page and AFTER the JS is executed have its style change, while the former just applies the style change before the element gets shown.
So while in general window.setTimeout should be avoided, when you need to deal with these ... complications of browser rendeering, it's really the only way to go. Personally I like using the Underscore library's defer function:
var itemRender = view.render().el;
$('#items-list').append(itemRender);
_(function(){
$(itemRender).addClass('show');
}).defer();
It's the same darn thing, but because it's encapsulated in a library function it feels less dirty to me :-) (and if the "post-render" logic is more than a line or two I can factor it in to a Backbone View method and do _(this.postRender).defer() inside my render method).
You can use CSS animations
#keyframes show {
0% { opacity: 0; }
100% { opacity: 1; }
}
#items-list li {
padding: 0 10px;
margin: 0 10px 10px;
border: 1px solid #black;
.border-radius(10px);
position: relative;
}
#items-list li.show {
animation: show 1s;
}

Categories