Running javascript function conditionally - Razor, HTML - javascript

Right now I have a table that's created dynamically using user selection + data. Sometimes, there are errors with the data, and I'd like to let a user know in the table.
Razor (the link takes you to ~/ControllerName/givenID)
<tr id="#givenID">
<td>#Html.ActionLink(name, someMethod, "ControllerName", new { id = givenID }, new { title = "", id = givenID })</td>
#if (errorConditions)
{
<div id="#givenID" onload="addErrorMessage(this)" data-test="ErrorMessage" style="display: none"></div>
}
</tr>
Javascript
$(function () {
//adding custom tooltip to document
$(document).tooltip();
//adding error message to standard
function addErrorMessage(element) {
var theElemOfID = document.getElementById(element.id);
theElemOfID.title = element.attr("data-test");
theElemOfID.style.color = rgb(255, 0, 0);
} });
I'm trying to add a title to the ActionLink (so it can be used by the tooltip) and also change the color of the link, but only when the the errorConditions are true.
Right now when I run, nothing happens. The addErrorMessage never calls, but all the information is in the places I expect (as in the link has the right ID, etc).
Does anyone have an idea?

onload is only supported on the <body>, but it is highly recommended that you do not use inline event handlers!
Use an inline script, like this (using jQuery since you seem to be using it):
#if (errorConditions)
{
<div id="#givenID" data-test="ErrorMessage" style="display: none"></div>
<script>
$(function() {
addErrorMessage($('##givenID'));
});
</script>
}

Related

Avoid flickering when changing existing DOM elements on page load event

