大家好,不知道大家听说过 Vanilla JavaScript 这款框架吗?最近我在浏览国外的一些技术网站时,这个词出现的频率实在是太高了,好多框架都宣称自己是基于 Vanilla JavaScript。那到底什么是 Vanilla JavaScript 呢?

vanilla-js.com 官方网站是这样介绍的:Vanilla JS is a fast, lightweight, cross-platform framework for building incredible, powerful JavaScript applications.

Vanilla JS 是一个快速、轻量级、跨平台的JavaScript框架。我们可以用它构建强大的JavaScript应用程序。

大家是不是觉得很这个框架很强大呢,哈哈,不和大家卖关子了,Vanilla JavaScript 就是原生JavaScript。现在做前端项目,大家是不是都在用vue、react这样的框架呢,遇到一些复杂的功能和效果,就是想寻找是否有相关的插件呢,很少想到手工实现呢?大家是否想过这些问题,如果没有这些前端框架,我们是否还能顺利完成项目呢?

本篇文章,我将和大家一起使用原生 JavaScript 创建一个简单的天气查询应用。

一、看看应用长啥样

这是一款界面十分简洁大气的天气查询应用,大概的需求是这样的:

  • 在输入框里输入城市的英文名称,就会很直观的展示相关城市的天气信息,

  • 如果输入的城市信息已经查询过了,则会有信息提示已经查询过此城市信息。

  • 如果输入的城市信息不正确或者没找到匹配的城市,应用则会提示未查询到相关信息。

  • 查询过的城市信息都会以列表的形式在这里展示。

大概就是这些简单的需求,具体界面长啥样,如下图所示:

交互效果,请看下段视频展示:

是不是很漂亮呢,那还不赶紧和我一起动手完成这个应用。

二、本示例将会用到的知识点

  • flexbox 及 grid(网格)布局

  • 媒介查询,完成响应式的支持

  • 使用 fetch api 完成 AJAX 请求

  • 常用的JS对DOM的操作

  • ES6一些常用的新语法

三、 项目前的准备工作

1、申请天气查询API首先我们需要寻找一个天气查询的API,方便我们集成。这样的API市面上比较多,比如阿里云市场就可以申请,不过好像是收费的,调用起来还需要后端配合,为了让大家快速上手,我推荐大家去国外 https://openweathermap.org/ 这个网站申请一个免费的API,之所以用这个,调用方便,通过URL地址传参就能进行调用,虽然高级功能需要付费,但是做个简单的天气查询应用,免费功能已经够用。

2、下载天气图标这个项目中,我们需要用天气图标直观的展示天气情况,这里我建议用SVG格式的图标,主要原因是矢量的形式,不失真,还有一个原因就是我们能根据自己的需要很方便的改变颜色。

下图是我在网络上找到的图标,喜欢的可以去这里下载:https://pan.baidu.com/s/1XS5Ua5c5SgUPiTqK_iXw7w 密码:041m

四、创建HTML结构

基本工作准备完后,我们就开始动手实践吧!

我们先定义两个<p>区域,第一个 p 区域,包含了应用名称、一个表单和一个提示信息文本。提示信息文本默认是没内容的,只有在特定的条件下才能显示,比如城市名称不正确或者重复输入已查询过的城市信息。

第二个 p 区域用来展示已查询过的城市列表,默认的情况,这个区域是没有查询信息的,只有输入城市信息,成功调用天气API接口时,才能显示相关信息。

初始化的 HTML 结构如下:

<p class="top-banner"><div class="container"><h1 class="heading">Simple Weather App</h1><form><input type="text" placeholder="Search for a city" autofocus><button type="submit">SUBMIT</button><span class="msg"></span></form></div>
</p>
<p class="ajax-p"><div class="container"><ul class="cities"></ul></div>
</p>

autofocus 页面初始化时,输入焦点默认聚焦输入表单

你会注意到第二个 p 区域里,没有城市列表信息,这部分的结构,是通过JS代码动态生成的,基本结构如下:

<li class="city"><h2 class="city-name" data-name="..."><span>...</span><sup>...</sup></h2><span class="city-temp">...<sup>°C</sup></span><figure><img class="city-icon" src="..." alt="..."><figcaption>...</figcaption></figure>
</li>

<sup> 用来展示上角标文本。

五、添加基础的样式

创建完基本的结构后,我们需要用 CSS 进行美化,如下代码所示我们定义了全局的颜色自定义变量,以及一些基础的样式外观,示例代码如下:

