HTML comment if without javascript - javascript

I know that I can display/hide content whether the browser is IE or not or even the version of IE. I was wondering if I can use other expressions too such as
<!--[if 1 == 0]-->
This should be hidden
<!--[endif]-->
The reason behind this is that I'm sending auto generated E-Mails and for me it would be easier to insert such comments in the template E-Mail instead of creating multiple templates.

if you have a template system, then make this in your template. Anyway when you render the template you calculate the condition, but instead of printing "0 == 1" or "0 == 0", use the template's ability to print or not to print the following paragraph

I know this would look like a long answer but I just wanted to divide the code into small functions each does its own job -kind of-, first select each element with a class name of hasComment in an array using querySelectorAll then pass this array to updateHTML() function, loop through its element and call returnComment() function for each item in the array.
The returnComment() function first call hasComment() function on the element passed to it, and using .replace() to get the exact string. Function hasComment() loop through the child nodes of the element and if the nodeType of the child node is 8 it then it's a comment, we return the text between the comment <!-- and -->.
This .replace(/\[|\]/ig, ''); omits the brackets to get value of either show or hide which according to it we "hide" or "show" the child .contentDiv div.
JS Fiddle
var commentDivs = document.querySelectorAll('.hasComment');
updateHTML(commentDivs);
function updateHTML(arr) {
for (var i = 0; i < arr.length; i++) {
var childDiv = arr[i].querySelector('.contentDiv'),
showIt = returnComment(arr[i]);
if (showIt == 'show') {
childDiv.style.display = 'block';
console.log('Div-' + (i + 1) + ': shown');
} else if (showIt == 'hide') {
childDiv.style.display = 'none';
console.log('Div-' + (i + 1) + ': hidden');
}
}
}
function returnComment(element) {
var comment = hasComment(element);
comment = comment.replace(/\[|\]/ig, '');
return comment;
}
function hasComment(element) {
for (var i = 0; i < element.childNodes.length; i++) {
if (element.childNodes[i].nodeType == 8) {
return element.childNodes[i].data;
}
}
}
<div class="hasComment">
<!--[hide]-->
<div class="contentDiv">Div -1: This should be hidden</div>
</div>
<hr>
<div class="hasComment">
<!--[hide]-->
<div class="contentDiv">Div -2: Again, This should be hidden</div>
</div>
<hr>
<div class="hasComment">
<!--[show]-->
<div class="contentDiv">Div -3: But this should be shown</div>
</div>
----------
Notes:
Wrapping the all contents of each .hasComment elements making controlling the content easier.
The above solution only work on the very top level of .hasComment element children, so if you have other comments inside .contentDiv these comments won't be affected.Demo Fiddle
You could probably use [if 1==0] for "templating" like in your code then use eval() or more complex regex to check upon it, but IMHO I think using show and hide look easier and mostly less bugs as you this over and over through your document.
More details about nodeType:
https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
https://developer.mozilla.org/fr/docs/Web/API/Node/nodeType
http://www.w3schools.com/xml/dom_nodetype.asp

Since you are developing for email clients, no this isn't possible. You need to figure out how different clients can be targeted. Then set the display property via CSS to whatever is affected.
Ideally, your emails shouldn't need any kind of crazy logic like this. It is a smell that your email is bad. Not to mention, anything you put in the email itself is viewable, all someone needs to do is turn off HTML rendering or view the source.

Related

Toggle many classes