I have the following DOM element change function using vanilla JavaScript where I change the span element that contains some text string of the DOM with a page load event.
With the following code, the DOM elements are changed as expected. However, they still see a minimal fraction of flickering before the DOM element change for the variable desktop and mobile.
Example of the flickering scenario:
I see the tag span "Text I want to change" for a fraction of second.
I see the changed tag span "text1 changed" after the content has fully loaded.
I think this is happening because the DOM changes are applied when the DOM content of the page is fully loaded. I would like to hide the existing tag elements until the changes have been applied and then display them.
The elements structure for desktop and mobile I want to manipulate is like the following:
<span class="first-headline target-span">Text I want to change</span>
See the concept below:
var changesObj = {
"us": {
title: "text1 changed"
},
"eu": {
title: "text2 changed"
}
};
function changes() {
var webshop = changesObj[window.location.pathname.split('/')[1]];
console.log(webshop);
var changesText;
if (!webshop) {
console.log('webshop not found');
}
changesText = webshop.title;
if (document.querySelector('.target-span').innerText.length > 0) {
var desktop = document.querySelector('.target-span');
console.log(desktop);
desktop.innerText = changesText;
console.log(desktop.innerText);
console.log("applied changes for dekstop");
}
if (document.querySelector('.target-span').innerText.lenght > 0) {
var mobile = document.querySelector('.target-span');
mobile.innerText = changesText;
console.log(mobile.innerText);
console.log("applied changes for mobile");
}
}
function invokeChanges() {
document.addEventListener("DOMContentLoaded", function () {
changes();
});
}
invokeChanges();
Is there a way to initially hide the DOM element until the change to the existing element has been applied and then show it?
I was thinking of using something inline CSS rules like the following:
Set .style.visbility='hidden' and when the text content is changed to show it with .style.visbility='visble'.
But I'm not sure how this solution should be appropriately implemented in my code.
There are a few reasons as to why it's flickering, but two preventative steps you can take:
Use defer on your script tag <script defer>, as this lets the browser handle the order your scripts are run instead of DOMContentLoaded. You also get to avoid the changes wrapper function.
As suggested by this question (and your own reasoning) attach inline CSS / a CSS file to the page to set the text as invisible, then mark it as visible
If it doesn't affect the layout of your page too much, you can also opt to dynamically generate the element instead.
However, do note that regardless of any of these, these still require JS to be executed and may still have a delay. If you are able to use it, you may be interested in prerendering your webpage - from your JS code, it looks for the route name if its eu or us, which means your page is pre-renderable.
Quick & Dirty:
Place the script tag just below your span tag:
<span class="first-headline target-span">Text I want to change</span>
<script>
var changesObj = {
"us": {
title: "text1 changed"
},
"eu": {
title: "text2 changed"
}
};
function changes() {
var webshop = changesObj[window.location.pathname.split('/')[1]];
console.log(webshop);
var changesText;
if (!webshop) {
console.log('webshop not found');
}
changesText = webshop.title;
if (document.querySelector('.target-span').innerText.length > 0) {
var desktop = document.querySelector('.target-span');
console.log(desktop);
desktop.innerText = changesText;
console.log(desktop.innerText);
console.log("applied changes for dekstop");
}
if (document.querySelector('.target-span').innerText.lenght > 0) {
var mobile = document.querySelector('.target-span');
mobile.innerText = changesText;
console.log(mobile.innerText);
console.log("applied changes for mobile");
}
}
changes();
</script>
This way you avoid the asynchronous code part.
In any case you need to wait until the DOM has been loaded before you can manipulate it. Using an even listener for DOMContentLoaded would be the way to go. So, three things need to happen:
Wait for the DOM to load
Find the elements and change the text
Make the elements visible. You can either use the property visibility: hidden or display: none. Difference is that with visibility: hidden the element will still take up space. So, the choice depends on the context.
In the first example I add a timeout so that you can see what the page looks like just before the text is changed. I also styled the <p> (display: inline-block) so that you can see the size of the hidden span.
window.location.hash = "us"; // for testing on SO
var changesObj = {
"us": { title: "text1 changed" },
"eu": { title: "text2 changed" }
};
function changes(e) {
//let webshop = changesObj[window.location.pathname.split('/')[1]];
let webshop = changesObj[window.location.hash.substring(1)]; // for testing on SO
if (webshop) {
[...document.querySelectorAll('.target-span')].forEach(span => {
span.textContent = webshop.title;
span.classList.add('show');
});
}
}
document.addEventListener('DOMContentLoaded', e => {
setTimeout(changes, 1000);
});
p {
border: thin solid black;
display: inline-block;
}
.target-span {
visibility: hidden;
}
.target-span.show {
visibility: visible;
}
<p>
<span class="first-headline target-span">Text I want to change</span>
</p>
<p>
<span class="first-headline target-span">Text I want to change</span>
</p>
In the second example I combined all the code into one HTML page. Notice that the style is defined in the header. So, when the DOM is passed the CSS will be passed as well without having to load a external style-sheet (well, there are tricks for that as well, but out of scope for this answer).
<html>
<head>
<style>
p {
border: thin solid black;
}
.target-span {
visibility: hidden;
}
.target-span.show {
visibility: visible;
}
</style>
<script>
window.location.hash = "us"; // for testing on SO
var changesObj = {
"us": {title: "text1 changed"},
"eu": {title: "text2 changed"}
};
function changes(e) {
//let webshop = changesObj[window.location.pathname.split('/')[1]];
let webshop = changesObj[window.location.hash.substring(1)]; // for testing on SO
if (webshop) {
[...document.querySelectorAll('.target-span')].forEach(span => {
span.textContent = webshop.title;
span.classList.add('show');
});
}
}
document.addEventListener('DOMContentLoaded', changes);
</script>
</head>
<body>
<p>
<span class="first-headline target-span">Text I want to change</span>
</p>
<p>
<span class="first-headline target-span">Text I want to change</span>
</p>
</body>
</html>

JavaScript click event on div

I am working on JavaScript project and I am having a problem with click event and retrieving the correct information about the element clicked. I am relatively new to the JavaScript.
The real code I am working on is fairly complex however I am posting only a chunk of code to illustrate my problem.
function App(){
this.name = "New App";
}
App.prototype.createDIV = function() {
var h = "<div class='clickable' id='idToShow'><div class='name' id='notToShow'>" + this.name + "</div></div>";
$('#content').html(h);
}
App.prototype.showID = function(e) {
if (e.target.id == 'idToShow') {
alert(this.name); // this doesn't display, because incorrect ID is retrieved
}
}
$(document).ready(function() {
var newApp = new App();
$("input#btn").click(newApp.createDIV.bind(newApp));
$("div").on("click", ".clickable", newApp.showID.bind(newApp));
});
<script src="http://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
</head>
<div id="content"></div>
<input type="button" id="btn" value="CLICK">
I have a object in the app with number of prototypes. On document load, object is constructed, events are attached to elements and bind to object. Clicking the button, new set of div results are created dynamically.
Now this is where it starts to go wrong for me. I attached an event to div with the class CLICKABLE and I would like to retrieve the id of that particular DIV element (id='idToShow'); however I keep retrieving the id of the following DIV (id='notToShow').
I might not fully understand why is this happening and what to do to prevent it in order to get the correct ID.
Try using if (e.currentTarget.id == 'idToShow') instead of if (e.target.id == 'idToShow').

