I got strange flickering when coming to my blazor server side application.
For better user experience I implemented a GIF throbber but afterwards the loading is not fluently as you can see yourself:
https://shop6.gastroblitz.de/streetchooser
My goal is that the page should appear completely and in one refresh after the loading throbber is done:
some ideas for performance issues (but don't have an idea to improve with my current knowledge...):
_Host.cshtml
PreRender "HtmlHeadComponent" in the area (for generating domain specific title, meta data, etc.)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<base href="~/" />
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="height=device-height,
width=device-width, initial-scale=1.0,
minimum-scale=1.0, maximum-scale=1.0,
user-scalable=no" />
<link rel="stylesheet" href="/css/main.css" />
<link rel="stylesheet" href="https://owlcarousel2.github.io/OwlCarousel2/assets/owlcarousel/assets/owl.carousel.min.css" />
<link rel="stylesheet" href="https://owlcarousel2.github.io/OwlCarousel2/assets/owlcarousel/assets/owl.theme.default.min.css" />
<script src="_content/Microsoft.AspNetCore.ProtectedBrowserStorage/protectedBrowserStorage.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="/js/animations.js"></script>
<script src="/js/simplebar.min.js"></script>
<script src="/js/articlePopup.js"></script>
<script src="/js/accordion.min.js"></script>
<script src="https://owlcarousel2.github.io/OwlCarousel2/assets/owlcarousel/owl.carousel.js"> </script>
<script src="/js/my.js"></script>
<script src="_content/Blazor-Analytics/blazor-analytics.js"></script>
#(await Html.RenderComponentAsync<HtmlHeadComponent>(RenderMode.ServerPrerendered))
</head>
<body>
<app>
#(await Html.RenderComponentAsync<App>(RenderMode.ServerPrerendered))
</app>
<script src="_framework/blazor.server.js"></script>
<script>
Blazor.defaultReconnectionHandler._reconnectCallback = function (d) {
document.location.reload();
}
</script>
<script src="https://cdn.syncfusion.com/ej2/17.4.43/dist/ej2.min.js"></script>
<script src="https://cdn.syncfusion.com/ej2/17.4.43/dist/ejs.interop.min.js"></script>
</body>
</html>
App.razor
Here I just start when every data is loaded (see the if statement + many CascadingParameters for handling data changes in sub components)
<Router AppAssembly="#typeof(Program).Assembly">
<Found Context="routeData">
#if (AppState.StoreData != null &&
AppState.MultiStoreData != null &&
AppState.StoreChooserData != null)
{
<CascadingValue Value="AppState.StoreData" Name="StoreData">
<CascadingValue Value="AppState.MultiStoreData" Name="MultiStoreData">
<CascadingValue Value="AppState.StoreChooserData" Name="StoreChooserData">
<CascadingValue Value="AppState.BasketData" Name="BasketData">
<CascadingValue Value="AppState.CheckoutData" Name="CheckoutData">
<RouteView RouteData="#routeData" DefaultLayout="#typeof(MainLayout)" />
<GoogleAnalytics TrackingId="#AppState.StoreData?.StoreContainer?.Store?.GoogleAnalyticsId" />
</CascadingValue>
</CascadingValue>
</CascadingValue>
</CascadingValue>
</CascadingValue>
}
else
{
<img class="center" src="/img-dev/ajax-loader.gif" />
}
</Found>
<NotFound>
<LayoutView Layout="#typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
Last step is the given storechooser page which loads content also just when all data is available
#page "/streetchooser"
#inherits StreetChooserBase
#if (StoreChooserData != null && MultiStoreData != null)
{
<div class="slider" id="home">
<div id="owl-demo" class="owl-carousel owl-theme pitch_img">
#if (!string.IsNullOrEmpty(MultiStoreData.MultiStore.StreetChooserImage1))
{
<div class="item">
<img src="img/#StoreData?.StoreContainer?.Store?.Key/#MultiStoreData.MultiStore.StreetChooserImage1" alt="" class="pitch_img" />
</div>
}
#if (!string.IsNullOrEmpty(MultiStoreData.MultiStore.StreetChooserImage2))
{
<div class="item">
<img src="img/#StoreData?.StoreContainer?.Store?.Key/#MultiStoreData.MultiStore.StreetChooserImage2" alt="" class="pitch_img" />
</div>
}
#if (!string.IsNullOrEmpty(MultiStoreData.MultiStore.StreetChooserImage3))
{
<div class="item">
<img src="img/#StoreData?.StoreContainer?.Store?.Key/#MultiStoreData.MultiStore.StreetChooserImage3" alt="" class="pitch_img" />
</div>
}
#if (!string.IsNullOrEmpty(MultiStoreData.MultiStore.StreetChooserImage4))
{
<div class="item">
<img src="img/#StoreData?.StoreContainer?.Store?.Key/#MultiStoreData.MultiStore.StreetChooserImage4" alt="" class="pitch_img" />
</div>
}
#if (!string.IsNullOrEmpty(MultiStoreData.MultiStore.StreetChooserImage5))
{
<div class="item">
<img src="img/#StoreData?.StoreContainer?.Store?.Key/#MultiStoreData.MultiStore.StreetChooserImage5" alt="" class="pitch_img" />
</div>
}
</div>
<div class="slider_content ">
<div class="container">
<div class="row ">
<div class="col-sm-5">
#if (StoreChooserData != null && MultiStoreData != null)
{
<EditForm Model="#StoreChooserData">
<div class="search-section">
<div class="search-logo">
<img src="img/#StoreData?.StoreContainer?.Store?.Key/#StoreData?.StoreContainer?.Store?.LogoUrl" alt="" />
</div>
<p>Sushi und mehr online bestellen und abholen oder direkt nach Hause liefern lassen.</p>
<div class="row">
<div class="col-sm-6">
<div class="select-location #(StoreChooserData.OrderType == "1" ? "active" : "")"
#onclick="OrderTypeChangedToDelivery"> Lieferung</div>
</div>
<div class="col-sm-6">
<div class="select-location #(StoreChooserData.OrderType == "2" ? "active" : "")"
#onclick="OrderTypeChangedToPickup"> Selbstabholer</div>
</div>
</div>
<div class="search-rest">
<label>Plz / Ort </label>
<EjsComboBox Value="#StoreChooserData.PostcodeCitySite"
ModelType="#typeof(Db_DeliveryStreet)"
Placeholder="Ort eingeben"
ShowClearButton="true"
AllowCustom="true"
AllowFiltering="true"
FilterType="FilterType.Contains"
NoRecordsTemplate="<div>Keine Ergebnisse</div>"
DataSource="#MultiStoreData.DeliveryZones">
<AutoCompleteEvents TValue="string" ValueChange="PostcodeCityChanged"></AutoCompleteEvents>
<ComboBoxFieldSettings GroupBy="Postcode" Value="FullName"></ComboBoxFieldSettings>
<ComboBoxTemplates>
<GroupTemplate Context="Item">
<span class="group">#((Item as Db_DeliveryStreet).Postcode)</span>
</GroupTemplate>
<ItemTemplate Context="Item">
<span><span class='name'>#((Item as Db_DeliveryStreet).CitySite)</span><span class='minOrderValue'>#($"ab {(Item as Db_DeliveryStreet).MinOrderValue?.ToString("f2")}€")</span></span>
</ItemTemplate>
</ComboBoxTemplates>
</EjsComboBox>
<ValidationMessage For="#(() => StoreChooserData.PostcodeCitySite)">
</ValidationMessage>
</div>
<div class="search-rest">
<label>Straße / Hausnr. </label>
<EjsComboBox Value="#StoreChooserData.Street"
ModelType="#typeof(StreetItem)"
Placeholder="Straße eingeben"
ShowClearButton="true"
AllowCustom="true"
AllowFiltering="true"
FilterType="FilterType.Contains"
NoRecordsTemplate="<div>Keine Ergebnisse</div>"
DataSource="#Streets">
<AutoCompleteEvents TValue="string" ValueChange="StreetChanged"></AutoCompleteEvents>
<ComboBoxFieldSettings Value="Name"></ComboBoxFieldSettings>
</EjsComboBox>
<ValidationMessage For="#(() => StoreChooserData.Street)"></ValidationMessage>
</div>
<div class="search-rest">
<button class="search-send-btn" #onclick="#GoToNext">weiter </button>
</div>
</div>
</EditForm>
}
</div>
</div>
</div>
</div>
</div>
<FooterComponent />
}
Think one issue is that the images are requested very late in my code and produce the flickering...
But how could handle this in a better way? Or maybe another point could be some issue.
Hope you got my point.
Thanks in advance!
I am using MemoryCache to prevent flickering caused by using RenderMode.ServerPrerendered. The issue is discussed in this thread.
The problem with filckering can be avoided by caching the resultset between prerendering and rendering phase on server. Hopefully they fix it somehow in the next release.
On server-side blazor it is simpler and better to use the in memory
cache for that.
It avoids rountripping the state.
It can be combined with a react/redux approach where you simply pass an id for the store and put the store in a memory cache with the
id, which is then used to retrieve the store when the app reconnects
and to produce an identical render to the one it was prerendered.
As an example below is a modified WeatherService that shows that it
causes no flickering.
public WeatherForecastService(IMemoryCache memoryCache)
{
MemoryCache = memoryCache;
}
public IMemoryCache MemoryCache { get; }
public Task<WeatherForecast[]> GetForecastAsync(DateTime startDate)
{
return MemoryCache.GetOrCreateAsync(startDate, async e =>
{
e.SetOptions(new MemoryCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(30) });
var rng = new Random();
await Task.Delay(TimeSpan.FromSeconds(10));
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
}).ToArray();
});
}
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
The line await Task.Delay(TimeSpan.FromSeconds(10)); is there just for demonstration purposes, it is certainly not necessary to wait 10 seconds.
Related
I'm trying to pass movie.Id as a parameter and my IDE is complaining that I have to do a boxing conversion. I've looked up boxing conversions but don't know how to do it in this context. Also I'm trying to pass movies to increment and decrement the page and running into the same problem. The error is happening inside the anchor tag inside the foreach loop.
#using System.Collections
#{
Layout = null;
int IncrementPage(MovieResults movies) => movies.Page++;
int DecrementPage(MovieResults movies) => movies.Page--;
IEnumerable<MovieResults> movieResults = (IEnumerable<MovieResults>)ViewData["movies"]
Root movies = ViewData["movies"] as Root;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Movie Streaming Availability App</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="css/site.css">
</head>
<body>
<header>
<div class="container">
<div class="row">
<div class="col-4">
<button type="button" asp-controller="Home" asp-action="Index" style="background: #1b6ec2">Home Page</button>
</div>
<div class="col-6">
<h1 class="text-center">Movie Streaming Availability App</h1>
</div>
<div class="dropdown col-4">
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropDownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="False">Sort by Genres</button>
<div class="dropdown-menu" aria-labelledby="dropDownMenuButton">
<a class="dropdown-item" asp-controller="Movie" asp-action="GetMovies" asp-route-genreId="28">Action</a>
<a class="dropdown-item" asp-controller="Movie" asp-action="GetMovies" asp-route-genreId="12">Adventure</a>
<a class="dropdown-item" asp-controller="Movie" asp-action="GetMovies" asp-route-genreId="16">Animation</a>
<a class="dropdown-item" asp-controller="Movie" asp-action="GetMovies" asp-route-genreId="35">Comedy</a>
<a class="dropdown-item" asp-controller="Movie" asp-action="GetMovies" asp-route-genreId="80">Crime</a>
</div>
</div>
</div>
</div>
</header>
<main>
<div class="container">
<div class="row">
#foreach (var movie in movieResults)
{
<div class="col-3">
<a asp-controller="Movie" asp-action="MoviePage" asp-route-Id="#movie.Id"><img src="https://image.tmdb.org/t/p/w300/#movie.PosterPath" alt="#movie.Title"></a>
<label>#movie.Title</label>
</div>
}
</div>
</div>
</main>
<footer>
<div class="container">
<div class="row">
<div class="col-3">
<button type="button" asp-controller="Movie" asp-action="GetMovies" asp-route-page="#(new MovieResults(movies.Page), #IncrementPage(movies))">Next Page</button>
</div>
<div class="col-3">
<button type="button" asp-controller="Movie" asp-action="GetMovies" asp-route-page="#(new MovieResults(movies.Page), #DecrementPage(movies))">Previous Page</button>
</div>
<div class="col-3">
<label class="text-center">Total Number of Pages</label>
<h3 class="text-center">#(new MovieResults(movies.TotalPages),movies.TotalPages)</h3>
</div>
<div class="col-3">
<label class="text-center">Total Number of Results</label>
<h3 class="text-center">#(new MovieResults(movies.TotalResults),movies.TotalResults)</h3>
</div>
</div>
</div>
<script type="text/javascript" src="lib/jquery/dist/jquery.js"></script>
<script type="text/javascript" src="lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
<script type="text/javascript" src="~/js/site.js" asp-append-version="true"></script>
</footer>
</body>
</html>
You can to this only, and only if you are sure that the object contains an integer, otherwise it will throw a runtime conversion error exception
For razor #((int)Model.Integer), for code int IncrementPage(MovieResults movies) => ((int)movies.Page)++;
It's never a good ideia to cast objects with no real need, specially inside a foreach loop since boxing/unboxing uses a nice amount of resources to do so. I believe that the best approach is adapt your Model to reflect the "front-end" needs and avoid casting
I'm trying to use the materialize framework for css with a Blazor application, however, when I copy/paste some of the examples into a layout, component, etc I don't get what Materialize shows on the example. There aren't any CSS/JS loading or console errors that can be identified in dev tools, but it seems like something isn't loading because lots of components aren't working as the examples show.
For example, the tab indicator that apperas under the active tab and hops from tab to tab when you select one.
layout page:
#inherits LayoutComponentBase
<div class="main">
<nav class="nav-extended">
<div class="nav-wrapper">
Logo
<i class="material-icons">menu</i>
<ul id="nav-mobile" class="right hide-on-med-and-down">
<li>Sass</li>
<li>Components</li>
<li>JavaScript</li>
</ul>
</div>
<div class="nav-content">
<ul class="tabs tabs-transparent">
<li class="tab">Test 1</li>
<li class="tab"><a class="" href="#test2">Test 2</a></li>
<li class="tab disabled">Disabled Tab</li>
<li class="tab">Test 4</li>
</ul>
</div>
</nav>
<div class="content px-4">
#Body
</div>
</div>
Edit
Looks like the M.AutoInit() isn't firing properly. Can someone point out where the best place to call that function would be? I tried a
document.onload = M.AutoInit();
on the index.html page and a interop JS on my component
#code {
...
protected override void OnInitialized()
{
JSRuntime.Invoke<string>("M.AutoInit");
}
...
}
Neither worked but maybe I'm doing the JSInterop wrong, couldn't find a definitive example, similar to what I'm trying to do, in the docs. Any suggestions or pointers would be great!
This works for me(very important respect the async await, so if you have any errors the Exception is properlly thowed to your browser console)
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (firstRender)
{
await JSRuntime.InvokeVoidAsync("M.AutoInit");
}
}
In the static JavaScript file that is used to initialize materialize elements, add AutoInit method to load first on DOMContentLoaded event listener.
Consider this example that initializes Materialize Sidenav:
In the Sidenav.razor:
<nav> <!-- navbar content here -optional --> </nav>
<ul id="slide-out" class="sidenav">
<li>
<div class="user-view">
<div class="background">
<img width="300" src="office.jpg">
</div>
<a href="#user">
<img class="circle" width="50"
src="user_female.svg.png">
</a>
<span class="white-text name">John Doe</span>
<span class="white-text email">xyz#xyz.com</span>
</div>
</li>
<li><i class="material-icons">cloud</i>First Link With Icon</li>
<li>Second Link</li>
<li><div class="divider"></div></li>
<li><a class="subheader">Sub-header</a></li>
<li><a class="waves-effect" href="#!">Third Link With Waves</a></li>
</ul>
<i class="material-icons">menu</i>
In the main.js:
(function () {
"use strict";
var M = window.M || globalThis.M;
function activateSideNav() {
var elems = document.querySelectorAll('.sidenav');
var options = {};
M.Sidenav.init(elems, options);
}
document.addEventListener('DOMContentLoaded', function () {
M.AutoInit();
setTimeout(activateSideNav, 864);
});
})();
In the MainLayout.razor:
<Sidenav />
<div class="row">
<div class="col s12 m12 l12 xl12">
#Body
</div>
</div>
Also make sure that Materialize libraries are referenced properly in the _Host.cshtml
#page "/"
#namespace Example.Pages
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
#{
Layout = null;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Example</title>
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
<base href="~/" />
<link href="css/materialize/css/materialize.css" rel="stylesheet" />
<link href="css/main.css" rel="stylesheet" />
</head>
<body>
<component type="typeof(App)" render-mode="ServerPrerendered" />
<script src="_framework/blazor.server.js"></script>
<script src="css/materialize/js/materialize.js"></script>
<script src="js/main.js"></script>
</body>
</html>
I am creating asp.net web api and i am trying to add a calendar control in one of the fields via date-time picker. After i run the application and press the button for creating article/event it shows this error: System.Web.HttpException: 'Section already defined: "scripts".'
Here is my _Layout.cshtml
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>#ViewBag.Title - Public Events</title>
#Styles.Render("~/Content/css")
#RenderSection("styles", required: false)
#Scripts.Render("~/bundles/modernizr")
#Scripts.Render("~/bundles/jquery")
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
#Html.ActionLink("Events", "Index", "Home", new { area = "" }, new { #class = "navbar-brand" })
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
#if (Request.IsAuthenticated)
{
<li>#Html.ActionLink("My Events", "My", "Events")</li>
<li>#Html.ActionLink("Create Event", "Create", "Events")</li>
}
</ul>
#Html.Partial("_LoginPartial")
</div>
</div>
</div>
<div class="container body-content">
#Html.Partial("_Notifications")
#RenderBody()
<hr />
<footer>
<p>© #DateTime.Now.Year - Events Application</p>
</footer>
</div>
#Scripts.Render("~/bundles/bootstrap")
#RenderSection("scripts", required: false)
</body>
</html>
Scripts section defined twice. Remove one or combine them.
#section scripts
{
//...
}
I am using this post to disable my buttons on click:
Prevent multiple form submits in MVC 3 with validation
This is my page source after it is rendered:
<!DOCTYPE html>
<html>
<head>
<title>Do not bookmark this page.</title>
<link href="/Content/reset.css" rel="stylesheet" type="text/css" />
<link href="/Content/Site.css" rel="stylesheet" type="text/css" />
<link href="/Content/styer.css?t=335" rel="stylesheet" type="text/css" />
<script src="/Scripts/jquery-1.5.1.min.js" type="text/javascript"></script>
<script src="/Scripts/Views/main.js" type="text/javascript"></script>
<link rel="shortcut icon" href="/Content/favicon.ico" type="image/ico" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<script language="javascript" type="text/javascript">
$(function () {
$('form').submit(function () {
if ($(this).valid()) {
$('input[type="submit"]').attr('disabled', 'disabled');
}
});
});
</script>
</head>
<body>
<div class="page">
<div id="header">
<div id="headerimg"></div>
<div id="logindisplay">
[ Log On ]
</div>
<div id="menucontainer">
<ul id="menu">
<li>Home</li>
<li>Logon</li>
<li>Register</li>
<li>Contact Us</li>
<li>News</li>
</ul>
</div>
<div id="main">
<h2>Forgot Username</h2>
<p>
Please provide the email address on your web account. Once validated, your username will be sent to your email address for your records.
</p>
<script src="/Scripts/jquery.validate.min.js" type="text/javascript"></script>
<script src="/Scripts/jquery.validate.unobtrusive.min.js" type="text/javascript"></script>
<form action="/Account/ForgotUsername" method="post"> <div>
<fieldset>
<legend>Email address</legend>
<div class="editor-label">
<label for="EmailAddress">Email</label>
</div>
<div class="editor-field focus">
<input class="GenericTextBox" data-val="true" data-val-length="The Email must be at least 7 characters long." data-val-length-max="100" data-val-length-min="7" id="EmailAddress" name="EmailAddress" type="text" value="" />
<span class="field-validation-valid" data-valmsg-for="EmailAddress" data-valmsg-replace="true"></span>
</div>
<p>
<input id="btn" class="makePaymentInput" type="submit" value="Submit"/>
</p>
</fieldset>
</div>
</form>
<div style="clear: both;"></div>
</div>
<div id="footer">
</div>
</div>
</div>
<!-- Start of StatCounter Code for Default Guide -->
<script type="text/javascript">
var sc_project = 9150358;
var sc_invisible = 1;
var sc_security = "1af31df9";
var scJsHost = (("https:" == document.location.protocol) ? "https://secure." : "http://www.");
document.write("<sc" + "ript type='text/javascript' src='" + scJsHost + "statcounter.com/counter/counter.js'></" + "script>");
</script>
<noscript>
<div class="statcounter"><a title="web analytics" href="http://statcounter.com/" target="_blank"><img class="statcounter" src="http://c.statcounter.com/9150358/0/1af31df9/1/" alt="web analytics"></a></div>
</noscript>
<!-- Visual Studio Browser Link -->
<script type="application/json" id="__browserLink_initializationData">
{"appName":"Internet Explorer"}
</script>
<script type="text/javascript" src="http://localhost:25759/610299fe88d74cee8d0267b4fc859da0/browserLink" async="async"></script>
<!-- End Browser Link -->
</body>
</html>
This works on most forms but this one, which is the main reason I used this code.
Anyone see why this one is not working?
Instead of writing this :-
$('input[type="submit"]').attr('disabled', 'disabled');
You should write this :-
$('input[type="submit"]').prop('disabled','true');
I found simple textbox watermark script that I was going to use in my project but I can't understand whats wrong, I tried debugging with Firebug and I can see it going through jquery code only once when page is loaded,after that textbox acts like nothing was binded to its focus or blur and I don't see any breakpoints getting hitted in script, here is whole layout page with script:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>#ViewBag.Title</title>
<link href="#System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl("~/Content/css")" rel="stylesheet" type="text/css" />
<link href="#System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl("~/Content/themes/base/css")" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="#System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl("~/Scripts/js")"></script>
<style type="text/css">
.water
{
font-family: Tahoma, Arial, sans-serif;
color:gray;
}
</style>
<script type="text/javascript">
$(document).ready(function () {
$(".water").each(function () {
$tb = $(this);
if ($tb.val() != this.title) {
$tb.removeClass("water");
}
});
$(".water").focus(function () {
$tb = $(this);
if ($tb.val() == this.title) {
$tb.val("");
$tb.removeClass("water");
}
});
$(".water").blur(function () {
$tb = $(this);
if ($.trim($tb.val()) == "") {
$tb.val(this.title);
$tb.addClass("water");
}
})
});
</script>
</head>
<body>
<div class="wrapper">
<div id="messageBox" align="center">
</div>
<div class="header">
<div class="header-column">
<h1 id="logo" class="no-border"><img src="../../Content/themes/base/images/ps-logo.png" style="margin-top:10px;" alt="" /></h1>
</div>
<div class="header-column lh50" align="center">
<div>
<input type="text" ID="txtSearch" class="water" title="Search" value="" />
</div>
</div>
<div class="header-column">
<div class="main-menu lh50">
<ul>
<li>
#if(!Request.IsAuthenticated)
{
<a href="Login">Login using
<img alt="Facebook" src="../../Content/themes/base/icons/facebook-icon.png" class="login-icon" />
<img alt="Google" src="../../Content/themes/base/icons/google-icon.png" class="login-icon" />
<img alt="Yahoo" src="../../Content/themes/base/icons/yahoo-icon.png" class="login-icon" />
</a>
<span> or </span>
Register
}
else{
<span>#GetCurrentUsername(this.Context)</span>
Log out
Post
}
</li>
</ul>
</div>
</div>
</div>
<div class="clear">
</div>
#RenderBody()
<div class="push">
</div>
</div>
<div class="footer" align="center">
<ul>
<li>Home</li>
<li>Services</li>
<li>Portfolio</li>
<li><a class="active" href="#">Resources</a></li>
<li>Contact</li>
</ul>
<p>
Copyright © 2012 Domain - All rights reserved</p>
</div>
</body>
</html>
Is there problem in mvc4, my code or something else?
The script works, you just need to ensure that the value of the textbox is the same as the title initially:
<input type="text" ID="txtSearch" class="water" title="Search" value="Search" />
Because that's what you are checking here:
$(".water").each(function () {
$tb = $(this);
if ($tb.val() != this.title) {
$tb.removeClass("water");
}
});
And if the value is different than the title (in your case title="Search" and value="") you remove the water class and nothing will happen later.
You are using the same .water class to describe the set of textboxes that might have watermarks, and also to specifically turn the watermark on and off.
That could get messy, as when you attach the focus and blur events its no longer clear what the .water selector will find because you've already removed it from some textboxes.
Think it should be more like:
$(document).ready(function () {
$(".potentialwater").each(function () {
$tb = $(this);
if ($tb.val() != this.title) {
$tb.removeClass("water");
}
});
$(".potentialwater").focus(function () {
$tb = $(this);
if ($tb.val() == this.title) {
$tb.val("");
$tb.removeClass("water");
}
});
$(".potentialwater").blur(function () {
$tb = $(this);
if ($.trim($tb.val()) == "") {
$tb.val(this.title);
$tb.addClass("water");
}
})
});