so thanks for you all that help me. You made me think what i was doing, so i short my code in what i thought would be good, but before the code was long but working now, short but not works with the first panel even that there is no errors.So maybe a loop?
So the html goes like this:
<div class="container">
<div class="down sound">
<img id="batman" class="image-1-panel active" src="flash.svg">
<img class="image-2-panel notactive" src="http://cdn.playbuzz.com/cdn/3c096341-2a6c-4ae6-bb76-3973445cfbcf/6b938520-4962-403a-9ce3-7bf298918cad.jpg">
<p class="image-3-panel notactive">Bane</p>
<p>Joker</p>
<p>Alfred</p>
</div>
And then it repeats for 3 more containers like that one. On css for the active and not active i have:
.notactive{
visibility: hidden;
position: relative;
}
.active{
position: absolute;
}
And in js:
document.querySelector('#batman').addEventListener('click', batman);
function batman(){
document.querySelector('.image-1-panel').classList.toggle('.notactive');
document.querySelector('.image-1-panel').classList.toggle('.active');
document.querySelector('.image-2-panel').classList.toggle('.active');
document.querySelector('.image-2-panel').classList.toggle('.notactive');
}
But nothing happens... I also have a for loop for sounds that is working still good, but the panels don´t move. Can someone share some light here? I thought in a loop and try it following the function but also didn´t work so clearly I am missing something, maybe wrong element I am trying to catch?
Above i have my question without the edit where you can understand what i mean.
Thanks for your help
I have 4 panels. In each panel, there will be 3 images/text one beyond the other. So it will work that when I press panel 1, panel 2,3 and 4 will turn to show an image. Press panel 2, and panel 1,3 and 4 will turn to show different image then when pressed the panel one. And so one for the rest of the panels.
So i start and i create a function for the first panel. It works, but there is any way i can make it simple? Here is the code:
function guessWho(){
document.querySelector('.image-1-panel').classList.toggle('notactive');
document.querySelector('.image-1-panel').classList.toggle('active');
document.querySelector('.image-2-panel').classList.toggle('active');
document.querySelector('.image-2-panel').classList.toggle('notactive');
document.querySelector('.image-3-panel').classList.toggle('active');
document.querySelector('.image-3-panel').classList.toggle('notactive');
document.querySelector('.image-4-panel').classList.toggle('active');
document.querySelector('.image-4-panel').classList.toggle('notactive1');
document.querySelector('.image-5-panel').classList.toggle('active');
document.querySelector('.image-5-panel').classList.toggle('notactive');
document.querySelector('.image-6-panel').classList.toggle('active');
document.querySelector('.image-6-panel').classList.toggle('notactive2');
document.querySelector('.image-7-panel').classList.toggle('active');
document.querySelector('.image-7-panel').classList.toggle('notactive');
document.querySelector('.image-8-panel').classList.toggle('active');
document.querySelector('.image-8-panel').classList.toggle('notactive3');
}
Any way I can put this simple? I don´t want to use any framework, so it has to be pure js. Thank you
Use a loop.
for (var i = 0; i <= 8; i++) {
document.querySelector(`.image-${i}-panel`).classList.toggle('notactive');
document.querySelector(`.image-${i}-panel`).classList.toggle('active');
}
Given that querySelector takes a string, you could build that string up inside of a loop and use the iterator inside of the string. Something like
for(i = 1; i <= 8; i++) {
document.querySelector('.image-' + i + '-panel').classList.toggle('notactive');
}
would toggle the 'notactive' class for everything that has image-x-panel.
Solution 1
for (var i = 1; i <= 8; i++) {
document.querySelector(`.image-${i}-panel`).classList.toggle('notactive');
document.querySelector(`.image-${i}-panel`).classList.toggle('active');
}
Solution 2
Add some CSS class to your HTML like CLASSNAME
And use try this
document.querySelector(`CLASSNAME`).classList.toggle('notactive');
document.querySelector(`CLASSNAME`).classList.toggle('active');
You can create a list of the new states you want your classes to be modified by. In this case, you have an object that looks like this:
{a:false,i:""}
where a is whether it's active, and i is your post class increment, like "notactive2".
Then you create a nested loop where you iterate over each number twice, and check the position in your list of states to determine what things will be added to your base "active" class.
var activeClassesList = [{a:false,i:""},{a:true,i:""},{a:true,i:""},{a:false,i:""},{a:true,i:""},{a:false,i:""},{a:true,i:""},{a:false,i:"1"},{a:true,i:""},{a:false,i:""},{a:true,i:""},{a:false,i:"2"},{a:true,i:""},{a:false,i:""},{a:true,i:""},{a:false,i:"3"}];
var imageClassPrefix = ".image-";
var imageClassPostfix = "-panel";
var start =1;
var base = 8;
for (var i=0;i<2;i++)
{
var count = 1;
for (var j=start;j<(start+base);j++) {
var modifier = activeClassesList[j-1];
var cls = (modifier.a ? "" : "not") + "active" + modifier.i;
document.querySelector(imageClassPrefix + String(count) + imageClassPostfix).classList.toggle(cls);
count++;
}
start = start + base;
}
Another approach might be to just focus on the results, so we can remove the first selectors for each image panel since they are immediately being overwritten.
That leaves the second queries for each selector. This can be simplified by combining multiple selectors inside a queryselectorAll for items with the same class being toggled.
function guessWho(){
document.querySelector('.image-1-panel').classList.toggle('active');
document.querySelector('.image-2-panel').classList.toggle('notactive');
document.querySelectorAll('.image-3-panel, .image-5-panel, .image-7-panel, .image-7-panel').forEach((f)=>{f.classList.toggle('notactive');})
document.querySelector('.image-4-panel').classList.toggle('notactive1');
document.querySelector('.image-6-panel').classList.toggle('notactive2');
}
guessWho();
.allpanels > div {display: inline-block;border:solid 1px black;height:50px;width:50px;margin:8px;}
.active { background-color:red;}
.notactive { background-color:lightsteelblue;}
.notactive1 { background-color:green;}
.notactive2 { background-color:orange;}
<div class="allpanels">
<div class="image-1-panel">1</div>
<div class="image-2-panel">2</div>
<div class="image-3-panel">3</div>
<div class="image-4-panel">4</div>
<div class="image-5-panel">5</div>
<div class="image-6-panel">6</div>
<div class="image-7-panel">7</div>
</div>

