I'm working on C# mvc project, and I'm trying to load product cart partial view, where I want in each row to have a button Read More for product specification. When users click on Read more it should collapse in div where specification for product are loaded.
I manage to accomplish this by creating css style for div Id - product-intro, but It functioning just on first row.
I'm now trying to dynamically create div Id's and also style inline product-intro"+"-"+i and I cant manage working.
Thanks in advance for any suggestions.
Here is my html:
#{int i = 0;}
#foreach (var line in Model.Lines)
{
var validQuantityClass = line.Quantity <= line.InStock ? "" : "danger";
<tr class="article #validQuantityClass" id="article-#line.Product.ProductId">
<td>
<div class="media">
<h4 class="group inner list-group-item-heading">#line.Product.Name</h4>
#{i++;}
<script>articleSpec()</script>
<div class="media-body">
<div id="#("product-intro"+"-"+i)" class="collapse" style="margin-bottom: 10px; border-bottom:1px solid #dee2e6; padding:10px 0; font-size: 12px;">
<div class="row justify-content-between">
<div class="col">
<span id='#string.Format("article-{0}-spec", line.Product.tecId)'></span>
</div>
</div>
</div>
<button type="button" class="btn btn-default" data-toggle="collapse" data-target="#product-intro-#i">Read More +</button>
</div>
</div>
</td>
</tr>
}
Javascript:
<script>
function articleSpec() {
$("#product-intro").each(function (index) {
var articleId = document.getElementById('tecId').value;
if (!articleId)
return;
var labelSelector = "#article-" + articleId + "-spec";
var url = "/Search/ArticleSpecification";
var postdata = "articleid=" + articleId;
$.post(url,
postdata,
function (data) {
$(labelSelector).html(data);
});
});
}
</script>
And this is my css for product-intro:
.cart-content #product-intro {
margin-bottom: 10px;
border-top: 1px solid #dee2e6;
border-bottom: 1px solid #dee2e6;
padding: 10px 0;
font-size: 12px;
}
I also tried to pass #i in javascript and dynamically load data in div:
function articleSpec(number) {
$("#product-intro" + '-' + number).each(function (index) {
Related
I am trying to change the color of background.
I am changing every odd results to light green(#f0f5f5) so when the result ends in even number,
I get big white space.
I would like to change background color of pagination section to light green when the result ends in even number.
Sear
search results displays only 5 results so it could be 2th and 4th.
search.addWidgets([
instantsearch.widgets.searchBox({
container: '#searchbox',
}),
instantsearch.widgets.hits({
container: '#Algolia_Result',
transformItems: function (items) {
return items.map(function (item) {
if (item.objectType === 'Startup') {
item._isDescription = isNotNull(item.description);
} else if (item.objectType === 'NEWS') {
item._isSource = isNotNull(item.source);
} else if (item.objectType === 'Comment') {
item._isComment = isNotNull(item.comment);
return item;
});
},
templates: {
empty: '<div id="empty">No results have been found for {{ query }}.</div><br>',
item: `
<a href="{{linkUrl}}" target="_blank">
<div class="algolia_container">
<div class="item1">
<div id="images"><img src="{{logoUrl}}" alt="{{hits-image}}" id="hits-image"></div>
<div id="objTyeps"><span class="objectType {{objectCss}}">{{objectType}}</span></div>
</div>
<div class="item2">
<div id="objectTitle">
<span id="titleForDisplay">{{#helpers.highlight}}{ "attribute": "titleForDisplay" }{{/helpers.highlight}}</span>
</div>
</div>
<div class="item3">
{{#_isLocation}}
<div id="location">{{#helpers.highlight}}{ "attribute": "location" }{{/helpers.highlight}}</div>
{{/_isLocation}}
</div>
</div></a>
`,
},
}),
instantsearch.widgets.pagination({
container: '#pagination',
}),
]);
#Algolia_Result > div > div > ol > li:nth-child(odd){background-color: #f0f5f5;}
.ais-Pagination-item {
display:inline;
padding: 5px;
margin: 0 5px;
border: 1px solid #E8E8E8;
border-radius:5px;
font-size:18px;
}
.ais-Pagination-list {
text-align: center;
height:45px;
padding-top: 10px;
}
.ais-Pagination-item:hover {
background-color: #DCDCDC;
transition: background-color .2s;
}
.ais-Pagination-item--selected{
background-color: #E8E8E8;
}
<div id="searchbox"></div>
<div id="results">
<div id="Algolia_Result"></div>
<div id="pagination"></div>
</div>
This is ok
This need be fixed as if the background color of pagination area is the same as the last result, it must be green
This is what I get in the console.
You can color background of the pagination row by using JavaScript to count the number of results and apply color if the number of results is even.
Check out the example below.
Example 1 is with an odd number of result rows and the CSS works fine, same as your working example.
Example 2 is with an even number of result rows and uses the JS code to style the pagination background.
// Count the rows
let numRows = document.querySelectorAll('#example-2 .row').length
// If the number of rows is even
if (numRows % 2 == 0) {
// Apply the background color to the pagination row
document.querySelector('#example-2 .pagination').style.backgroundColor = '#eee'
}
.container {
border: 1px solid #000;
margin-bottom: 5px;
}
.row:nth-child(odd) {
background-color: #eee;
}
Example 1
<div id="example-1" class="container">
<div>
<div class="row">Row 1</div>
<div class="row">Row 2</div>
<div class="row">Row 3</div>
</div>
<div>
<div class="pageination">Pagination Row</div>
</div>
</div>
Example 2
<div id="example-2" class="container">
<div>
<div class="row">Row 1</div>
<div class="row">Row 2</div>
<div>
<div>
<div class="pagination">Pagination Row</div>
</div>
</div>
EDIT: So in your example, you would add the following JavaScript.
<script>
let numRows = document.querySelectorAll('.ais-Hits-item').length
if (numRows % 2 == 0) {
document.querySelector('.ais-Pagination-list').style.backgroundColor = '#eee'
}
</script>
EDIT 2: Looking at your code sandbox I can see that the issue is that the JS that counts the number of rows is being run before the rows have been rendered by Algolia.
To solve this issue we need to place our row counting JS into an Algolia callback that is ran after the rows have been rendered. We can use the algolia search.on('render', ...) event callback.
Try this:
search.on('render', () => {
let numRows = document.querySelectorAll('.algolia_container').length;
if (numRows % 2 === 0) {
document.querySelector('#pagination').style.backgroundColor = 'red';
} else {
document.querySelector('#pagination').style.backgroundColor = 'transparent';
}
});
Is there a more efficient way of creating this function with Javascript.
As you can see in the demo, the first block works fine but not the second block. I plan to roll this out across hundreds of categories, so wonder if there's a neater solution here.
const btn = document.getElementById("category36SeeMore");
btn.addEventListener("click", function(e){
e.preventDefault();
const id = this.id.replace('SeeMore', '')
document.querySelectorAll('.' + id).forEach(el=>el.style.display = 'block')
});
.category36 {
margin: 5px;
padding: .5rem;
box-shadow: 2px 2px 5px rgba(0,0,0,0.03);
border-radius: 4px;
background: Skyblue;
border-bottom: 1px solid #F9F2D6;
border-right: 1px solid #F9F2D6;
}
<p>
See more
</p>
<div class="category36">
test
</div>
<div class="category36" style="display:none;">
test
</div>
<div class="category36" style="display:none;">
test
</div>
<div class="category36" style="display:none;">
test
</div>
<hr>
<p>
See more
</p>
<div class="category37">
test
</div>
<div class="category37" style="display:none;">
test
</div>
<div class="category37" style="display:none;">
test
</div>
<div class="category37" style="display:none;">
test
</div>
Demo: https://jsfiddle.net/qzfesw5d/
Is jQuery a better approach here? We use that on the site already.
You have to target all the elements and loop through them to attach the event. You can either use attribute starts with selector or use a common class to target the elements:
const btnList = document.querySelectorAll("a[id^=category");
btnList.forEach(function(btn){
btn.addEventListener("click", function(e){
e.preventDefault();
const id = this.id.replace('SeeMore', '')
document.querySelectorAll('.' + id).forEach(el=>el.style.display = 'block')
});
});
.category36, .category37 {
margin: 5px;
padding: .5rem;
box-shadow: 2px 2px 5px rgba(0,0,0,0.03);
border-radius: 4px;
background: Skyblue;
border-bottom: 1px solid #F9F2D6;
border-right: 1px solid #F9F2D6;
}
<p>
See more
</p>
<div class="category36">
test
</div>
<div class="category36" style="display:none;">
test
</div>
<div class="category36" style="display:none;">
test
</div>
<div class="category36" style="display:none;">
test
</div>
<hr>
<p>
See more
</p>
<div class="category37">
test
</div>
<div class="category37" style="display:none;">
test
</div>
<div class="category37" style="display:none;">
test
</div>
<div class="category37" style="display:none;">
test
</div>
If you're using plain javascript (like your example) you could apply the following logic:
Give a specific class to all your container like category-container
Instead of selection ONE using the ID, select them ALL using the classname
Loop over them to add the event
Make sure that the event will change the display of the children of the current parent
Adding new categories will not break anything. This should be entirely scalable.
Using jQuery might make things a little simpler. Here is one approach with plain JavaScript.
const categoryCount = 36
for (let i = 0; i < categoryCount; i++) {
const btn = document.getElementById(`category${i+1}SeeMore`)
if (btn) {
btn.addEventListener("click", e => {
e.preventDefault();
document.querySelectorAll(`.category${i+1}`)
.forEach(el => el.style.display = 'block')
})
}
}
You can change all ...SeeMore elements to class and then add data-* attribute to all to point to the collection that you want to show like:
See more
Then loop through each class and on click of any seemore, you can get the id easily using:
const id = this.dataset.id
and then easily show the collections divs like:
document.querySelectorAll('.category' + id).forEach(el=>el.style.display = 'block')
const btns = document.querySelectorAll(".SeeMore");
btns.forEach(function(btn) {
btn.addEventListener("click", function(e) {
e.preventDefault();
const id = this.dataset.id
document.querySelectorAll('.category' + id).forEach(el => el.style.display = 'block')
});
});
.category36, .category37 {
margin: 5px;
padding: .5rem;
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.03);
border-radius: 4px;
background: Skyblue;
border-bottom: 1px solid #F9F2D6;
border-right: 1px solid #F9F2D6;
}
<p>
See more
</p>
<div class="category36">
test
</div>
<div class="category36" style="display:none;">
test
</div>
<div class="category36" style="display:none;">
test
</div>
<div class="category36" style="display:none;">
test
</div>
<hr>
<p>
See more
</p>
<div class="category37">
test
</div>
<div class="category37" style="display:none;">
test
</div>
<div class="category37" style="display:none;">
test
</div>
<div class="category37" style="display:none;">
test
</div>
I am trying to step away from jsTree as this is not as much as configurable as having my own custom code. I am making use of Bootstrap to have a somewhat similar functionality as jsTree. I am also stepping away from jQuery (for now), because of debugging reasons.
//Event delegation
function BindEvent(parent, eventType, ele, func) {
var element = document.querySelector(parent);
element.addEventListener(eventType, function(event) {
var possibleTargets = element.querySelectorAll(ele);
var target = event.target;
for (var i = 0, l = possibleTargets.length; i < l; i++) {
var el = target;
var p = possibleTargets[i];
while (el && el !== element) {
if (el === p) {
return func.call(p, event);
}
el = el.parentNode;
}
}
});
}
//Add content after referenced element
function insertAfter(referenceNode, newNode) {
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
//Custom function
function LoadSubOptions(ele) {
ele = ele.parentElement.parentElement;
let newEle = document.createElement("div");
newEle.classList.add("row", "flex");
//Generated HTML Content (currently hard coded):
newEle.innerHTML = "<div class='col-xs-1'><div class='tree-border'></div></div><div class='col-xs-11'><div class='row'><div class='col-xs-12'><button class='btn btn-default btn-block btn-lg'>Test</button></div></div></div>";
insertAfter(ele, newEle);
}
//Bind method(s) on button click(s)
BindEvent("#tree-replacement", "click", "button", function(e) {
LoadSubOptions(this);
});
#tree-replacement button {
margin-top: 5px;
}
.tree-border {
border-left: 1px dashed #000;
height: 100%;
margin-left: 15px;
}
.flex {
display: flex;
}
/*Probably not wise to use this method on Bootstrap's grid system: */
#tree-replacement .row.flex>[class*='col-'] {
display: flex;
flex-direction: column;
}
<link href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet" />
<div class="container">
<div id="tree-replacement">
<div class="row">
<div class="col-xs-12">
<button class="btn btn-default btn-block btn-lg">
Option 1
</button>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<button class="btn btn-default btn-block btn-lg">
Option 2
</button>
</div>
</div>
<!--The generated html as example: -->
<!--<div class="row">
<div class="col-xs-1">
<div class="tree-border">
</div>
</div>
<div class="col-xs-11">
<div class="row">
<div class="col-xs-12">
<button class="btn btn-default btn-block btn-lg">
Option 2
</button>
</div>
</div>
</div>
</div>-->
</div>
</div>
JSFiddle
I added a border in a .column-*-1 to allow for some spacing for the border:
The spacing however, I find a bit too much. How could I address this problem? I would like to refrain from styling Bootstrap's grid system (meaning I preferably would not want to touch any styling behind .col-* and .row classes etc.) because this might break the responsiveness or anything else related to Bootstrap.
Edit:
I also noticed that when adding a lot of buttons by just clicking them, the layout of tree will start failing as well. (I am aware this is a different question, so if I need to post another question regarding this problem, please do let me know) Is there a way I could address this so that the element works correctly?
Add this little CSS
#tree-replacement .row.flex > .col-xs-11:nth-child(2):before {
content: ' ';
position: absolute;
left: calc(-100% / 11 + 30px);
top: 2em;
border-top: 1px dashed #000000;
width: calc(100% / 5 - 15px);
}
//Event delegation
function BindEvent(parent, eventType, ele, func) {
var element = document.querySelector(parent);
element.addEventListener(eventType, function(event) {
var possibleTargets = element.querySelectorAll(ele);
var target = event.target;
for (var i = 0, l = possibleTargets.length; i < l; i++) {
var el = target;
var p = possibleTargets[i];
while (el && el !== element) {
if (el === p) {
return func.call(p, event);
}
el = el.parentNode;
}
}
});
}
//Add content after referenced element
function insertAfter(referenceNode, newNode) {
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
//Custom function
function LoadSubOptions(ele) {
ele = ele.parentElement.parentElement;
let newEle = document.createElement("div");
newEle.classList.add("row", "flex");
//Generated HTML Content (currently hard coded):
newEle.innerHTML = "<div class='col-xs-1'><div class='tree-border'></div></div><div class='col-xs-11'><div class='row'><div class='col-xs-12'><button class='btn btn-default btn-block btn-lg'>Test</button></div></div></div>";
insertAfter(ele, newEle);
}
//Bind method(s) on button click(s)
BindEvent("#tree-replacement", "click", "button", function(e) {
LoadSubOptions(this);
});
#tree-replacement button {
margin-top: 5px;
}
.tree-border {
border-left: 1px dashed #000;
height: 100%;
margin-left: 15px;
}
.flex {
display: flex;
}
/*Probably not wise to use this method on Bootstrap's grid system: */
#tree-replacement .row.flex>[class*='col-'] {
display: flex;
flex-direction: column;
}
#tree-replacement .row.flex > .col-xs-11:nth-child(2):before {
content: ' ';
position: absolute;
left: calc(-100% / 11 + 30px);
top: 2em;
border-top: 1px dashed #000000;
width: calc(100% / 5 - 15px);
}
<link href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet" />
<div class="container">
<div id="tree-replacement">
<div class="row">
<div class="col-xs-12">
<button class="btn btn-default btn-block btn-lg">
Option 1
</button>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<button class="btn btn-default btn-block btn-lg">
Option 2
</button>
</div>
</div>
<!--The generated html as example: -->
<!--<div class="row">
<div class="col-xs-1">
<div class="tree-border">
</div>
</div>
<div class="col-xs-11">
<div class="row">
<div class="col-xs-12">
<button class="btn btn-default btn-block btn-lg">
Option 2
</button>
</div>
</div>
</div>
</div>-->
</div>
</div>
Here I have used absolute positioning and increased height by 5px which kind of makes it touches the next div element.
Here is the Fiddle Link
and the Code Snippet:
//Event delegation
function BindEvent(parent, eventType, ele, func) {
var element = document.querySelector(parent);
element.addEventListener(eventType, function(event) {
var possibleTargets = element.querySelectorAll(ele);
var target = event.target;
for (var i = 0, l = possibleTargets.length; i < l; i++) {
var el = target;
var p = possibleTargets[i];
while (el && el !== element) {
if (el === p) {
return func.call(p, event);
}
el = el.parentNode;
}
}
});
}
//Add content after referenced element
function insertAfter(referenceNode, newNode) {
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
//Custom function
function LoadSubOptions(ele) {
ele = ele.parentElement.parentElement;
let newEle = document.createElement("div");
newEle.classList.add("row", "flex");
//Generated HTML Content (currently hard coded):
newEle.innerHTML = "<div class='col-xs-1'><div class='tree-border'></div></div><div class='col-xs-11'><div class='row'><div class='col-xs-12'><button class='btn btn-default btn-block btn-lg'>Test</button></div></div></div>";
insertAfter(ele, newEle);
}
//Bind method(s) on button click(s)
BindEvent("#tree-replacement", "click", "button", function(e) {
LoadSubOptions(this);
});
#tree-replacement button {
margin-top: 5px;
}
.tree-border {
border-left: 1px dashed #000;
height: calc(100% + 5px);
margin-left: 20px;
position: absolute;
}
.flex {
position: relative;
display: flex;
}
.col-xs-11 .col-xs-12 {
padding-left: 0;
}
/*Probably not wise to use this method on Bootstrap's grid system: */
#tree-replacement .row.flex>[class*='col-'] {
display: flex;
flex-direction: column;
}
<link href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"/>
<div class="container">
<div id="tree-replacement">
<div class="row">
<div class="col-xs-12">
<button class="btn btn-default btn-block btn-lg">
Option 1
</button>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<button class="btn btn-default btn-block btn-lg">
Option 2
</button>
</div>
</div>
<!--<div class="row">
<div class="col-xs-1">
<div class="tree-border">
</div>
</div>
<div class="col-xs-11">
<div class="row">
<div class="col-xs-12">
<button class="btn btn-default btn-block btn-lg">
Option 2
</button>
</div>
</div>
</div>
</div>-->
</div>
</div>
I have a view with a test link on the left side. Each time user clicks the test link, I am adding a tab button and tab content (straight up HTML5 and CSS). This is what it looks like:
Controller Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MDMS_Web.Controllers
{
public class MainViewController : Controller
{
//
// GET: /MainView/
public ActionResult MainView(string name)
{
ViewBag.Name = name;
return View();
}
//[ChildActionOnly]
//public PartialViewResult MainContentPartial()
//{
// return PartialView("~/Views/MainView/MainContentPartial.cshtml");
//}
public ActionResult GetView()
{
return PartialView("~/Views/MainView/MainContentPartial.cshtml");
}
}
}
Partial View
<div id="MainContentBox" style="margin: 0em 0em;">
<h2>Testing</h2>
</div>
Main View
#{
ViewBag.Title = "Main View";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<main id="mainView">
<div class="row" style="min-width: 100%; ">
<div style="float: left; width: 20%; min-height: 870px; margin-top: 0.5em; margin-left: -1em; overflow: hidden; border-style: solid; border-width: thin; border-color: lightgray; ">
<div id="Test">
<div class="row" style="background-color: #c2cbfb; margin-left: 0; margin-right: 0; ">
<p id="menuTitle" style="width: 100%; text-align: center; margin: 5px 0 5px 0; ">Test</p>
</div>
<div class="row content-wrapper">
<span style="white-space: nowrap;">
<img class="icon" style="width: 30px; height: 30px; " src="Content/images/dashboard/CheckIcon.png" alt="Check icon" />
<a id="TestLink">Test Stuff</a>
</span>
</div>
</div>
</div>
<div style="float: left; width: 80%; min-height: 870px; margin-top: 0.5em; margin-left: 0em; overflow: hidden; ">
<div id="MainContentBox" style="margin: 0em 0em;">
<div id="tabs" class="tab">
</div>
<div id="content">
</div>
</div>
</div>
</div>
<div id="loading">
</div>
</main>
#section scripts{
#Scripts.Render("~/bundles/App/MainView")
<script type="text/javascript">
$(function () { MainView.initModule('#ViewBag.Name') });
</script>
}
JavaScript
function addTab(evt) {
stateMap.tabIndex += 1;
// add tab button
console.log(evt);
var tHtml = '<button id="tb' + stateMap.tabIndex + '" class="tablinks">' + "New Tab " + stateMap.tabIndex + '</button>';
$("#tabs").append(tHtml);
console.log("we have a new tab!");
// add tab content section
var node = document.createElement('div');
node.setAttribute('id', 't' + stateMap.tabIndex);
node.className = "tabContent";
// load partial page place holder
var contentPlaceHolder = document.createElement('div');
contentPlaceHolder.setAttribute('id', 'c' + stateMap.tabIndex);
node.appendChild(contentPlaceHolder);
document.getElementById("content").appendChild(node);
console.log("we have new content placeholder for partial view!");
// HERE IS WHERE MY PROBLEM BEGINS !!!!!!
// NOTHING I DO WILL LOAD MY PARTIAL PAGE !!!!
//#{ Html.RenderPartial("MainContentPartial"); }
//$("#c" + stateMap.tabIndex).load('#{ Html.RenderPartial("MainContentPartial"); }');
//$("#c" + stateMap.tabIndex).load("GetView");
$(function () {
$("#c" + stateMap.tabIndex).load(
'<%= Url.Action("GetView", "MainViewController") %>'
);
})
//url: 'MainViewController/GetView',
//$.ajax({
// url: 'GetView',
// dataType: 'html',
// success: function (data) {
// $("#c" + stateMap.tabIndex).html(data);
// }
//});
}
JavaScript initModule
var initModule = function (data) {
stateMap.currentSection = data;
//Bind events
$("#TestLink").on("click", function (event) {
addTab(event);
});
$(document).ready(function () {
$(".tab").on("click", "button", function (event) {
openTab(event);
});
});
};
return { initModule: initModule };
My issue is with the last part of the JavaScript and probably the Controller. Can someone please tell me the correct way to load the partial view into my dynamically created tab content using JQuery?
You can load Partial View dynamically using Jquery in following way
$(document).on("click", "#TestLink", function () {
var url = "/MainView/GetView";
$.get(url, function (data) {
$("#content").html(data);
});
});
Here URL is the URL of action which will return PartialView.
Writing some js for an html file where i input a sentence (string). and when i click a button, it outputs the amount of each individual vowel, excluding y and not paying attention to punctuation. I cannot use var so i am trying to make this work using let. I believe i'm on the right path here,starting with the vowel a, yet if the sentence doesn't contain an a i get an error. I can't think of what to do next. Any thoughts?
'use strict';
let vButton = document.querySelectorAll('#vowels');
vButton.forEach(function(blip) {
blip.addEventListener('click', function(evt) {
evt.preventDefault();
console.log('click');
let vowelString = document.getElementById('roboInput'),
sentence = vowelString.value;
if (sentence !== '') {
let aMatches = sentence.match(/a/gi).length;
alert("a - " + aMatches);
}
vowelString.value = '';
});
});
a {
cursor: pointer;
}
.well-robot {
min-height: 340px;
}
.input-robot {
width: 100%;
min-height: 100px;
}
.output-robot {
border: 1px solid #000000;
min-height: 150px;
margin-top: 10px;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css">
<div class="container">
<div class="alert alert-info">
Hello! I'm a smart robot. I can do many interesting things. Type something below and click a button to watch me work!
</div>
<div class="row">
<div class="col-sm-4">
<img src="./robot.gif">
</div>
<div class="col-sm-8 well well-robot">
<textarea id="roboInput" placeholder="Input something here!" class="input-robot"></textarea>
<div class="btn-group btn-group-justified">
<a class="btn btn-default" id="vowels">Count Vowels</a>
<a class="btn btn-default" id="anagrams">Count Anagrams</a>
<a class="btn btn-default" id="distance">Word Distance</a>
</div>
<div id="robotResult" class="output-robot">
</div>
</div>
</div>
</div>
When there's no match for the regular expression, .match() returns null, not an empty array, so you can't get the length. You need to check for that.
let matches = sentence.match(/a/gi);
let matchLength = matches ? matches.length : 0;
alert('a - ' + matchLength);
If I understand your question correctly, you may want something like this:
'use strict';
let vButton = document.querySelectorAll('#vowels');
vButton.forEach(function(blip) {
blip.addEventListener('click', function(evt) {
evt.preventDefault();
//console.log('click');
let vowelString = document.getElementById('roboInput'),
sentence = vowelString.value;
if (sentence) {
let result = {a: 0, e: 0, i: 0, o: 0, u: 0 };
for(var i = 0, l = sentence.length; i < l; i++) {
if(result.hasOwnProperty(sentence[i]))
result[sentence[i]]++;
}
console.log(result);
}
vowelString.value = '';
});
});
a {
cursor: pointer;
}
.well-robot {
min-height: 340px;
}
.input-robot {
width: 100%;
min-height: 100px;
}
.output-robot {
border: 1px solid #000000;
min-height: 150px;
margin-top: 10px;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css">
<div class="container">
<div class="alert alert-info">
Hello! I'm a smart robot. I can do many interesting things. Type something below and click a button to watch me work!
</div>
<div class="row">
<div class="col-sm-4">
<img src="./robot.gif">
</div>
<div class="col-sm-8 well well-robot">
<textarea id="roboInput" placeholder="Input something here!" class="input-robot"></textarea>
<div class="btn-group btn-group-justified">
<a class="btn btn-default" id="vowels">Count Vowels</a>
<a class="btn btn-default" id="anagrams">Count Anagrams</a>
<a class="btn btn-default" id="distance">Word Distance</a>
</div>
<div id="robotResult" class="output-robot">
</div>
</div>
</div>
</div>