`

自动下拉框,仿百度

阅读更多

下面给大家分享下自己的拙作,希望大家多点评下。

本网站前端采用的JSP,后台为Servlet模块。

数据库采用流行的MySQL

在实现功能之前,应该了解一下基本的原理。


首先是要知道对表单的输入框的响应事件。

Javascript中有onpropertychange(IE支持)和oninput(FF等)

对于IE来说,可以这样来实现:

 

[javascript] view plaincopyprint?
  1. <script type="text/javascript">  
  2. document.getElementById('myinput').attachEvent('onpropertychange',function(o){      
  3.         if(o.propertyName =='value') {  
  4.             //doSth  ex. alert(myinput.value);   
  5.             }  
  6.             });  
  7. </script>  
<script type="text/javascript">
document.getElementById('myinput').attachEvent('onpropertychange',function(o){    
      	if(o.propertyName =='value') {
      		//doSth  ex. alert(myinput.value);
      		}
      		});
</script>

 


对于非IE浏览器,如Google Chrome,可以利用下面的代码:

 

[javascript] view plaincopyprint?
  1. <script type="text/javascript">  
  2. document.getElementById('myinput').addEventListener( "input",function(o) {  
  3.             //doSth   
  4.             // ex. alert(myinput.value);                       
  5.             },false);  
  6. </script>  
<script type="text/javascript">
document.getElementById('myinput').addEventListener( "input",function(o) {
			//doSth
        	// ex. alert(myinput.value);  				  	
  		  	},false);
</script>


这样,我们就可以动态地取得用户输入的内容。通过AJAX的异步交互,将信息传递给Servlet处理,

 

Servlet连接数据库,得到匹配的信息,返回给页面。

 

在数据库中,可以这样模糊查询:

 

  1. SELECT * FROM table_name WHERE column_name = 'content%';  
SELECT * FROM table_name WHERE column_name = 'content%';


如果匹配多个字符则用"%",单个字符用"_"。

 


**************************************************************************************

                                     中文乱码问题

另外,由于我们是利用JS取得用户的数据,因此如果含有中文信息会乱码,

我们可以利用JS里的函数库来处理乱码内容:

encodeURI() 或者     encodeURIComponent(uriComponent)

(调用一次还是会乱码,网上有人说要两次掉要该方法就可以解决问题,即encodeURI(encodeURI()))

在Servlet对得到的信息解码:

URLDecoder.decode(request.getParameter("msg","UTF-8"));

 

这样,我们就解决了中文乱码问题.

***************************************************************

整个流程大概如下:

下面给出一些关键代码:

 

首先是数据库:

创建数据库:

 

  1. CREATE DATABASE autoComplete;  
CREATE DATABASE autoComplete;


接着创建表:

 

 

  1. Create TableCREATE TABLE `hotwords` (  
  2.   `id` int(11) NOT NULL AUTO_INCREMENT,  
  3.   `msg` text,  
  4.   PRIMARY KEY (`id`)  
  5. ) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8  
Create Table: CREATE TABLE `hotwords` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `msg` text,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8


再随机插入一些数据:

 

如图:

 

  1. mysql> select * from hotwords;  
  2. +----+---------+  
  3. | id | msg     |  
  4. +----+---------+  
  5. |  1 | china   |  
  6. |  2 | chinese |  
  7. |  3 | cake    |  
  8. |  4 | cub     |  
  9. |  5 | chaos   |  
  10. |  6 | chicken |  
  11. |  7 | 陈旭    |  
  12. |  8 | 陈龙    |  
  13. |  9 | 陈滟龙  |  
  14. +----+---------+  
  15. 9 rows in set (0.00 sec)  
mysql> select * from hotwords;
+----+---------+
| id | msg     |
+----+---------+
|  1 | china   |
|  2 | chinese |
|  3 | cake    |
|  4 | cub     |
|  5 | chaos   |
|  6 | chicken |
|  7 | 陈旭    |
|  8 | 陈龙    |
|  9 | 陈滟龙  |
+----+---------+
9 rows in set (0.00 sec)

 

 

接下来是JSP页面:

index.jsp:

 

  1. <%@ page contentType="text/html; charset=UTF-8" %>  
  2. <html>  
  3.     <head>  
  4.         <title>自动提示框</title>  
  5.     <style type="text/css">  
  6.     .mystyle  
  7.     { background:white none repeat scroll 0 0;  
  8.       border:1px solid black;  
  9.       cursor:default;  
  10.       font-size:13px;  
  11.       line-height:17px;  
  12.       margin:0;  
  13.       z-index:99;   
  14.       width:150px;  
  15.       left:10px;  
  16.       top:10px;   
  17.       visibility: hidden;  
  18.       }  
  19.     </style>  
  20.     </head>  
  21.     <body>  
  22.   
  23.         <img src="pics/baidu_sylogo1.gif"/>  
  24.         <form>  
  25.             <input type="text" name="myinput" id="myinput" autocomplete="off"/>  
  26.   
  27.             <input type="submit" value="百度一下" />  
  28.         </form>  
  29.           
  30.                     <div id="aaa" class="mystyle"></div>  
  31.     </body>  
  32.     <script type="text/javascript">     
  33.     <!--      
  34.         var xmlHttp; //XMLHttpRequest对象  
  35.           
  36.         //判断浏览器类型  
  37.         function isIE() {  
  38.         if(navigator.userAgent.indexOf( "MSIE")>0) {  
  39.         return true;  
  40.         }  
  41.         }  
  42.           
  43.         //定义表单内容改变事件  
  44.         if(isIE()) {  
  45.         document.getElementById('myinput').attachEvent('onpropertychange',function(o){      
  46.         if(o.propertyName =='value') {  
  47.             var msg = document.getElementById('myinput').value; //取得用户输入实时信息  
  48.             getContent(msg);  
  49.           
  50.         }  
  51.     });   
  52.     } else {  
  53.             document.getElementById('myinput').addEventListener( "input",function(o) {  
  54.             var msg = document.getElementById('myinput').value; //取得用户输入实时信息  
  55.             getContent(msg);              
  56.             },true);  
  57.     }  
  58.         //创建XMLHttpRequest对象     
  59.         function createXMLHttp() {  
  60.             if(window.ActiveXObject)  
  61.             xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");  
  62.             else   
  63.             xmlHttp = new XMLHttpRequest();  
  64.         }  
  65.           
  66.         //向服务器取数据  
  67.         function getContent(msg) {  
  68.             createXMLHttp();  
  69.             var url = encodeURI(encodeURI("query.do?msg=" + msg));  
  70.             xmlHttp.open("POST",url,true);  
  71.             xmlHttp.onreadystatechange = function() {  
  72.                 if(xmlHttp.readyState == 4) {  
  73.                     if (xmlHttp.responseText == '')  
  74.                         document.getElementById("aaa").style.visibility = 'hidden';  
  75.                     else{  
  76.                         document.getElementById("aaa").style.visibility = 'visible';  
  77.                         document.getElementById("aaa").innerHTML = xmlHttp.responseText;  
  78.                 }   
  79.                       
  80.                 }  
  81.             };  
  82.             xmlHttp.send();  
  83.         }  
  84.           
  85.     //-->     
  86.     </script>   
  87. </html>   
<%@ page contentType="text/html; charset=UTF-8" %>
<html>
	<head>
		<title>自动提示框</title>
	<style type="text/css">
	.mystyle
	{ background:white none repeat scroll 0 0;
	  border:1px solid black;
	  cursor:default;
	  font-size:13px;
	  line-height:17px;
	  margin:0;
	  z-index:99; 
	  width:150px;
	  left:10px;
	  top:10px; 
	  visibility: hidden;
	  }
	</style>
	</head>
	<body>

		<img src="pics/baidu_sylogo1.gif"/>
		<form>
			<input type="text" name="myinput" id="myinput" autocomplete="off"/>

			<input type="submit" value="百度一下" />
		</form>
		
					<div id="aaa" class="mystyle"></div>
	</body>
	<script type="text/javascript">   
	<!--    
		var xmlHttp; //XMLHttpRequest对象
		
		//判断浏览器类型
		function isIE() {
		if(navigator.userAgent.indexOf( "MSIE")>0) {
		return true;
		}
		}
		
		//定义表单内容改变事件
		if(isIE()) {
 	 	document.getElementById('myinput').attachEvent('onpropertychange',function(o){    
      	if(o.propertyName =='value') {
      		var msg = document.getElementById('myinput').value; //取得用户输入实时信息
      		getContent(msg);
      	
      	}
  	}); 
  	} else {
  		  	document.getElementById('myinput').addEventListener( "input",function(o) {
        	var msg = document.getElementById('myinput').value; //取得用户输入实时信息
      		getContent(msg);		  	
  		  	},true);
  	}
  		//创建XMLHttpRequest对象   
  		function createXMLHttp() {
  			if(window.ActiveXObject)
  			xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
  			else 
  			xmlHttp = new XMLHttpRequest();
  		}
  		
  		//向服务器取数据
  		function getContent(msg) {
  			createXMLHttp();
  			var url = encodeURI(encodeURI("query.do?msg=" + msg));
  			xmlHttp.open("POST",url,true);
  			xmlHttp.onreadystatechange = function() {
  				if(xmlHttp.readyState == 4) {
  					if (xmlHttp.responseText == '')
  						document.getElementById("aaa").style.visibility = 'hidden';
  					else{
    					document.getElementById("aaa").style.visibility = 'visible';
  						document.getElementById("aaa").innerHTML = xmlHttp.responseText;
  				} 
  					
  				}
  			};
  			xmlHttp.send();
  		}
  		
	//-->   
	</script> 
</html> 


再来是Servlet

 

QueryServlet.java:

 

 

  1. import java.io.IOException;  
  2. import java.io.PrintWriter;  
  3. import java.net.URLDecoder;  
  4. import java.sql.Connection;  
  5. import java.sql.DriverManager;  
  6. import java.sql.PreparedStatement;  
  7. import java.sql.ResultSet;  
  8. import java.sql.SQLException;  
  9.   
  10. import javax.servlet.ServletException;  
  11. import javax.servlet.http.HttpServlet;  
  12. import javax.servlet.http.HttpServletRequest;  
  13. import javax.servlet.http.HttpServletResponse;  
  14.   
  15. public class QueryServlet extends HttpServlet {  
  16.   
  17.     private static final long serialVersionUID = 1L;  
  18.   
  19.     public void doPost(HttpServletRequest request, HttpServletResponse response)  
  20.             throws ServletException, IOException {  
  21.   
  22.         request.setCharacterEncoding("UTF-8");  
  23.         response.setContentType("text/html;charset=UTF-8");  
  24.         PrintWriter out = response.getWriter();  
  25.   
  26.         String msg = request.getParameter("msg");  
  27.         String decoded_msg = URLDecoder.decode(msg,"UTF-8");  
  28.         System.out.println(decoded_msg);  
  29.         getContent(decoded_msg, out);  
  30.     }  
  31.   
  32.     public static Connection getConnection() {  
  33.         Connection conn = null;  
  34.         try {  
  35.             Class.forName("com.mysql.jdbc.Driver");  
  36.   
  37.             conn = DriverManager.getConnection(  
  38.                     "jdbc:mysql://localhost:3306/autocomplete""root",  
  39.                     "your password");  
  40.   
  41.             return conn;  
  42.         } catch (ClassNotFoundException cnfe) {  
  43.             cnfe.printStackTrace();  
  44.             return null;  
  45.         } catch (SQLException e) {  
  46.             e.printStackTrace();  
  47.             return null;  
  48.         }  
  49.     }  
  50.   
  51.     public static void getContent(String msg, PrintWriter out) {  
  52.         Connection conn = null;  
  53.         PreparedStatement pstmt = null;  
  54.         ResultSet rs = null;  
  55.         if ( !msg.equals("")) {  
  56.             System.out.println(msg);  
  57.             String sql = "select * from hotwords where msg like '" + msg + "%'";  
  58.             System.out.println(sql);  
  59.   
  60.             try {  
  61.   
  62.                 conn = getConnection();  
  63.                 pstmt = conn.prepareStatement(sql);  
  64.                 rs = pstmt.executeQuery();  
  65.   
  66.                 while (rs.next()) {  
  67.                     out.print(rs.getString("msg") + "<br/>");  
  68.                 }  
  69.   
  70.             } catch (SQLException e) {  
  71.                 e.printStackTrace();  
  72.             }finally {  
  73.                 try{  
  74.                     if(rs != null) {  
  75.                         rs.close();  
  76.                     }  
  77.                     if(pstmt != null) {  
  78.                         pstmt.close();  
  79.                     }  
  80.                     if(conn != null) {  
  81.                         conn.close();  
  82.                     }  
  83.                 }catch(SQLException e) {  
  84.                     e.printStackTrace();  
  85.                 }  
  86.             }  
  87.   
  88.         }  
  89.     }  
  90.     //删除内容   
  91.     public static boolean deleteContent(String msg) {  
  92.         Connection conn = null;  
  93.         PreparedStatement pstmt = null;  
  94.             System.out.println(msg);  
  95.             String sql = "delete * from hotwords where msg =?";  
  96.   
  97.             try {  
  98.   
  99.                 conn = getConnection();  
  100.                 pstmt = conn.prepareStatement(sql);  
  101.                 pstmt.setString(1,msg);  
  102.                   
  103.                 int n = pstmt.executeUpdate();  
  104.                   
  105.                 if ( n > 0) {  
  106.                     return true;  
  107.                 }else {  
  108.                     return false;  
  109.                 }  
  110.   
  111.             } catch (SQLException e) {  
  112.                 e.printStackTrace();  
  113.                 return false;  
  114.             }finally {  
  115.                 try{  
  116.                     if(pstmt != null) {  
  117.                         pstmt.close();  
  118.                     }  
  119.                     if(conn != null) {  
  120.                         conn.close();  
  121.                     }  
  122.                 }catch(SQLException e) {  
  123.                     e.printStackTrace();  
  124.                 }  
  125.             }  
  126.   
  127.           
  128.           
  129.     }  
  130. }  
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLDecoder;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class QueryServlet extends HttpServlet {

	private static final long serialVersionUID = 1L;

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		request.setCharacterEncoding("UTF-8");
		response.setContentType("text/html;charset=UTF-8");
		PrintWriter out = response.getWriter();

		String msg = request.getParameter("msg");
		String decoded_msg = URLDecoder.decode(msg,"UTF-8");
		System.out.println(decoded_msg);
		getContent(decoded_msg, out);
	}

	public static Connection getConnection() {
		Connection conn = null;
		try {
			Class.forName("com.mysql.jdbc.Driver");

			conn = DriverManager.getConnection(
					"jdbc:mysql://localhost:3306/autocomplete", "root",
					"your password");

			return conn;
		} catch (ClassNotFoundException cnfe) {
			cnfe.printStackTrace();
			return null;
		} catch (SQLException e) {
			e.printStackTrace();
			return null;
		}
	}

	public static void getContent(String msg, PrintWriter out) {
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		if ( !msg.equals("")) {
			System.out.println(msg);
			String sql = "select * from hotwords where msg like '" + msg + "%'";
			System.out.println(sql);

			try {

				conn = getConnection();
				pstmt = conn.prepareStatement(sql);
				rs = pstmt.executeQuery();

				while (rs.next()) {
					out.print(rs.getString("msg") + "<br/>");
				}

			} catch (SQLException e) {
				e.printStackTrace();
			}finally {
				try{
					if(rs != null) {
						rs.close();
					}
					if(pstmt != null) {
						pstmt.close();
					}
					if(conn != null) {
						conn.close();
					}
				}catch(SQLException e) {
					e.printStackTrace();
				}
			}

		}
	}
	//删除内容
	public static boolean deleteContent(String msg) {
		Connection conn = null;
		PreparedStatement pstmt = null;
			System.out.println(msg);
			String sql = "delete * from hotwords where msg =?";

			try {

				conn = getConnection();
				pstmt = conn.prepareStatement(sql);
				pstmt.setString(1,msg);
				
				int n = pstmt.executeUpdate();
				
				if ( n > 0) {
					return true;
				}else {
					return false;
				}

			} catch (SQLException e) {
				e.printStackTrace();
				return false;
			}finally {
				try{
					if(pstmt != null) {
						pstmt.close();
					}
					if(conn != null) {
						conn.close();
					}
				}catch(SQLException e) {
					e.printStackTrace();
				}
			}

		
		
	}
}


最后贴一个web.xml:

 

 

 

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <web-app version="2.5"   
  3.     xmlns="http://java.sun.com/xml/ns/javaee"   
  4.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
  5.     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee   
  6.     http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">  
  7.   <display-name></display-name>  
  8.   <servlet>  
  9.     <servlet-name>QueryServlet</servlet-name>  
  10.     <servlet-class>QueryServlet</servlet-class>  
  11.   </servlet>  
  12.   
  13.   <servlet-mapping>  
  14.     <servlet-name>QueryServlet</servlet-name>  
  15.     <url-pattern>/query.do</url-pattern>  
  16.   </servlet-mapping>  
  17.   <welcome-file-list>  
  18.     <welcome-file>index.jsp</welcome-file>  
  19.   </welcome-file-list>  
  20. </web-app>  
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
	xmlns="http://java.sun.com/xml/ns/javaee" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
	http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <display-name></display-name>
  <servlet>
    <servlet-name>QueryServlet</servlet-name>
    <servlet-class>QueryServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>QueryServlet</servlet-name>
    <url-pattern>/query.do</url-pattern>
  </servlet-mapping>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>


大功告成,下面看一下效果:

 

 

IE9:

 

Google Chrome:

 

 

(数据库用户名和密码可以在QueryServlet.java类里改)

(别忘了在类库里添加MySQL驱动包)

(整个web项目放在资源里,供大家分享)

至于百度的图片,呵呵,自己去官网索取吧。大笑

分享到:
评论

相关推荐

    jQuery仿百度搜索下拉框自动补全代码插件

    **jQuery仿百度搜索下拉框自动补全代码插件** 在网页开发中,为了提供用户友好的搜索体验,经常会使用到自动补全功能,就像百度搜索引擎那样。这种功能可以极大地提高用户输入效率,减少用户的输入错误,提升用户...

    超级仿百度 自动搜索提示下拉框

    "超级仿百度 自动搜索提示下拉框"就是一个专门针对这一需求设计的JavaScript对象,它模仿了百度搜索引擎的搜索提示功能,旨在提供一个高效、易用的解决方案。这个功能对于前端开发者来说非常实用,尤其是那些需要...

    jquery提示下拉框 仿Google自动补全框

    修改网上的代码完成的,使用jquery+JavaScript形式写的,服务端代码用java编写,当然你可以用其他任何语言编写。suggest是MyEclipse的一个工程,直接import既可用。也可以提取里面的文件自己修改

    自动补全(仿百度搜索下拉框)

    本示例以“仿百度搜索下拉框”为主题,探讨如何实现一个自动补全的功能,它能够在用户输入时根据已输入的部分文字,预测并显示可能的完整词汇或短语,提高用户输入效率。 自动补全的核心在于实时性和准确性,它涉及...

    jQuery仿百度搜索下拉框自动补全代码插件.zip

    《jQuery仿百度搜索下拉框自动补全代码插件详解》 在网页开发中,用户交互体验至关重要,其中搜索功能的便捷性和智能性是衡量一个网站用户体验的重要指标。jQuery作为一个强大的JavaScript库,提供了丰富的功能来...

    JS仿百度自动下拉框模糊匹配提示

    标题“JS仿百度自动下拉框模糊匹配提示”和描述“主要介绍了JS仿百度自动下拉框模糊匹配提示的相关资料,需要的朋友可以参考下”说明了这篇文章的主题是关于如何用JavaScript和jQuery技术模仿百度搜索的自动下拉框...

    仿百度google的自动搜索可输入下拉框

    仿百度google的自动搜索可输入下拉框, 本人开发的, 网上的都不好用,郁闷,提手花了一天半呢

    AJAX仿百度输入自动补全源码,下拉框提示源码

    【标题】"AJAX仿百度输入自动补全源码,下拉框提示源码" 描述了一种使用AJAX技术来实现类似百度搜索框的自动补全功能的应用。这种技术通常用于提高用户输入效率,通过实时获取服务器端数据并在用户输入时动态显示建议...

    仿Baidu的搜索下拉提示框源码(ASP.NET)

    【标题】"仿Baidu的搜索下拉提示框源码(ASP.NET)" 是一个基于ASP.NET平台开发的项目,旨在实现与百度搜索类似的下拉提示框功能。在网页的搜索框中输入关键字时,该功能会自动显示与输入相关的搜索建议,提升用户的...

    仿google,百度的下拉框提示(无刷新)

    标题中的"仿google,百度的下拉框提示(无刷新)"是指实现一个类似于谷歌和百度搜索引擎的自动完成功能,即在用户输入关键词时,无需刷新页面就能在下拉框中显示相关的搜索建议。这种功能提高了用户体验,减少了用户...

    jquery仿百度下拉列表搜索框

    本项目“jquery仿百度下拉列表搜索框”旨在利用jQuery的功能,模仿百度搜索引擎的智能下拉列表功能,提供用户友好的模糊查询体验。 首先,我们要了解jQuery的核心概念。jQuery通过一种简化的语法,使开发者可以更...

    javascript仿百度输入框提示自动下拉补全

    在JavaScript编程中,实现类似百度或谷歌搜索框的输入框自动下拉补全功能是一项常见的需求,这可以提升用户体验,使用户在输入时快速找到可能的搜索建议。本实例将介绍如何利用JavaScript来创建这样的功能。 首先,...

    自动完成仿google、百度,支持中文,html版本

    标题中的“自动完成仿google、百度,支持中文,html版本”指的是一个网页应用程序,它使用HTML、CSS和jQuery技术来创建一个类似于Google和百度搜索的自动填充功能,尤其强调了对中文的支持。这个功能通常在用户输入...

    jQuery仿百度搜索下拉框自动补全插件特效代码

    **jQuery仿百度搜索下拉框自动补全插件特效代码详解** 在网页开发中,用户交互体验至关重要,其中搜索功能的便捷性和智能性是衡量一个网站用户体验的重要指标。百度搜索的下拉框自动补全功能就是一个很好的例子,它...

    jQuery实现的仿百度,仿谷歌搜索下拉框效果示例

    在本示例中,我们将探讨如何使用jQuery来创建一个仿百度和谷歌的搜索下拉框效果。这个功能常用于搜索引擎或网站的搜索框,当用户输入关键字时,下拉框会显示相关的建议搜索项,提高用户体验。以下是实现这一功能的...

    仿Google自动完成Access版.rar

    "仿百度自动完成Access版"则表示它也参考了百度的相似功能,结合了国内用户习惯。"ajax自动完成Access版"揭示了其采用Ajax(Asynchronous JavaScript and XML)技术,实现了异步通信,使用户在无需刷新页面的情况下...

    JavaFx实现百度文本框的下拉提示功能

    `TextField`提供了基本的文本输入和编辑功能,但默认并不支持自动完成或下拉提示。为了实现这个功能,我们需要扩展`TextField`类或者通过监听`TextField`的事件来添加自定义行为。 关键步骤包括: 1. **监听输入**...

Global site tag (gtag.js) - Google Analytics