Getting html code from another action and manipulate the code throught javascript

I have a cshtml view file with the following code:
<ul id="ul1">
<li id="ul1li1"></li>
<li id="ul1li2"></li>
<li id="ul1li3"></li>
</ul>
And an action "x" which returns me html code with which is several divs one after the other.
something like :
<div class="xdiv"></div>
<div class="xdiv"></div>
<div class="xdiv"></div>
<div class="xdiv"></div>
<div class="xdiv"></div>
Id like to use javascript function in order to put the divs I get from the action "X", into the lis in a circular ascending order.
I couldn't find the right solution, and so far my javascript function is something like this :
function fillLis()
{
var i = 0;
divs = #Url.Action("X");
for(ind in divs){
div = divs[ind];
i = i%3 + 1;
var currentli = document.getElementById('ul1li'+i);
currentli.innterHTML += div.innerHTML;
}
};
Because of some bad html coding, I have to do it that way.
This isn't doing what you think:
divs = #Url.Action("X");
It's just going to resolve to a string (and a syntax error), such as:
divs = /Home/X;
What you want to do is make an AJAX request to that action. (Note: Since you're using ASP.NET, I'm assuming that jQuery is an option.) Something like this:
$.get('#Url.Action("X")', function (data) {
// update your UI
});
In that callback function, data will contain the response from the server. Which, in this case, should be the HTML. At this point it looks like you want to loop over the div elements. So you can probably wrap it in a jQuery object and loop over that. Something like this:
$.get('#Url.Action("X")', function (data) {
var divs = $('div', data);
// at this point "divs" is a list of div HTML elements in the response
});
Your existing code should work on the divs array, perhaps with some minor adjustments through debugging. But essentially that's how you'd get the HTML elements you're looking for from the server in order to use those elements in your client-side code.
Something like that?
Array.prototype.forEach.call(#Url.Action("X"), function(xdiv, i) {
document.getElementById("ul1li"+i).appendChild(xdiv);
});
If not then please update your question with more detailed informations about what you're trying to do.
Some quick notes about your code:
function fillLis()
{
var i = 0;
divs = #Url.Action("X");
for(ind in divs){
div = divs[ind];
// This will always be equal to 1
i = i%3 + 1;
// You say you want to "put the divs [...] into the list", but it seems like you're trying to transfer only the content of your divs
var currentli = document.getElementById('ul1li'+i);
currentli.innterHTML += div.innerHTML;
}
};

Using html <template>: innerHTML.replace