:root {--bg_main: #0a1f44;--text_light: #fff;--text_med: #53627c;--text_dark: #1e2432;--red: #ff1e42;--darkred: #c3112d;--orange: #ff8c00;
}* {margin: 0;padding: 0;box-sizing: border-box;font-weight: normal;
}button {cursor: pointer;
}input {-webkit-appearance: none;
}button,
input {border: none;background: none;outline: none;color: inherit;
}img {display: block;max-width: 100%;height: auto;
}ul {list-style: none;
}body {font: 1rem/1.3 "Roboto", sans-serif;background: var(--bg_main);color: var(--text_dark);padding: 50px;
}

六、添加主要的个性化样式

基础样式完成后,我们需要为两个 Section 区域添加样式

1、Section #1 Styles

首先我们需要完善下 Section 区域一的样式,当前屏幕大于 >700px,界面如下图所示:

当前屏幕小与等于700px时,应用名称、输入框、按钮各占一行,界面如下图所示:

完后的样式代码如下所示:

.top-banner {color: var(--text_light);
}.heading {font-weight: bold;font-size: 4rem;letter-spacing: 0.02em;padding: 0 0 30px 0;
}.top-banner form {position: relative;display: flex;align-items: center;
}.top-banner form input {font-size: 2rem;height: 40px;padding: 5px 5px 10px;border-bottom: 1px solid;
}.top-banner form input::placeholder {color: currentColor;
}.top-banner form button {font-size: 1rem;font-weight: bold;letter-spacing: 0.1em;padding: 15px 20px;margin-left: 15px;border-radius: 5px;background: var(--red);transition: background 0.3s ease-in-out;
}.top-banner form button:hover {background: var(--darkred);
}.top-banner form .msg {position: absolute;bottom: -40px;left: 0;max-width: 450px;min-height: 40px;
}@media screen and (max-width: 700px) {.top-banner form {flex-direction: column;}.top-banner form input,.top-banner form button {width: 100%;}.top-banner form button {margin: 20px 0 0 0;}.top-banner form .msg {position: static;max-width: none;min-height: 0;margin-top: 10px;}
}

2、Section #2 Styles

这部分区域,我们将用到网格布局进行展示城市天气信息列表,当然这部分区域也是要支持响应式的。

如果当前屏幕大于1000px,我们一行将展示4个城市信息,如下图所示:

当屏幕在 (>700px and ≤1000px) 时,显示三列;当屏幕 (>500px and ≤700px) 时;显示两列;当屏幕 (≤500px) 时,则显示一列。

以下是基于媒介属性的网格布局:

.ajax-p {margin: 50px 0 20px;
}.ajax-p .cities {display: grid;grid-gap: 32px 20px;grid-template-columns: repeat(4, 1fr);
}@media screen and (max-width: 1000px) { .ajax-p .cities {grid-template-columns: repeat(3, 1fr);}
}@media screen and (max-width: 700px) {.ajax-p .cities {grid-template-columns: repeat(2, 1fr);}
}@media screen and (max-width: 500px) {  .ajax-p .cities {grid-template-columns: repeat(1, 1fr);}
}

为了让每个城市信息的效果更加生动,类似个卡片,我们可以使用 ::after 伪元素,利用 bottom 属性添加一个背景阴影的效果。

在这个卡片上,当接口请求成功时,我们需要展示当前城市的名称、所属国家、温度及具体的天气,天气通过图标和文字结合的形式进行展示,如下所示:

.ajax-p .city {position: relative;padding: 40px 10%;border-radius: 20px;background: var(--text_light);color: var(--text_med);
}.ajax-p .city::after {content: ’’;width: 90%;height: 50px;position: absolute;bottom: -12px;left: 5%;z-index: -1;opacity: 0.3;border-radius: 20px;background: var(--text_light);
}.ajax-p figcaption {margin-top: 10px;text-transform: uppercase;letter-spacing: 0.05em;
}.ajax-p .city-temp {font-size: 5rem;font-weight: bold;margin-top: 10px;color: var(--text_dark);
}.ajax-p .city sup {font-size: 0.5em;
}.ajax-p .city-name sup {padding: 0.2em 0.6em;border-radius: 30px;color: var(--text_light);background: var(--orange);
}.ajax-p .city-icon {margin-top: 10px;width: 100px;height: 100px;
}

七、添加 JavaScript

通过以上的操作我们把应用的样式弄完了,接下来我们开始完成核心的脚本代码。

1、当按钮提交时

