preface
As a front-end engineer who has worked on several H5 projects, it is inevitable that there will be some compatibility issues during the development process. Now I will sum up these problems one by one, and give the principle of pit generation, and the conventional pit filling scheme at the present stage. From this to do a phased summary.
The problem
The following is a list of some common problems I have encountered. If you have other problems, please add them in the comment section. I will also add them after practice.
Summary of h5-related problems on mobile terminal:
-
1 px problem
-
Responsive layout
-
IOS doesn’t swipe smoothly
-
IOS pull-up border pull-down white space appears
-
Page widgets zoom in or out of uncertain behavior
-
Click through and delay
-
Soft keyboard pop up the page up, put up the problem did not fall back
-
IPhone X bottom bar fit problem
-
Save the page for pictures and QR code problems and solutions
-
Wechat official account H5 shared questions
-
Problems and solutions related to SDK invocation by H5
-
H5 Debugging related schemes and policies
Mobile terminal H5 related basic technology overview
Principles and Practice
Two previous articles have discussed the 1PX problem and mobile terminal adaptation in detail, and given the principle and scheme.
Now, let’s look at the principles and solutions for other problems.
IOS doesn’t swipe smoothly
performance
Swiping up and down the page creates a stutter, and when your finger leaves the page, the page immediately stops moving. The overall performance is not smooth sliding, no sliding inertia.
The reasons causing
Why is iOS webView not sliding smoothly, and how is it defined?
I finally found the answer in the Safari documentation (link to the documentation in the Resources section).
In iOS 5.0 and later, there are two values for sliding: auto and touch. The default is auto.
-webkit-overflow-scrolling: touch; /* When the finger is removed from the screen, it keeps scrolling for a while */-webkit-overflow-scrolling: auto; /* When the finger is removed from the touch screen, scrolling stops immediately */Copy the code
The solution
1. Add the scroll touch method to the scroll container
Set -webkit-overflow-scrolling to touch
.wrapper { -webkit-overflow-scrolling: touch; }Copy the code
Container ::-webkit-scrollbar {display: none; }
May result in position:fixed; Fixed positioned elements that scroll along with the page
2. Set the overflow
Set the external overflow to Hidden and the content element overflow to Auto. The inner element beyond the body produces a scroll, and the part beyond the body is hidden.
body { overflow-y: hidden; }.wrapper { overflow-y: auto; }Copy the code
A combination of the two is better!
IOS pull-up border pull-down white space appears
performance
Hold your finger on the screen and pull it down to create a white area at the top of the screen. Hold your finger on the screen and pull, creating a white area at the bottom.
The reasons causing
In iOS, holding a finger and dragging it up and down triggers the TouchMove event. This event triggers the entire WebView container, which will naturally be dragged, leaving the rest blank.
The solution
1. Do not slide listening events
There are three touch events on the mobile terminal, which are defined as
1. Touchstart: Place your finger on a DOM element. 2. Touchmove: Drag a DOM element with your finger. 3. Touchend: Remove your finger from a DOM element.Copy the code
The obvious thing to control is the TouchMove event, which I found in the W3C documentation
Note that the rate at which the user agent sends touchmove events is implementation-defined, and may depend on hardware capabilities and other implementation details.
If the preventDefault method is called on the first touchmove event of an active touch point, it should prevent any default action caused by any touchmove event associated with the same active touch point, such as scrolling.
touchmove
The speed of events is implementation-definable, depending on hardware performance and other implementation details
preventDefault
Method to block all default behavior on the same touch, such as scrolling.
Therefore, we found a solution. By listening to TouchMove, we made it possible to slide where it was needed and forbidden to slide where it was not needed.
It is important to note that we filter out elements that have scroll containers.
The implementation is as follows:
document.body.addEventListener('touchmove', function(e) { if(e._isScroller) return; // block the default event e.preventDefault(); }, { passive: false});Copy the code
2. Scroll to compromise to fill in the blanks and decorate into other features
In many cases, we can stop trying to solve this problem and start thinking about it. Depending on the scenario, we can use the drop-down as a functional operation.
For example: refresh the page after the drop-down
Page zooms in or out of uncertain behavior
performance
Double-click or open the finger page element with both fingers to zoom in or out.
The reasons causing
HTML itself generates zooming in and out behavior, such as in a PC browser, where pages can be zoomed in and out freely. But on mobile, we don’t need this behavior. Therefore, we need to prohibit this uncertain behavior to improve the user experience.
Principles and solutions
The HTML meta meta tag standard has a medium viewPort property that controls the zoom of a page. It is usually used on mobile devices. Described in MDN in the following figure
Mobile terminal general writing
< meta name = "viewport" content = "width = device - width, initial - scale = 1.0" >Copy the code
So maximum-scale, minimum-scale, and user-scalable=no can be used to avoid this problem
<meta name=viewport content="width=device-width, initial-scale=1.0, minimum-scale=1.0 maximum-scale=1.0, user-scalable=no">
Copy the code
Click Event delay and penetration
performance
Listen for element click event, click element trigger time delay is about 300ms.
Click on the mask layer, after the mask disappears, the lower element clicks trigger.
The reasons causing
Why is there a click delay?
In safari on iOS, to double – click zoom operation, click 300ms later, if the second click is not performed, click operation. That is, to determine whether the user’s behavior is caused by a double click. However, in the App, a click creates a 300ms delay whether or not you need to double click to zoom.
Why does click penetration occur?
When a two-layer element is stacked, the touch event is bound to the upper element and the click event to the lower element. Since click occurs after touch, clicking on the upper element causes the element to disappear, and the lower element triggers the click event, creating the click-through effect.
Principles and solutions
Solution 1: Replace Click with TouchStart
As mentioned earlier, mobile devices support not only clicks, but also several touch events. So the basic idea here is to use the Touch event instead of the click event.
Replacing click with TouchStart not only solves the click event delay problem, but also solves the penetration problem. Because penetration problems occur when touch and click are mixed.
Used in native
el.addEventListener("touchstart", () => { console.log("ok"); }, false);
Copy the code
Used in VUE
< button@touchStart ="handleTouchstart()"> click </button>Copy the code
Open source solutions also provide both click and TouchStart events. Such as the Button component in vant
So, is it possible to putclick
The events are all replaced bytouchstart
? Why are open source frameworks still givenclick
The event?
We imagine a scene that requires clicking and swiping at the same time. What happens if you replace click with touchStart?
Event trigger sequence: TouchStart, TouchMove, TouchEnd, click.
It’s easy to imagine that when I need to swipe touchMove, the touchStart click event is preferentially triggered. Is there already a conflict?
So, in the case of scrolling, the click handle is recommended.
This is also done in the following FastClick open source library. For TouchStart and Touchend, intercepted part of the source code.
// If the target element is a child of a scrollable layer (using -webkit-overflow-scrolling: touch) and:// 1) the user does a fling scroll on the scrollable layer// 2) the user stops the fling scroll with another tap// then the event.target of the last 'touchend' event will be the element that was under the user's finger// when the fling scroll was started, causing FastClick to send a click event to that layer - unless a check// is made to ensure that a parent layer was not scrolled before sending a synthetic click (issue #42).this.updateScrollParent(targetElement); // Don't send a synthetic click event if the target element is contained within a parent layer that was scrolled// and this tap is being used to stop the scrolling (usually initiated by a fling - issue #42).scrollParent = targetElement.fastClickScrollParent; if (scrollParent && scrollParent.fastClickLastScrollTop ! == scrollParent.scrollTop) {return true; }Copy the code
The main purpose is to ensure that the click event is not under the scrolling parent when synthesized using TouchStart.
Solution 2: Use the FastClick library
Use NPM or YARN after installation
import FastClick from 'fastclick'; FastClick.attach(document.body, options);Copy the code
Also, with the Fastclick library, click latency and penetration issues are eliminated
In my general practice, when it comes to open source libraries, it’s important to understand how it works. It is mainly to encapsulate the existing native event set into a strong compatibility event set.
Fastclick source code core code is not long, less than 1000 lines. Have interest can understand!
Soft keyboard will top up the page, put up the problem did not fall back
performance
On Android phones, when you click on the Input box, the keyboard pops up and pops up the page, causing the page style to mess up.
When the focus is removed, the keyboard is folded up and the keyboard area is blank without falling back.
The reasons causing
We’re going to have a fixed bottom in our app layout. On some versions of Android, typing in a popup will extract the absolute and fixed elements. Resulting in a smaller viewable area, layout confusion.
Principles and solutions
Soft keyboard will page up the solution, mainly by listening to the page height changes, forced to return to the height before the pop-up.
/ / record the viewport height of the original const originalHeight. = the document body. ClientHeight | | document. The documentElement. ClientHeight; window.onresize = function(){ var resizeHeight = document.documentElement.clientHeight || document.body.clientHeight; If (resizeHeight < originalHeight){// Restore content area height // const container = document.getelementById ("container") // for example container.style.height = originalHeight; }}Copy the code
The problem of keyboard not falling back appears in iOS 12+ and wechat 6.7.4+, which is a common Bug in the development of wechat H5.
Compatibility principle, 1. Determine version type 2. Change the scrolling visual area
const isWechat = window.navigator.userAgent.match(/MicroMessenger\/([\d\.]+)/i); if (! isWechat) return; const wechatVersion = wechatInfo[1]; const version = (navigator.appVersion).match(/OS (\d+)_(\d+)_? (\d+)? /); If the device type is iOS 12+ and wechat 6.7.4+, If (+ wechatversion.replace (/\./g, ") >= 674&& +version[1] >= 12) {window.scrollto (0, Math.max(document.body.clientHeight, document.documentElement.clientHeight)); }Copy the code
Window.scrollto (x-coord, y-coord), where window.scrollto (0, clientHeight) restores to the original viewport
IPhone X series security zone adaptation problem
performance
On both sides of the bangs or at the bottom of the head, bangs block the text, or blank areas with black or white background.
The reasons causing
The iPhone X and its successors all feature bangs and gestures across the screen. The head, bottom and sides all need special treatment. In order to adapt to the special circumstances of iPhone X.
The solution
Set security zones and fill dangerous zones. Do not perform operations or display contents in dangerous zones.
Danger area Finger irregularity area, bottom bar area, left and right trigger area.
Specific operations are as follows:viewport-fit
meta
Tag set tocover
To get all regions populated. Determine whether the device belongs to the iPhone X and add the bottom of the headAdaptation layer
Viewport-fit has three values:
Auto: This value does not affect the initial layout view port, and the entire Web page is viewable.
Contain: View port is scaled to fit the largest embedded rectangle.
Cover: The view port is scaled to fill the device display. It is highly recommended to use the Safe Area inset variable to ensure that important content does not appear outside the display.
Set viewport-fit to cover
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes, viewport-fit=cover">
Copy the code
Add an adaptive layer
Use the Safe Area inset variable
Supports (top: env(safe-area-inset-top)){body,.header{padding-top: supports (top: env(safe-area-inset-top)) constant(safe-area-inset-top, 40px); padding-top: env(safe-area-inset-top, 40px); padding-top: var(safe-area-inset-top, 40px); }}/* iPhoneX fills the footer padding-bottom to the bottom */@supports (bottom: env(safe-area-inset-bottom)){ body, .footer{ padding-bottom: constant(safe-area-inset-bottom, 20px); padding-bottom: env(safe-area-inset-bottom, 20px); padding-top: var(safe-area-inset-bottom, 20px); }}Copy the code
safe-area-inset-top, safe-area-inset-right, safe-area-inset-bottom, Safe-area-inset -left Safe-area-inset -* consists of four environment variables that define the top, right, bottom, and left rectangles inside the viewport edge so that content can be safely placed without the risk of being cut off by a non-rectangular display. For a rectangular viewport, such as a regular laptop monitor, the value is zero. For non-rectangular displays (such as round dials, iPhoneX screens), everything is visible within the rectangle formed by the four values set by the user agent.
Env (
,
?) , the first parameter is the custom region, and the second parameter is the alternate value.
Var () : var(
,
?) Is used to give an alternate value if env() is not in effect.
Constant () was drafted by CSS 2017-2018 and it is unknown whether it has been standardized. It is unknown whether this function is available in other iOS browser versions, but added for compatibility purposes.
See resources at the end of this article for details.
compatibility
The page became a problem with images and QR codes
performance
There is a need to generate pictures or TWO-DIMENSIONAL code on the page at work. Probably the first thing that comes to mind is that it’s easier to hand it over to the back end. But in this way, we need to pass all the page code to the back end, which consumes too much network performance.
The solution
Generate qr code
Generate a QRCode using QRCode
import QRCode from 'qrcode'; // Use async to generate images const options = {}; const url = window.location.href; async url => { try { console.log(await QRCode.toDataURL(url, options)) } catch (err) { console.error(err); }}Copy the code
Assign await qrcode.todataURL (URL, options) to the image URL
Generate images
HtmlToCanvas is mainly used to generate canvas canvas
import html2canvas from 'html2canvas'; html2canvas(document.body).then(function(canvas) { document.body.appendChild(canvas); });Copy the code
But it’s not just here, because it’s canvas. Mobile generated images are blurry.
We use a new canvas method to generate multiple times and put it into a double container to achieve a clearer effect. It is simply realized by downloading pictures and files through hyperlink, and a more complete implementation method will be updated later
const scaleSize = 2; const newCanvas = document.createElement("canvas"); const target = document.querySelector('div'); const width = parseInt(window.getComputedStyle(target).width); const height = parseInt(window.getComputedStyle(target).height); newCanvas.width = width * scaleSize; newCanvas.height = widthh * scaleSize; newCanvas.style.width = width + "px"; newCanvas.style.height =width + "px"; const context = newCanvas.getContext("2d"); context.scale(scaleSize, scaleSize); html2canvas(document.querySelector('.demo'), { canvas: NewCanvas}).then(function(canvas) {document.querySelector(".btn").setattribute ('href', canvas.toDataURL()); }Copy the code
Set the scaleSize size as required
Wechat public account to share questions
performance
In the development of wechat public account H5, click the share button inside the page to invoke SDK, but the method does not take effect.
The solution
Solution: Add a layer of mask to share the boot.
Because click the share button inside the page cannot be called directly, and the share function needs to click more in the upper right corner to operate.
Then users may not know about sharing through the function in the top right sidebar. If you want to guide users to share, what should you do?
Technology can not achieve, from the product.
If the implementation is technically complex, or simply not possible. Don’t be too pushy. Learning how to play games is a must for programmers.
H5 invokes sdK-related solutions
The reasons causing
The use of H5 in Hybrid apps is the most common, but it will be unfamiliar and fuzzy when you just touch it. I don’t know how H5 and Hybrid interact. How do you support both iOS and Android? Now let’s talk about Hybrid technology essentials, native-to-H5 communication.
The solution
useDSBridge
Both iOS and Android are supported
See Resources for documentation
The SDK team provides the method
-
Register method bridge.register
bridge.register(‘enterApp’, function() { broadcast.emit(‘ENTER_APP’)})
-
The callback method bridge.call
export const getSDKVersion = () => bridge.call(‘BLT.getSDKVersion’)
Event listening and triggering methods
const broadcast = { on: function(name, fn, pluralable) { this._on(name, fn, pluralable, false) }, once: function(name, fn, pluralable) { this._on(name, fn, pluralable, true) }, _on: function(name, fn, pluralable, once) { let eventData = broadcast.data let fnObj = { fn: fn, once: once } if (pluralable && Object.prototype.hasOwnProperty.call(eventData, 'name')) { eventData[name].push(fnObj) } else { eventData[name] = [fnObj] } return this }, emit: function(name, data, thisArg) { let fn, fnList, i, len thisArg = thisArg || null fnList = broadcast.data[name] || [] for (i = 0, len = fnList.length; i < len; i++) { fn = fnList[i].fn fn.apply(thisArg, [data, name]) if (fnList[i].once) { fnList.splice(i, 1) i-- len-- } } return this }, data: {}}export default broadcast
Copy the code
Note on pit
Before calling a method, make sure that the SDK provides the method. If Android provides the method, iOS calls will show a popup window such as method call failure. How do you solve it?
Provide a check whether Android, iOS. Judge according to the device
export const hasNativeMethod = (name) => return bridge.hasNativeMethod('BYJ.' + name)}export const getSDKVersion = function() { if (hasNativeMethod('getSDKVersion')) { bridge.call('BYJ.getSDKVersion') }}
Copy the code
The same function requires the same iOS and Android method name, so it is easier to handle oh
H5 Debug related scheme policies
performance
Debugging code is generally about looking at data and locating bugs. There are two scenarios, one is debugging during development and testing, and the other is debugging in production environment.
Why is there debugging on production? There are times when this bug cannot be replicated in the test environment and the test environment is inconsistent with the production environment, requiring urgent production debugging.
While developing on the PC side, you can simply drop out of the console and use the tools provided by the browser to operate DevTools or view the logs. But what do we do inside the App?
Principles and solutions
1. Vconsole plug-in
It’s easy to use
import Vconsole from 'vconsole'new Vconsole()
Copy the code
Interested in seeing the fundamentals of its implementation, the point we should focus on is how vsConsole prints out all our logs for Tencent open source Vconsole
The above methods are for development and testing purposes only. It is not allowed in the production environment, so you need to judge the environment when using it.
import Vconsole from 'vconsole'if (process.env.NODE_ENV ! == 'production') { new Vconsole()}Copy the code
2. Agent + Spy – Debugger
It’s a little bit tricky to do, but I’ll write it out in detail, basically in 4 steps
-
Installing plug-ins (global installation)
sudo npm install spy-debugger -g
-
Mobile phone and computer under the same wifi, mobile phone set agent
Set the HTTP proxy IP address of the mobile phone to the IP address of the PC and the port to the spy-Debugger startup port
Spy – Debugger default port: 9888
Android: Settings – WLAN – Long press Select Network – Modify Network – Advanced – Proxy Settings – Manual
IOS: Settings – Wi-Fi – Select the network, click the exclamation mark, HTTP proxy manual
-
Open the H5 page in the browser or APP
-
Open the desktop log site for debugging and click on the NPM console to listen for the address. View the packet capture and H5 page structure