The most popular intro says that I can easily clone html templates within my document.
<template id="mytemplate">
<img src="" alt="great image">
<div class="comment"></div>
</template>
The word "template" however implies that you won't copy-paste it as is, unmodified. Template means that you want to update some variables with specific values. It recommends the following approach for updating the node:
var t = document.querySelector('#mytemplate');
// Populate the src at runtime.
t.content.querySelector('img').src = 'logo.png';
var clone = document.importNode(t.content, true);
document.body.appendChild(clone);
Isn't it perfect? There is querySelector to get the element so that you can update its attributes. I just do not understand why does he updates the template before cloning it. But that is not my question. The real question is that, in mine case, the location of variable to update is unknown. It can be either attribute or innerText in whatever template structure is. This is the most general and frequent use of template, I believe. So, I can ensure that the variable id is unique within the template, like #reply here
<template id="comment-template">
<li class="comment">
<div class="comment-author"></div>
<div class="comment-body"></div>
<div class="comment-actions">
Reply
</div>
</li>
</template>
ought to update the #reply, but author does not explain how to do that. I succeeded to use innerHTML on the original template, document.querySelector('#mytemplate').innerHTML.replace(id, value) but this breaks the template for later use, as explained above. I failed to update the cloned text. This is probably because template.clone produces a document fragment, which has no innerHTML. But, before pushing that forth, I decided to investigate for alternatives since I know that innerHTML/outerHTML is not quite standard.
Alternative for innerHTML? inspects the alternatives to innerHTML but again, they assume too much about the template. Instead of just replacing some specific identifiers with user values, they completely recreate the template, which defeats the whole notion of template. Template looses any sense once you recreate its whole code in the variable valuation. So, how is <template> is supposed to use?
Here is mine solution, the valuate function
<div id="div1">env: </div>
<template id="template1">
var1=var2
</template>
<script language="javascript">
function valuate(template, targetParent, dict) {
var t = document.querySelector('#' + template);
var clone = t.cloneNode(true)
for (key in dict) {
clone.innerHTML = clone.innerHTML.replace(key, dict[key])
}
var fragment = document.importNode(clone.content, true)
var canvas = document.querySelector('#' + targetParent);
canvas.appendChild(fragment);
//alert(canvas.innerHTML)
}
valuate("template1", "div1", {var1:"1+1", var2:2, "#ref":"abc.net"})
</script>
It takes the template, dictionary and target element. Unfortunately, it fails for SVG hierarchies. Don't you know why?
Using again <template>.querySelectorAll to select the element and setAttribute to change the href value
var a = <template>.querySelectorAll("a[href='#reply']");
a[0].setAttribute("href", <url>)
This is a more generic function. It changes an attribute of the selected elements within a cloned template, of course this is very basic,
//object = the cloned template.
//selector = the selector argument which selects all nodes.
//attribute = the attribute to change.
//value = the value that needs to be set to the attribute.
function changeTemplateValues(object, selector, attribute, value)
{
elements = object.querySelectorAll(selector);
if (elements.length == 0)
{
return false; //stop executing. No elements were found.
}
else if (!attribute && !value)
{
return elements; //no attributes and values are set, return nodelist;
}
else
{
if (attribute)
{
//loop over all selected elements to change them.
for (var i = 0; i < elements.length; ++i)
{
if (attribute.match(/innerHTML|textContent|text|nodeValue/g) )
{
elements[i][attribute] = value;
}
else
{
elements[i].setAttribute(attribute, value);
}
}
}
else
{
//No attribute return false;
return false;
}
}
}

jQuery, selecting just number from id like "article-23"

All right, I have a div tag which got a class="blog-post" and id like id="article-23" (where "23" could be any number, as it is id of blog post in a database). I need to somehow get just a number from that id and than apply some rules to that div tag. So say:
if number from id % 2 == 0 {
set text colour black to associated div tag with class of blog-post
} else {
set text colour white to associated div tag with class of blog-post
}
Thats just a "pseudo" code to show logic that I wan't to apply dependent if number from id is even or odd, but the question remains same, how do I just get number from id like "article-23" ?
As simple as
var number = "article-23".match(/\d+/)[0];
But you have to be sure that any digit exists in the string, otherwise you'd get a error.
You can actually apply rules via function, which makes this the cleanest solution (in my opinion):
$(".blog-post").css('color', function () {
return +this.id.replace('article-', '') % 2 ? 'blue' : 'red';
});
http://jsfiddle.net/ExplosionPIlls/Jrc5u/
Try this:
$('.blog-post[id^="article-"]').each(function () {
if (parseInt(this.id.replace('article-', '')) % 2 === 0) {
$('#' + this.id).css('color', 'black');
} else {
$('#' + this.id).css('color', 'white');
}
});
jsFiddle Demo
As an alternative, HTML5 supports these things called "data attributes", which are specifically meant for attaching data to your DOM without abusing things like the "class" or "id" attributes. jQuery provides a handy .data method for reading these attributes in a more obvious way.
You can add your own numeric ID attribute using something like "data-id":
<div class="blog-post" data-id="23" />
$("#blog-post").each(function () {
console.log($(this).data("id")); // Look up the data-id attribute
});
If I'm understanding correctly, you want the number after the hyphen of the id tag of your .blog-post class.
var article = $(".blog-post").attr('id'); //get the id
var article = article.split("-"); // split it on hyphens
return article = article[article.length-1]; // return the last element

IE javascript error - possibly related to setAttribute?

