Modifying MDL elements on page load - javascript

I am trying to set the value of a mdl slider when a page loads. The recommended way of setting the value for a mdl slider is like this-
document.querySelector('#slider').MaterialSlider.change(value);
This however throws an error TypeError: document.querySelector(...).MaterialSlider is undefined if called from $(document).ready() or $(window).load().
Setting a timeout of a second on it fixes the issue but seems hacky.
Is there any other event I can tie it to to ensure it works?
This is how I am loading the scripsts --
//mobile optimized
meta(name='viewport', content='width=device-width, initial-scale=1.0')
//css
link(rel='stylesheet', href='/stylesheets/style.css')
//material-design-lite
link(rel='stylesheet', href='/bower_components/material-design-lite/material.min.css')
script(src='/bower_components/material-design-lite/material.min.js')
link(rel='stylesheet', href='https://fonts.googleapis.com/icon?family=Material+Icons')
//jQuery
script(src='/bower_components/jquery/dist/jquery.min.js')
//Material icon fonts
//link(rel="stylesheet", href="https://fonts.googleapis.com/icon?family=Material+Icons")
link(href='https://fonts.googleapis.com/css?family=Open+Sans', rel='stylesheet', type='text/css')
//polyfill (support for dialog)
script(src="/bower_components/dialog-polyfill/dialog-polyfill.js")
link(rel="stylesheet", type="text/css", href="/bower_components/dialog-polyfill/dialog-polyfill.css")

It took me a few months but I finally found a solution to this. Just needed to add this --
$( document ).ready(function() {
componentHandler.upgradeAllRegistered();
// --- do stuff on mdl elements here ---
});
This has the added advantage of hiding the ugly version of the mdl components before they are fully loaded.

Based on your error message, I do agree with the question line that #e666 is going down. Some other things to check is where you are loading MDL relative to the other scripts. Script loading can get a bit tricky sometimes.
Besides that, something else you can consider is setting the value of the slider in the DOM directly if you are building your HTML pages out dynamically on the server.
For example, a slider's HTML looks like this:
<p style="width:300px">
<input class="mdl-slider mdl-js-slider" type="range" id="s1" min="0" max="10" value="4" step="2">
</p>
Note the value option. You could set that value dynamically with server side code if that is a possible alternative for you.
If you were working with ASP.NET, you may do something like this.
#model MyModel
<div>
<p style="width:300px">
<input class="mdl-slider mdl-js-slider" type="range" id="s1" min="0" max="10" value="#Model.InitialValue" step="2">
</p>
</div>
But, if you are having issues with your JavaScript loading, I would recommend making sure everything is loading properly before going further as it will likely affect you later on.

In which order are your slider and your javascript code?
If you first call your function and then create the slider, the function won't find it. That might be the reason why it is undefined.
That's also the reason why it works after a waiting time of one second. In this one second the page has been loaded completly and the slider can be found.
If you create the slider first and change it afterwards, you can manipulate it.
You can do this with a code-block after the slider, where the function to change the sliders value is called.

Related

How do I use MDCSlider in JavaScript?