当用户点击按钮或者按回车键时,我们的程序应该这么做:

  • 阻止提交按钮的默认行为,防止刷新页面。

  • 获取输入框输入的城市信息。

代码的基础结构如下所示:

const form = document.querySelector(".top-banner form");form.addEventListener("submit", e => {e.preventDefault();const inputVal = input.value;
});

接下来我们来处理,如何展示城市列表的数据信息。

2、执行 AJAX 请求

假设第一次进入页面,城市列别还没相关信息,这种情况我们只需要发送 OpenWeatherMap 的 API 请求即可,遵循 API 文档,我们只需要传递申请的 API 的 key,城市名称即可,如下段代码所示:

const apiKey = "YOUR_OWN_KEY";
const inputVal = input.value;...const url = `https://api.openweathermap.org/data/2.5/weather?q=${inputVal}&appid=${apiKey}&units=metric`;

基于文档说明,我们通过JS自带的 fetch() 请求方法,处理AJAX请求,具体的示例代码如下:

...fetch(url).then(response => response.json()).then(data => {// do stuff with the data}).catch(() => {msg.textContent = "Please search for a valid city ";});

下图为我们请求过来的数据格式:

3、编写单个城市卡片组件

数据请求成功后,我们就需要处理数据,展示城市的天气信息,填充到城市列表展示区域,相关代码如下所示:

const { main, name, sys, weather } = data;
const icon = `https://openweathermap.org/img/wn/${weather[0]["icon"]
}@2x.png`;const li = document.createElement("li");
li.classList.add("city");
const markup = `<h2 class="city-name" data-name="${name},${sys.country}"><span>${name}</span><sup>${sys.country}</sup></h2><div class="city-temp">${Math.round(main.temp)}<sup>°C</sup></div><figure><img class="city-icon" src=${icon} alt=${weather[0]["main"]}><figcaption>${weather[0]["description"]}</figcaption></figure>
`;
li.innerHTML = markup;
list.appendChild(li);

这段代码我们两点需要说明下:

  • 在这里 icon 图标变量,我们用的是 API 接口提供的,在后面的代码中我们需要替换成我们个性化的图标

  • 在 .city-name 元素里面,我们添加 data-name 自定义属性,其值包含了 cityName,countryCode,主要方便我们处理重复请求已搜索过的城市,稍后的代码会处理。

4、重置表单输入接口请求完后,我们需要将表单输入框置空,提示信息置空,输入焦点重新聚焦到输入框。示例代码如下:

msg.textContent = "";
form.reset();
input.focus();

5、替换成自己的个性化图标

如下图所示,以下接口自带的几种图片我们需要一一对应成我们自己个性化的图标,名称也保持一致,放到我们的图片文件夹即可:

对应代码需要做相应的修改,如下所示:

//BEFORE
const icon = `https://openweathermap.org/img/wn/${weather[0]["icon"]
}@2x.png`;//AFTER
const icon = `images/${weather[0]["icon"]
}.svg`;

6、阻止相同城市请求

为了防止多次提交同一个城市的信息 ,我们需要进行去重,要不就会发生如下的效果,并不是我们期望的:

这是个糟糕的用户体验,除此之外,还需要处理一个情况,如果一个城市,比如 Athens,在希腊是雅典,在美国为雅典-克拉克县,这种情况不能认为是重复的请求,我们支持用逗号分隔输入,前面城市后面国家简写。

基于以上的去重需求,刚才前面提及到的自定义 data-name 就派上用场了,完后的代码如下所示:

...//1
const listItems = list.querySelectorAll(".ajax-p .city");
const listItemsArray = Array.from(listItems);if (listItemsArray.length > 0) {//2const filteredArray = listItemsArray.filter(el => {let content = "";//athens,grif (inputVal.includes(",")) {//athens,grrrrrr->invalid country code, so we keep only the first part of inputValif (inputVal.split(",")[1].length > 2) {inputVal = inputVal.split(",")[0];content = el.querySelector(".city-name span").textContent.toLowerCase();} else {content = el.querySelector(".city-name").dataset.name.toLowerCase();}} else {//athenscontent = el.querySelector(".city-name span").textContent.toLowerCase();}return content == inputVal.toLowerCase();});//3if (filteredArray.length > 0) {msg.textContent = `You already know the weather for ${filteredArray[0].querySelector(".city-name span").textContent} ...otherwise be more specific by providing the country code as well `;form.reset();input.focus();return;}
}

接下来,我来解释下上述代码的一些关键点:

  • 首先我们需要判断城市展示列表是否为空,如果为空,我们直接进行AJAX请求,如果不为空,我们将触发检查逻辑。

  • 接下来,我们需要判断用不是否输入了逗号分隔用于城市+国家的形式进行精准搜索,通过 data-name 属性进行判断是否有重复的城市。

  • 如果检查到有重复的城市,代码逻辑就不进行AJAX请求,系统将会提示用户已经查询过此城市信息,并重置表单输入内容为空。

特殊逻辑说明:

Note #1: 如果你通过逗号的形式精确搜索时,如果国家简写不正确的化(两个字母简写,比如 athens,aa),API接口不会返回任何信息。如果你输多于三个字母的国家简写,而且没有意义(比如 athens,aaaa),API接口 则会不考虑逗号的部分,则按照城市的信息默认搜索,比如直接返回希腊的雅典。

Note #2: 如果一个城市属于多个国家,没有进行逗号精准搜索的话,API 接口也不会把所有相关国家的城市都罗列出来,只会显示一个城市而已。

7、最后贴上完整的 JS 代码

'use strict';
const form=document.querySelector(".top-banner form");
const input=document.querySelector(".top-banner input");
const msg=document.querySelector(".top-banner .msg");
const list=document.querySelector(".ajax-p .cities");
const apiKey="YOUR_OWN_KEY(你申请的APIKEY)";form.addEventListener("submit",e=>{e.preventDefault();let inputVal=input.value;const  listItems=list.querySelectorAll(".ajax-p .city");const listItemsArray=Array.from(listItems);if(listItems.length>0){const filteredArray=listItemsArray.filter(el=>{let content="";if(inputVal.includes(",")){if(inputVal.split(",")[1].length>2){inputVal=inputVal.split(",")[0];content=el.querySelector(".city-name span").textContent.toLocaleLowerCase();}else{content=el.querySelector(".city-name").dataset.name.toLowerCase();}} else{content=el.querySelector(".city-name span").textContent.toLowerCase();}return content===inputVal.toLowerCase();});if(filteredArray.length>0){msg.textContent=`Your already know the weather for${filteredArray[0].querySelector(".city-name span").textContent}... otherwise be more specific by providing the country code as well`;form.reset();input.focus();return;}}//ajax hereconst url=`https://api.openweathermap.org/data/2.5/weather?q=${inputVal}&appid=${apiKey}&units=metric`;fetch(url).then(response=>response.json()).then(data=>{const { main,name,sys,weather } =data;const icon=`images/${weather[0]["icon"]}.svg`;const li=document.createElement("li");li.classList.add("city");const markup=`<h2 class="city-name" data-name="${name},${sys.country}"><span>${name}</span><sup>${sys.country}</sup></h2><div class="city-temp">${Math.round(main.temp)}<sup>°C</sup></div><figure><img class="city-icon" src="${icon}" alt="${weather[0]["description"]}"<figcaption>${weather[0]["description"]}</figcaption></figure>`;li.innerHTML=markup;list.appendChild(li);}).catch(()=>{msg.textContent="please search for a valid city ";});msg.textContent="";form.reset();input.focus();
});

小节

到这里我们的代码终于完成了,是不是很长,希望你能看下去,建议你还是亲手动手实践一遍享受下代码实践的成就感,这个应用还有许多地方改需要改进,比如ajax的等待请求提示,输入格式的验证等等,有兴趣的可以自己尝试下。本示例大家可以点击 阅读原文 进行在线体验。

写完这篇原创文章已是凌晨12点多,从实践到写文章花了将近12个小时以上,如果你喜欢我的分享,麻烦给个关注、点赞加转发哦,你的支持,就是我分享的动力,后续会持续分享实践案例,欢迎持续关注。

延伸阅读

动手练一练,使用 Flexbox 创建一个响应式的表单

动手练一练,用纯 CSS 制作一款侧滑显示留言面板的网页组件

使用 CSS Checkbox Hack 技术纯手工撸一个手风琴组件

专注分享当下最实用的前端技术。关注前端达人,与达人一起学习进步!

长按关注"前端达人"

使用 Vanilla JavaScript 框架创建一个简单的天气应用相关推荐

  1. 用SVG和Vanilla JS框架创建一个“星形变心形”的动画效果

    在我写的这篇文章中, 讲述了如何用Vanilla JavaScript使动画顺滑的从一种状态过渡到另一种.最好先看下那篇文章,因为在这篇文章中我们要用到一些那篇文章中讲过的内容.例如例子的演示.各种时 ...

  2. html+js画一颗心形,用SVG和Vanilla JS框架创建一个“星形变心形”的动画效果

    在我写的这篇文章中, 讲述了如何用Vanilla JavaScript使动画顺滑的从一种状态过渡到另一种.最好先看下那篇文章,因为在这篇文章中我们要用到一些那篇文章中讲过的内容.例如例子的演示.各种时 ...

  3. 如何利用laragon框架制作一个简单的应用?

    如何利用laragon框架制作一个简单的应用? 一.搭建环境 1. 安装Laragon 1.1 打开安装包用的语言 选择自己习惯用的语言 1.2 选择安装地址 1.3 选择Next,开始install ...

  4. 利用thinkphp创建一个简单的站点

    本文我们将利用thinkphp创建一个简单的站点,这里所使用的thinkphp版本是5.0.24,这里是它的中文文档.如果有需要可以参考它的中文文档. thinkphp框架是一个典型的MVC框架,该框 ...

  5. python写rest服务_Python 如何创建一个简单的REST接口

    问题 你想使用一个简单的REST接口通过网络远程控制或访问你的应用程序,但是你又不想自己去安装一个完整的web框架. 解决方案 构建一个REST风格的接口最简单的方法是创建一个基于WSGI标准(PEP ...

  6. blockchain 区块链200行代码:在JavaScript实现的一个简单的例子

    blockchain 区块链200行代码:在JavaScript实现的一个简单的例子 了解blockchain的概念很简单(区块链,交易链块):它是分布式的(即不是放置在同一台机器上,不同的网络设备上 ...

  7. visjs使用小记-1.创建一个简单的网络拓扑图

    1.插件官网:http://visjs.org/  2.创建一个简单的网络拓扑图 <!doctype html> <html> <head><title> ...

  8. 创建一个简单的后台教务管理系统,包含动态增删改查学生,年级和课程信息及查看登录日志等功能,包含表单验证.

    创建一个简单的后台教务管理系统,包含动态增删改查学生,年级和课程信息及查看登录日志等功能,包含表单验证. 一.显示页面 新建一个登录页面,即login.jsp <%--Created by In ...

  9. 基于JavaScript+css写一个简单的h5动态下雨效果

    基于JavaScript+css写一个简单的h5动态下雨效果 文章目录 什么是前端 展示效果 JavaScript是什么? 步骤 1.html 2.css 3.js 什么是前端 前端它是一个工作,它的 ...

最新文章

  1. python基础 练习题
  2. Kotlin的解析(下)
  3. 做网络推广浅析影响网站打开速度的因素,促进网站SEO优化效果提升!
  4. 网络营销外包——网络营销外包专员浅析网站首页设计需要注意哪些
  5. dede 验证码不显示 vdimgck.php,Dede后台验证码不显示解决方法详解(dedecms 5.7)
  6. 多线程三种同步方式(模拟银行取款)
  7. 出现“cannot identify image file /.DS_Store'”问题解决的办法
  8. php正则过滤%3e,实用的替换或者过滤数据正则表达式php代码
  9. Oracle Goldengate 安装配置
  10. PCB封装下载方法(亲测有效)
  11. Unity - Timeline 之 Activation track properties(激活轨道的属性)
  12. STM32F103ZE工程移植到STM32F107VC上软件调试时死循环在while((RCC-CR RCC_CR_PLL2RDY) == 0) { }
  13. 38、程序中的三国天下
  14. http协议中get和post的区别(转)
  15. 如何用MATLAB进行电路仿真
  16. 网络中的iso七层模型
  17. 单片机毕设分享100例(五)
  18. android htc G13刷机初探
  19. 面试汇总-Spring-IOC和AOP
  20. 天梯赛---7-6 集合相似度 (25分)

热门文章

  1. JAVA SE8 OCA/OCP和PL/SQL OCA/OCP认证书籍推荐
  2. DFT分析连续非周期信号注意事项
  3. 业内首档程序员真人秀?这不比博人传热血???
  4. EdgeView 2 for Mac(图像查看软件)
  5. 砸金蛋java代码_java实现砸金蛋抽奖功能
  6. 杜克大学计算机统计学,杜克大学的统计学专业怎么样?
  7. qcom 8953 usb hub device descriptor read/64 error -71
  8. python自动买股票_用python可以做哪些有趣的事--我:选股票
  9. USACO COWXOR
  10. 收购江铃重汽!沃尔沃卡车将在中国生产重型卡车