Get numerical value from parent with id like 'post-1' and use it in jQuery function

I'm trying to figure out the following.
I have following jQuery code:
var as = "";
var bPlay = 0;
audiojs.events.ready(function() {
as = audiojs.createAll();
$(".audiojs .play-pause").click(function() {
var e = $(this).parents(".audiojs").index(".audiojs");
$.each(as, function(t, n) {
if (t != e && as[t].playing) {
as[t].pause()
}
})
bPlay = !bPlay;
if (bPlay == 1) {
$(".bar").each(function(i) {
fluctuate($(this));
});
} else {
$(".bar").stop();
}
})
});
In a nutshell it preforms list of things when someone clicks particular .audiojs instance on a page. 1) checks if there is any other instance playing, if there is pauses it. And if it is playing applies fluctuate function to elements on a page that have class="bar". This is the issue! I don't want to apply it to all .bar's on a page, but only to a specific group that is associated with particular .audiojs instance (the one that is being clicked and is playing).
I thought of the following solution. Each .audiojs instance is inside a div tag that has id like "post-1", "post-2" etc.. where numerical value is post id from database. I can add this numerical id to bar, so it would be like bar-1, bar-2 etc... However after this I'm having issues.
For javascript to work I need to retrieve numerical value from "post-[id]" associated with audiojs instance that is being clicked and than store it somehow, so I can use it like this afterwards
bPlay = !bPlay;
if (bPlay == 1) {
$(".bar-[value retrieved from post-...]").each(function(i) {
fluctuate($(this));
});
} else {
$(".bar-[value retrieved from post...]").stop();
}
Could someone explain to me how it can be achieved?
Honestly, the easiest way would be to stick it in a custom data-* attribute on the <div id="post-X"> element, like so:
<div id="post-1" data-bar="bar-1">...</div>
Then, you said your .audiojs element is inside that <div>, so just go from this inside the event handler to that <div> element (using .closest()) and get the value of it:
var barId = $(this).closest('[id^="post-"]').attr('data-bar');
Then when you need to use it:
$("." + barId).each(function(i) {
fluctuate($(this));
});
Instead of embedding the value in a class or ID, use a data-* attribute:
<div class="audiojs" data-fluctuate-target="bar-1">
<button type="button" class="play-pause">
<!-- ... -->
</button>
</div>
<div class="bar-1">
<!-- ... -->
</div>
In your click event handler, use the following to fluctuate or stop the correct elements:
var fluctuateClass = $(this).closest('.audiojs').attr('data-fluctuate-target');
$('.' + fluctuateClass).each(function () {
if (bPlay == 1) {
fluctuate($(this));
} else {
$(this).stop();
}
});

manipulate a property of a child element