I have created an DOM element for an MDC slider (https://material.io/develop/web/components/sliders).
It looks nice (except for the colors). And it works, but I really have no idea how to initialize it.
I import MDC from the CDN. I can't understand from the documentation how to do the initialization. This is one version that works:
setTimeout(() => { slider = new mdc.slider.MDCSlider(eltSlider) });
Without setTimeout it does not work.
I have tried using a Promise instead and wait a second. That does not work.
And maybe even worse: If I use a Promise to wait after the setTimeout it does not work any more.
What is going on and how am I supposed to do it?
I do not use ts. And I do not use any package handler. Just plain JavaScript. (And I would be glad if the documentation covered this use case first.)
(There seems to be only one other question about MDCSlider here. It does not cover my question: actual use of foundation and adapter class of mdc-components)
EDIT: By "import from CDN" I mean the setup mentioned here: https://material.io/develop/web/docs/getting-started
<link href="https://unpkg.com/material-components-web#latest/dist/material-components-web.min.css" rel="stylesheet">
<script src="https://unpkg.com/material-components-web#latest/dist/material-components-web.min.js"></script>
There is no JavaScript error. It is just the slider on the screen that does not work. (It looks ok, but it does not work.)
I think this is a problem with MDC and the DOM state. The example in the link above suggests that the DOM is ready, but it does not say so. And it does not explain how to check this when manipulating the DOM with JavaScript.
it seems the latest version on unpkg was recently changed from 8.0.0 to 9.0.0 fixing this issue
<script src="https://unpkg.com/material-components-web#9.0.0/dist/material-components-web.js"></script>
<link href="https://unpkg.com/material-components-web#9.0.0/dist/material-components-web.css" rel="stylesheet">
<div class="mdc-slider">
<input class="mdc-slider__input" type="range" min="0" max="100" value="50" name="volume" aria-label="Continuous slider demo">
<div class="mdc-slider__track">
<div class="mdc-slider__track--inactive"></div>
<div class="mdc-slider__track--active">
<div class="mdc-slider__track--active_fill"></div>
</div>
</div>
<div class="mdc-slider__thumb">
<div class="mdc-slider__thumb-knob"></div>
</div>
</div>
<script>
sldr = new mdc.slider.MDCSlider(document.querySelector('.mdc-slider'));
sldr.root.addEventListener('MDCSlider:change', (e)=>console.log(e));
</script>
now works as expected https://stackblitz.com/edit/js-4ycwx5?file=index.html
Here is some documentation about using MDC Web in plain JavaScript - https://material.io/develop/web/docs/importing-js, section "Global / CDN".
Make sure you call JavaScript after slider HTML is loaded. Usually I insert it just before the closing body tag.
Here is an example of slider initialization code for JavaScript:
const MDCSlider = mdc.slider.MDCSlider;
const slider = new MDCSlider(document.querySelector('.mdc-slider'));
Here is a minimum working example of the MDCSlider - https://jsfiddle.net/klyakh/oky0zf7e/1/

Proper way to handle ajax HTML block

So I was wondering what is the "spec" or "proper" way to handle HTML that is used via ajax.
For example, should I keep all the HTML in the actual page that is using it? Or should I just an ajax call to load it in?
Is there performance increase in keeping it loaded in the page since its one less load? Or does loading that extra data at page load off-set it.
Here is a screenshot illustrating what I mean.. You can see the {name} which is changed depending on what the user provide (limited characters of course).
Any help/opinion is appreciated! Thanks!
Partial source for those asking:
<!-- text field -->
<div class="add-field-wrapper float-left">
<input type="radio" value="text" name="input_type" id="rad-type-text" class="type-radio-btn">
<label for="rad-type-text" class="radio-lbl" data-tooltip="Used for simple inputs such as: <b>Phone Number</b> or <b>Email Address</b>">
<img src="https://placeholdit.imgix.net/~text?txtsize=16&txt=70x70&w=70&h=70" class="field-type-icon" />
<div class="field-type-text">Text Field</div>
</label>
</div>
<!-- select -->
<div class="add-field-wrapper float-left">
<input type="radio" value="select" name="input_type" id="rad-type-select" class="type-radio-btn">
<label for="rad-type-select" class="radio-lbl" data-tooltip="Use this option when you need to provide a list of choices for the user." >
<img src="https://placeholdit.imgix.net/~text?txtsize=16&txt=70x70&w=70&h=70" class="field-type-icon" />
<div class="field-type-text">Select Menu</div>
</label>
</div>
<!-- textarea -->
<div class="add-field-wrapper float-left">
<input type="radio" value="textarea" name="input_type" id="rad-type-textarea" class="type-radio-btn">
<label for="rad-type-textarea" class="radio-lbl" data-tooltip="The textarea field will appear as a <b>WYSIWIG</b> (What you see is what you get) editor. This allows for some customization of the appearance of the input.">
<img src="https://placeholdit.imgix.net/~text?txtsize=16&txt=70x70&w=70&h=70" class="field-type-icon" />
<div class="field-type-text">Text Area</div>
</label>
</div>
Edit:
<!-- Datepicker HTML block - used in JS -->
<div id="datepicker_html" style="display: none;">
<div id="{name}-block" class="datepicker-wrapper form-input-wrapper">
<div class="template-drag-handle"><img src="images/design/up-down-icon.png" class="template-drag-handle-icon" alt="Drag" /></div>
<div class="inputs-wrapper">
<div class="form-row"><input type="text" name="{name}" class="input-datepicker" placeholder="{placeholder}" id="{name}"/></div>
</div>
<?php echo $default_template_chkbox_options_html; ?>
</div>
</div>
That's a "piece" of the html.. it gets loaded into a JS variable:
This is what processes it -- adds the name, changes the placeholder (these can be reused as many times as you want)
function addDatePickerField(){
//Get the HTML
var datepicker_html = $('#datepicker_html').html();
datepicker_html = datepicker_html.replaceAll(/{name}/g, input_name_underscores);
datepicker_html = datepicker_html.replaceAll('{placeholder}', input_name);
$('#template-fields-wrapper').append(datepicker_html);
wrapUpAddInput('datepicker');
}
I just didnt now it if would be better to do an ajax call, store the "external" html and call it in when I need it -- Like, that datepicker HTML block, would be store in separate file, then on a link, load into the DOM.
I will try to address your question, even though it's a very broad one.
Generally, loading your content (e.g. HTML) dynamically via an ajax request does not always give you a performance boost, it really depends what you are doing and trying to achieve.
Should you always pre-load all of your HTML content with the initial request ? Or should you ajax load a portion of it after the page is already loaded on screen ? that is solely depends on your application and needs.
I will explain by an example:
Assuming I am developing a content site, which will be mainly content oriented (e.g articles) and will be served from traditional web browsers (desktop or mobile) then loading my articles for each page via ajax might not be a good idea, with very few rare exceptions.
On the other hand-
If I am developing a web application that needs to send and receive blocks of data in "real time", a project that contains a rich UI which has to have a rich and "enterprise"-like experience where stuff is being executed, updated and displayed on-the-fly smoothly without having to refresh and re-load my application page every time I am saving my work or executing an operation - I will certainly use ajax requests for handling some of that work.
Another aspect is the overall loading time of your page:
Some web-sites are loading some of their HTML via ajax after the page body is loaded - by doing this they are reducing the perceived loading-time of the page, by "perceived" I mean - to the user, it appears to load faster since the partial page is loading almost instantly, and then some blocks inside of the page are loading async via ajax.
Like i've said, this is a very broad question and there are many methods to learn and investigate to finally see what works best for your specific needs.
Good luck

My live-search element is not transforming into an input box

I'm using the livesearch plugin for Angular-JS to try and create an AJAX dynamic search box. Following tutorials, I believe that I have everything set up as it should be, but when the page runs, it doesn't transform from a <live-search> element to an <input> element. I can't figure out why.
In my code, I have the scripts ordered like this:
<script src="/Scripts/angular/angular.js"></script>
<script src="/Scripts/Custom/liveSearch.js"></script>
<script src="/Scripts/Custom/application.js"></script>
<script src="/Scripts/Controllers/salesEventEdit.ctrl.js"></script>
Here is a jsFiddle of the relevant code. If you need more, I can update it, but to make it completely functional would be a ton of code to copy over, thanks to angular. The code that I've included in the fiddle is a portion of the salesEventEdit.ctrl.js file.
From everything that I've read, the transformation from <live-search> to <input> should be taken care of via angular by itself and I shouldn't have to do anything.
To help with people in the future in case my fiddle disappears, here is the relevant HTML:
<span class="liveSearchWrap">
<live-search type="text"
class="liveSearch"
name="entitySearch"
live-search-callback="entityCallback"
live-search-item-template="{{result.Name}}"
live-search-select="Name"
live-search-max-result-size="50"
live-search-wait-timeout="500"
live-search-selected-id="Id"
user-id="entity.Id"></live-search>
<a class="btnSearch"><i class="fa fa-search"></i></a>
</span>
While the LiveSearch module does automatically handle <live-search> elements, it needs to be injected into the application. As shown in the GitHub page's README here, inject it like so:
var app = angular.module("MyApp", ["LiveSearch"]);
// -------------------------HERE --^

CSS/Javascript Lightbox

First I'll start with this: I am in no way shape or form a developer, coder etc etc. I'm just a graphic designer helping a friend with her website.
As of right now, I'm having issues linking up thumbnails to the full images on my lightbox call out - you can view the site at www.chrissybulakites.com
I noticed
With VOID:(0) being in every single one ... my thought process was that if I correspond 0 thumb with 0 full then 1 thumb with 1 full then 2 thumb wwith 2 full etc etc it would work .. it didn't.
Can somebody explain to me if I'm on the right path or what I can do to make this work.
Thanks
Rob
Have have two basic elements per image; the thumb and the full image. The thumb is using JavaScript to show and hide a div (kind of like a frame) to hold the full image.
The HTML on the page repeats itself a lot, you can probably solve your problem whilst removing some of the repetition. I'd keep all of your thumbs but on each one, add in a reference to the full image the thumb represents. As well as reducing repetition, it'll make it easier to update the page in the future as changing a thumb and main image is done in one place rather than two.
In the below I've added another part to the "onclick" to say update the src of 'frame' to be the full version of the thumb.
<img src="http://chrissybulakites.com/thumbnails/longshot_thumbnail.png" />
Then delete all of the large images except one, updating it so that the img tag has an ID of 'frame'
<div id="light" class="white_content"><img id='frame' src="http://chrissybulakites.com/images/longshot_full.png" /> <br />Actor Observor - Boston, MA Close</div>
<div id="fade" class="black_overlay"></div>
This will mean that as each thumb is clicked, it will do the light and fad bits it did before but it will also update the image being displayed.
Doing this for two images as a proof of concept I get this which works as expected:
<img src="http://chrissybulakites.com/thumbnails/longshot_thumbnail.png" />
<img src="http://chrissybulakites.com/thumbnails/actor_thumbnail.png" />
<div id="light" class="white_content"><img id='frame' src="http://chrissybulakites.com/images/longshot_full.png" /> <br />Actor Observor - Boston, MA Close</div>
<div id="fade" class="black_overlay"></div>
you need to give each full image div its own unique id like: id="image23". Then modify the onclick to refrence the corresponding id: onclick="document.getElementById('image23')...
The meaning of the function void() in JavaScript is "do nothing". This prevents it to load a new new page (or to open the thumbnail image).
onclick = "document.getElementById('light').style.display='block';
document.getElementById('fade').style.display='block' "
Says that when user clicks that item it will capture the element light and change the display to block it will also capture the element fade and change the display to block. The thing is all your images are wrapped in an element called "light" so the browser is opting to show the first one (instead of throwing an error).
There is plenty of fuzzy logic here.
Starting with the fact that you are loading all images (the high definition ones).
If you want my two cents (and you only want to get the results, as opposed to learn how JavaScript works) I would go with something like prettyPhoto that does it out of the box, in an easy and straightforward way and is well documented.
How to add prettyPhoto to your page?
Download the code and include both the Javascript and the CSS file's on your header.
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>
<link rel="stylesheet" href="css/prettyPhoto.css" type="text/css" media="screen" charset="utf-8" />
<script src="js/jquery.prettyPhoto.js" type="text/javascript" charset="utf-8"></script>
Then put this code on your page
<script type="text/javascript" charset="utf-8">
$(document).ready(function(){
$("a[rel^='prettyPhoto']").prettyPhoto();
});
</script>
The docs say to put it on the bottom of the page but you (should) also put it on the header.
And then put the thumbnails with links to the actual images. PrettyPhoto will take care of everything else. Do note the rel="prettyPhoto[my_gal]"
<a href="img/full/1.jpg" rel="prettyPhoto[my_gal]" title="Caption 1">
<img src="images/thumbnails/t_1.jpg" width="60" height="60" alt="Red round shape" />
</a>
You can customize it further and should really read the manual here.

Better alternative to an iframe to display tab content?

I have a page with an iframe to feature the contents of the clicked tab. There are 3 tabs and 1 iframe. The sources of the contents relating to each tab clicked are formatted and coded in other html & css files.
What is another alternative to using an iframe, because I noticed that when the tab is clicked, it still shows the white background, similar to when a new page is loading?
Here's my code:
<div id="tabs">
<div id="overview">
<a target="tabsa" class="imagelink lookA" href="toframe.html">Overviews</a>
</div>
<div id="gallery">
<a target="tabsa" class="imagelink lookA" href="tawagpinoygallery.html">Gallery</a>
</div>
<div id="reviews">
<a target="tabsa" class="imagelink lookA" href="trframe.html">Reviews</a>
</div>
</div>
<div id="tabs-1">
<iframe src="toframe.html" name= "tabsa" width="95%" height="100%" frameborder="0">
</iframe>
</div>
The only alternative to using IFRAMEs to load dynamic content (after the page has loaded) is using AJAX to update a container on your web page. It's pretty elegant and usually faster than loading a full page structure into an IFRAME.
Ajax with JQuery (use this and you will be loved on SO; the AJAX functions are great and simple)
Ajax with Prototype
Ajax with MooTools
Standalone Ajax with Matt Kruse's AJAX toolbox (Used to use this, using JQuery today because I needed a framework)
AJAX with Dojo (Said to be fast, but AJAX is not as straightforward)
Another alternative is to use AJAX to load the content of a tab and use a div to display the content. I would suggest that using an existing Tab library might be an option rather than trying to solve all the problems associated with creating tabs.
Maybe the jQuery UI Tab might be helpful here if you like to try it.
EDIT: AJAX example with UI Tabs.
First, the HTML will look like this.
<div id="tabs">
<ul>
<li><span>Overviews</span></li>
<li><span>Gallery</span></li>
<li><span>Reviews</span></li>
</ul>
</div>
Then make sure that you import the appropriate jQuery files:
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/ui-lightness/jquery-ui.css" type="text/css" media="all" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" type="text/javascript"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js" type="text/javascript"></script>
etc...
Then add the code to create the tabs:
<script type="text/javascript">
$(function() {
$("#tabs").tabs();
});
</script>
There's an alternative to AJAX!
You can load ALL three possible contents into separate DIVs.
Then clicking on a tab will simply make the display attribute of the appropriate content's DIV "block" while making the other two DIVs' display property "none".
Cheap, easy, does not require AJAX costs for extra http request or for coding.
Mind you, AJAX is a better solution if the contents of the tabs will change dynamically based on other data as opposed to being known at the time the page loads.
You don't need script.
<ul><li>foo link<li>bar link</ul>
<div class="tab" id="foo">foo contents</div>
<div class="tab" id="bar">bar contents</div>
Plus this CSS, in most browsers: .tab:not(:target) { display: none !important; }, which defaults to all content visible if :target isn't supported (any modern browser supports it).
If you're showing content with script, always hide it with script. Let it degrade gracefully if that script doesn't run.
It's probably better to load in the content for each tab into DIVs on the same page and then switch the visibility of each DIV when a tab button is clicked using JavaScript and the CSS display property.
If you can't do that then iframe is probably the best solution. You can make the iframe background transparent, see below:
<iframe src="toframe.html" name= "tabsa" width="95%" height="100%" frameborder="0" allowtransparency="true"></iframe>
You would then need to add the following CSS to the BODY element using:
BODY { Background: transparent; }
The HTML iframe is to be used to include/display non-template content, such as a PDF file. It's considered bad practice when used for template content (i.e. HTML), in both the SEO and UX opinions.
In your case you just want to have a tabbed panel. This can be solved in several ways:
Have a bunch of links as tabs and a bunch of div's as tab contents. Initially only show the first tab content and hide all others using CSS display: none;. Use JavaScript to toggle between tabs by setting CSS display: block; (show) and display: none; (hide) on the tab content divs accordingly.
Have a bunch of links as tabs and one div as tab contents. Use Ajax to get the tab content asynchronously and use JavaScript to replace the current tab contents with the new content.
Have a bunch of links as tabs and one div as tab contents. Let each link send a different GET request parameter or pathinfo representing the clicked tab. Use server-side flow-control (PHP's if(), or JSP's <c:if>, etc) or include capabilities (PHP's include(), or JSP's <jsp:include>, etc) to include the desired tab content depending on the parameter/pathinfo.
When going for the JavaScript based approach, I can warmly recommend to adopt jQuery for this.
This is jQuery example that includes another html page into your document. This is much more SEO friendly than iframe. In order to be sure that the bots are not indexing the included page just add it to disallow in robots.txt
<html>
<header>
<script src="/js/jquery.js" type="text/javascript">
</header>
<body>
<div id='include-from-outside'></div>
<script type='text/javascript'>
$('#include-from-outside').load('http://example.com/included.html');
</script>
</body>
</html>
You could also include jQuery directly from Google: http://code.google.com/apis/ajaxlibs/documentation/ - this means optional auto-inclusion of newer versions and some significant speed increase. Also, means that you have to trust them for delivering you just the jQuery ;)
As mentioned, you could use jQuery or another library to retrieve the contents of the HTML file and populate it into the div. Then you could even do a fancy fade to make it look all pretty.
http://docs.jquery.com/Ajax/jQuery.get
Something along these lines:
$.get("toframe.html", function(data){
$("#tabs-1").html(data);
});
edit..
you could prepopulate or onclick you could do the get dynamically
$("#tabs a").click(function(){
var pagetoget = $(this).attr("href");
$.get...
})
If you prepopulate could have three containers instead of the one you have now, 2 hidden, 1 display, and the click functions will hide them all except for the one you want.
The get is less code though, easier time.

Categories

Resources