I am using Safalra's javascript to create a collapsible list. The script works across several browsers with no problem. However, when I apply the javascript to my own list, it fails to act as expected when I use IE (I'm using 7 at the moment). It simply writes the list, without the expand and contract images.
I copied the Safalra's javascript precisely, so I assume the error must be in my own list. This is how I generated my list:
<body onLoad="makeCollapsible(document.getElementById('libguides'));">
<ul id="libguides">
<script type="text/javascript" src="http://api.libguides.com/api_subjects.php?iid=54&more=false&format=js&guides=true&break=li"></script>
</ul>
(Yes, I do close the body tag eventually.) When I run this in IE, it tells me that line 48 is causing the problem, which appears to be:
node.onclick=createToggleFunction(node,list);
Here's the entire function:
function makeCollapsible(listElement){
// removed list item bullets and the sapce they occupy
listElement.style.listStyle='none';
listElement.style.marginLeft='0';
listElement.style.paddingLeft='0';
// loop over all child elements of the list
var child=listElement.firstChild;
while (child!=null){
// only process li elements (and not text elements)
if (child.nodeType==1){
// build a list of child ol and ul elements and hide them
var list=new Array();
var grandchild=child.firstChild;
while (grandchild!=null){
if (grandchild.tagName=='OL' || grandchild.tagName=='UL'){
grandchild.style.display='none';
list.push(grandchild);
}
grandchild=grandchild.nextSibling;
}
// add toggle buttons
var node=document.createElement('img');
node.setAttribute('src',CLOSED_IMAGE);
node.setAttribute('class','collapsibleClosed');
node.onclick=createToggleFunction(node,list);
child.insertBefore(node,child.firstChild);
}
I confess I'm too much of a javascript novice to understand why that particular line of code is causing the error. I looked at some of the other questions here, and was wondering if it might be a problem with setAttribute?
Thanks in advance.
Edited to add:
Here's the code for the createToggleFunction function. The whole of the script is just these two functions (plus declaring variables for the images).
function createToggleFunction(toggleElement,sublistElements){
return function(){
// toggle status of toggle gadget
if (toggleElement.getAttribute('class')=='collapsibleClosed'){
toggleElement.setAttribute('class','collapsibleOpen');
toggleElement.setAttribute('src',OPEN_IMAGE);
}else{
toggleElement.setAttribute('class','collapsibleClosed');
toggleElement.setAttribute('src',CLOSED_IMAGE);
}
// toggle display of sublists
for (var i=0;i<sublistElements.length;i++){
sublistElements[i].style.display=
(sublistElements[i].style.display=='block')?'none':'block';
}
}
}
Edited to add (again):
Per David's suggestion, I changed all instances of setAttribute & getAttribute...but clearly I did something wrong. IE is breaking at the 1st line (which is simply the doctype declaration) and at line 49, which is the same line of code where it was breaking before:
node.onclick=createToggleFunction(node,list);
Here's the first function as written now:
function makeCollapsible(listElement){
// removed list item bullets and the sapce they occupy
listElement.style.listStyle='none';
listElement.style.marginLeft='0';
listElement.style.paddingLeft='0';
// loop over all child elements of the list
var child=listElement.firstChild;
while (child!=null){
// only process li elements (and not text elements)
if (child.nodeType==1){
// build a list of child ol and ul elements and hide them
var list=new Array();
var grandchild=child.firstChild;
while (grandchild!=null){
if (grandchild.tagName=='OL' || grandchild.tagName=='UL'){
grandchild.style.display='none';
list.push(grandchild);
}
grandchild=grandchild.nextSibling;
}
// add toggle buttons
var node=document.createElement('img');
node.src = CLOSED_IMAGE;
node.className = 'collapsibleClosed';
node.onclick=createToggleFunction(node,list);
child.insertBefore(node,child.firstChild);
}
child=child.nextSibling;
}
}
And here's the second function:
function createToggleFunction(toggleElement,sublistElements){
return function(){
// toggle status of toggle gadget
// Use foo.className = 'bar'; instead of foo.setAttribute('class', 'bar');
if (toggleElement.className == 'collapsibleClosed') {
toggleElement.className = 'collapsibleOpen';
toggleElement.src = OPEN_IMAGE;
} else {
toggleElement.className = 'collapsibleClosed';
toggleElement.src = CLOSED_IMAGE;
}
// toggle display of sublists
for (var i=0;i<sublistElements.length;i++){
sublistElements[i].style.display=
(sublistElements[i].style.display=='block')?'none':'block';
}
}
}
Internet Explorer (until version 8, and then only in best standards mode) has a very broken implementation of setAttribute and getAttribute.
It effectively looks something like this:
function setAttribute(attribute, value) {
this[attribute] = value;
function getAttribute(attribute, value) {
return this[attribute];
}
This works fine iif the attribute name matches the property name, and the property takes a string value.
This isn't the case for the class attribute, where the matching property is className.
Use foo.className = 'bar'; instead of foo.setAttribute('class', 'bar');
node.onclick=createToggleFunction(node,list);
That is probably not what you want. Does createToggleFunction return a function? If it doesn't, then I bet you meant this:
node.onClick = function() { createToggleFunction(node, list); };
If my guess is right then the way you have it will set the onClick event handler to be the result of createToggleFunction, not a function like it needs to be.

Categories

Resources