I have the following row element with a double click event atached to it.
<tr ondblclick="onLabelDoubleClicked(this)">
<td><label id="labelId" >My Label</label></td>
<td><input type="text" id="myInput" data-bind="kendoDropDownList: { data: source, value: myValue, enable: true }" /></td>
</tr>
On double click I need to set the enable property of kendoDropDownList in input element to toggle true/ false.
javascript:
onLabelDoubleClicked = function (event) {
}
I searched through the event properties but could not find anything useful to get the enable property and manipulate it. Any help with working example will be greatly appreciated. Thank You!
Instead of inlining the doubleclick event, it’s easier if you put the handler in the JS code and traverse/change the DOM from there.
I’m not sure about the Kendo stuff in the data-bind property, but it looks like a string to me so you’ll need to do string replaces unless you have a better way.
try this:
$('tr').dblclick(function() {
var $el = $(this).find(':text'),
data = $el.data('bind');
if (/true/.test(data)) {
data = data.replace(/true/,'false');
} else if (/false/.test(data)) {
data = data.replace(/false/,'true');
}
$el.prop('data-bind', data);
});
If you can use jQuery (and tag sets implies you're), why not just use jQuery (and Kendo) methods?
$('tr').dblclick(function() {
var $kd = $(this).find('input').data("kendoDropDownList");
$kd.enable( $kd.element.is(':disabled') );
});

Ajax callback refresh div

I implemented a Ajax callback function to update a div (announcement) in my web page.
It works fine, but the current issue is that the Ajax call function update the announcement div, however, after rewrite the content, the Javascript functions in this content are not load it. Therefore, my toggle function in table does not work after update content.
My ajax call function
setInterval("_checkUpdate()", 2000); //2 seconds one call.
function _checkUpdate() {
var callback=new Object();
callback.success=this.checkUpdateonExternalSuccess;
callback.failure=this.checkUpdateonExternalFailure;
YAHOO.util.Connect.asyncRequest('GET' , '/ci/ajaxCustom/ajaxCheckUpdate',callback);
};
On success, it update the content to rewrite to my Div
function checkUpdateonExternalSuccess (o){
if(o.responseText!==undefined) {
var str=o.responseText;
if(str !== 'nocontent') {
document.getElementById('updateContent').innerHTML=str; (Write the new content to my div)
location.reload(); //Reload the whole page, i do not want that
}
}
};
It works, it write the new content with Javascript, but the Javascript functions are not load it.
Javascript:
$(document).ready(function() {
//First hide all the contects except the headcontent
$(".content").hide();
$(".headContent").show();
//If user click on the head title, it hide/show content
$(".headTitle").click(function () {
collapseHeadContent();//reset all
var aParentTD= $(this).closest("td");
var aHeadContent = aParentTD.find (".headContent");
aHeadContent.toggle();
});
// If uses click on the title, it has toggle event for the content
$(".title").click(function () {
collapseContent(); //First reset all
var aParentTD = $(this).closest("td");
var aContent = aParentTD.find(".content"); // Content in the same TD with Title
aContent.toggle();
});
});
function collapseContent() {
//find the Head content to hide and reset of content
$(".headContent").hide();
$(".content").hide();
}
function collapseHeadContent() {
$(".content").hide();
}
HTML:
<div id="annoucementTableDiv">
<table id="annoucementTable">
<tbody id="tdContent">
<tr>
<td><span id="tdtitle"><a class="headTitle"> Forbidden: Error 403</a></span> <br />
<span class="annfrom">PLC team - 19/08/2010 at 10:30<br /> </span>
<br />
<span class="headContent"><P>Due to scheduled maintenance, HOME showed the error 403:<br/>
This is now resolved and customers can update again as normal </P>
</span>
</td>
</tr>
</tbody>
<tbody id="tdContent">
<tr>
<td>
<span id="tdtitle">
<a class="title">Downloading maps</a>
</span> <img src="/euf/assets/themes/standard/images/layout/important.png" class="alert_icon" alt="alert"></img><br />
<span class="annfrom">Sent by 2nd line - 05/11/2009 at 15:30<br /> </span>
<span class="content">Since this morning it has been reported that multiple customers are again receiving this error message.<br /><br />This error has been escalated to the relevant teams with the highest priority and is currently being looked into.
An estimated time for a fix is not yet available but we will keep you updated.
</span>
<br />
</td>
</tr>
</tbody>
</table>
</div>
If I do location.reload, the Javascript functions work. But I do not want to refresh the page.
Any ideas?
Since you're also hiding/showing things, I recommend you change you're ready handler into a named function then call it on document.ready, like this:
function loadFunc() {
$(".content").hide();
$(".headContent").show();
}
$(function() {
loadFunc():
$(".headTitle").live('click', function () {
collapseHeadContent();
$(this).closest("td").find(".headContent").toggle();
});
$(".title").live('click', function () {
collapseContent();
$(this).closest("td").find(".content").toggle();
});
});
This also uses .live() to set your click functions up once. Then when you do your AJAX load, you can just call loadFunc again to do hiding/showing or any other work, like this:
function checkUpdateonExternalSuccess (o){
if(o.responseText!==undefined) {
var str=o.responseText;
if(str !== 'nocontent') {
document.getElementById('updateContent').innerHTML=str;
loadFunc();
}
}
};
Sounds like you need the live function. It is quite straight forward.
$('.title').live('click', function() {
collapseContent(); //First reset all
var aParentTD = $(this).closest("td");
var aContent = aParentTD.find(".content"); // Content in the same TD with Title
aContent.toggle();
});
Basically adds the click handler to all title elements. Even if another loads the event is still associated with it. You can use this for all elements that are loaded by AJAX during the life of the page. So you do the same for the head element and so on.
In a project I'm writing at the moment, I insert HTML and Javascript content separately. The HTML is inserted as usual into innerHTML. And I got the javascript in a JS file which I load into my document during runtime like this:
var newScript = document.createElement('script');
newScript.type = 'text/javascript';
newScript.src = 'myJavascriptFile.js';
headID.appendChild(newScript);
Wrap your table related code into a function and call it once on document.ready and after you ajax-request. Avoid triggering document.ready as suggested by Uoli, because it can cause unpredictable side effects as all your $(document).ready(function() { blocks will be executed again.

Categories

Resources